Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 8 years ago.
Improve this question
I have a situation where my string can't go past a certain point, so what I want to do is cut it into smaller strings of "x" characters, and then print them one by one on top of each other. They don't all need to be equal, if x is 5, and I have an 11 character string, printing 3 lines with 5, 5, and 1 characters is fine. Is there an easy way to do this in C#?
Example:
string Test = "This is a test string";
stringarray = Cutup(Test, 5);
//Result:
//"This "
//"is a "
//"test "
//"strin"
//"g"
try something like this:
public string[] Cutcup(string s, int l)
{
List<string> result = new List<string>();
for (int i = 0; i < s.Length; i += l)
{
result.Add(s.Substring(i, Math.Min(5, s.Substring(i).Length)));
}
return result.ToArray();
}
You could cut up the strings then do a test.lastIndexOf(' '); If that helps
You can use the String Manipultion function Substring() and a for loop to accomplish this.
here is an example
int maxChars = 5;
String myStr = "This is some text used in testing this method of splitting a string and just a few more chars and the string is complete";
List<String> mySubStrings = new List<String>();
while (myStr.Length > maxChars)
{
mySubStrings.Add(myStr.Substring(0,maxChars));
myStr = myStr.Substring(maxChars);
}
mySubStrings.ToArray();
List<string> result = new List<string>();
string testString = "This is a test string";
string chunkBuilder = "";
int chunkSize = 5;
for (int i = 0; i <= testString.Length-1; i++)
{
chunkBuilder += testString[i];
if (chunkBuilder.Length == chunkSize || i == testString.Length - 1)
{
result.Add(chunkBuilder);
chunkBuilder = "";
}
}
Another try, with less string concatenations
string Test = "This is a test string";
List<string> parts = new List<string>();
int i = 0;
do
{
parts.Add(Test.Substring(i,System.Math.Min(5, Test.Substring(i).Length)));
i+= 5;
} while (i < Test.Length);
Here are a couple more ways. Cutup2 below is more efficient but less pretty. Both pass the test case given.
private static IEnumerable<string> Cutup(string given, int chunkSize)
{
var skip = 0;
var iterations = 0;
while (iterations * chunkSize < given.Length)
{
iterations++;
yield return new string(given.Skip(skip).Take(chunkSize).ToArray());
skip += chunkSize;
}
}
private static unsafe IEnumerable<string> Cutup2(string given, int chunkSize)
{
var pieces = new List<string>();
var consumed = 0;
while (consumed < given.Length)
{
fixed (char* g = given)
{
var toTake = consumed + chunkSize > given.Length
? given.Length - consumed
: chunkSize;
pieces.Add(new string(g, consumed, toTake));
}
consumed += chunkSize;
}
return pieces;
}
All in one line
var size = 5;
var results = Enumerable.Range(0, (int)Math.Ceiling(test.Length / (double)size))
.Select(i => test.Substring(i * size, Math.Min(size, test.Length - i * size)));
I once made an extensionmethod that can be used for this:
public static IEnumerable<IEnumerable<T>> Subsequencise<T>(this IEnumerable<T> input, int subsequenceLength)
{
var enumerator = input.GetEnumerator();
SubsequenciseParameter parameter = new SubsequenciseParameter { Next = enumerator.MoveNext() };
while (parameter.Next)
yield return getSubSequence(enumerator, subsequenceLength, parameter);
}
private static IEnumerable<T> getSubSequence<T>(IEnumerator<T> enumerator, int subsequenceLength, SubsequenciseParameter parameter)
{
do
{
yield return enumerator.Current;
} while ((parameter.Next = enumerator.MoveNext()) && --subsequenceLength > 0);
}
// Needed to let the Subsequencisemethod know when to stop, since you cant use out or ref parameters in an yield-return method.
class SubsequenciseParameter
{
public bool Next { get; set; }
}
then you can do this:
string Test = "This is a test string";
stringarray = Test.Subsequencise(5).Select(subsequence => new String(subsequence.Toarray())).Toarray();
Here's a rather LINQy one-liner:
static IEnumerable<string> SliceAndDice1( string s , int n )
{
if ( s == null ) throw new ArgumentNullException("s");
if ( n < 1 ) throw new ArgumentOutOfRangeException("n");
int i = 0 ;
return s.GroupBy( c => i++ / n ).Select( g => g.Aggregate(new StringBuilder() , (sb,c)=>sb.Append(c)).ToString() ) ;
}
If that gives you a headache, try the more straightforward
static IEnumerable<string> SliceAndDice2( string s , int n )
{
if ( s == null ) throw new ArgumentNullException("s") ;
if ( n < 1 ) throw new ArgumentOutOfRangeException("n") ;
int i = 0 ;
for ( i = 0 ; i < s.Length-n ; i+=n )
{
yield return s.Substring(i,n) ;
}
yield return s.Substring(i) ;
}
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I have a string and I want to insert a specific character, \n for example, at many indices, which I have stored in a List. Is there an efficient way of modifying the string without first splitting it? I care more about time than space in this case.
If this helps, the function is within a Singleton class, and is being used to reformat the string which will then be rendered to a game scene upon return (this for a game in Unity).
A first version could be this:
public static string InsertStringAt(string str, int[] indexes, string insert)
{
if (indexes.Length == 0 || insert.Length == 0)
{
return str;
}
var sb = new StringBuilder(str.Length + indexes.Length * insert.Length);
int ixIndexes = 0;
// We NEED ordered indexes
// We don't order "in place" indexes because it is bad
// modifying input parameters
var indexes2 = indexes.OrderBy(x => x).ToArray();
int i = 0;
for (; i < str.Length; i++)
{
while (ixIndexes < indexes2.Length && i == indexes2[ixIndexes])
{
if (indexes2[ixIndexes] < 0)
{
// index < 0
throw new ArgumentOutOfRangeException(nameof(indexes));
}
sb.Append(insert);
ixIndexes++;
}
sb.Append(str[i]);
}
while (ixIndexes < indexes2.Length && i == indexes2[ixIndexes])
{
if (indexes2[ixIndexes] < 0)
{
// index < 0
throw new ArgumentOutOfRangeException(nameof(indexes));
}
sb.Append(insert);
ixIndexes++;
}
if (ixIndexes != indexes2.Length)
{
// Some indexes were > indexes.Length
throw new ArgumentOutOfRangeException(nameof(indexes));
}
return sb.ToString();
}
Note that there is a possible optimization, but the optimization will render the code more complex. Instead of adding the chars one at a time, we could add all the substrings of str between the indexes.
Second version... In the end it wasn't more complex:
public static string InsertStringAt(string str, int[] indexes, string insert)
{
if (indexes.Length == 0 || insert.Length == 0)
{
return str;
}
var indexes2 = indexes.OrderBy(x => x).ToArray();
var sb = new StringBuilder(str.Length + indexes.Length * insert.Length);
int j = 0;
for (int i = 0; i < indexes2.Length; i++)
{
if (indexes2[i] < 0)
{
throw new ArgumentOutOfRangeException(nameof(indexes2));
}
if (indexes2[i] > str.Length)
{
throw new ArgumentOutOfRangeException(nameof(indexes2));
}
sb.Append(str, j, indexes2[i] - j);
sb.Append(insert);
j = indexes2[i];
}
sb.Append(str, j, str.Length - j);
return sb.ToString();
}
I suggest a string extension method that copies slices between positions into a StringBuilder and inserts the char at each position:
public static class StringExt {
public static string InsertCharAt(this string src, char ins, IEnumerable<int> poss) {
var orderedPoss = poss.ToList();
orderedPoss.Sort();
var ans = new StringBuilder(src.Length+orderedPoss.Count);
var srcSpan = src.AsSpan();
var beginPos = 0;
foreach (var insPos in orderedPoss) {
ans.Append(srcSpan.Slice(beginPos, insPos-beginPos));
ans.Append(ins);
beginPos = insPos;
}
return ans.ToString();
}
}
NOTE: If you don't have Span, the three argument StringBuilder.Append method can be used instead (as #xanatos did).
PS: Per #pingfloydx33 a single allocation version using String.Create:
public static string InsertCharAt2(this string src, char ins, IEnumerable<int> poss) {
var orderedPoss = poss.ToList();
orderedPoss.Sort();
return String.Create(src.Length + orderedPoss.Count, new { }, (ans, _) => {
var srcSpan = src.AsSpan();
var beginPos = 0;
var writePos = 0;
foreach (var insPos in orderedPoss) {
var subLen = insPos-beginPos;
srcSpan.Slice(beginPos, subLen).CopyTo(ans.Slice(writePos));
writePos += subLen;
ans[writePos++] = ins;
beginPos = insPos;
}
});
so I wrote some C# code and I am trying to test it incrementally, do to something that would take a while to explain, but bottom line, I'm new to c# and not understanding the online compiler error messages. Here is the error message I get when I try and compile, but the strings look good to me.
string solutionSet = "white|black|beige|pink|green|blue|red|yellow|orange|cyan|purple|brown";
string[] solutionSetArray = new string[12];
string ret = "";
string delimeter = "|";
int tempPos = 0;
int counter = 0;
int successFlag = 0;
int patternLength = 5;
for (int index = 0; index < solutionSet.Length; index++)
{
if (solutionSet[index] == delimeter)
{
solutionSetArray[counter] = solutionSet.Substring(tempPos, index);
tempPos = index + 1;
counter++;
}
if (solutionSet.Length - index == 1)
{
solutionSetArray[solutionSetArray.Length-1] = solutionSet.Substring(tempPos, solutionSet.Length);
}
}
for (int i = 0; i < patternLength; i++)
{
Random rnd = new Random();
int randIndex = rnd.Next(solutionSetArray.Length);
if (i != patternLength - 1)
{
ret += solutionSetArray[randIndex] + "|";
successFlag++;
}
else
{
ret += solutionSetArray[randIndex];
}
}
if (successFlag == patternLength - 1)
{
Console.WriteLine(ret);
}
else
{
Console.WriteLine("ERROR");
}
The error (which, according to the message, is on line 1, column 11) is being caused by your very first line of code, which begins string.
I can't tell the context from just your post, but my guess is that you are declaring solutionSet in a block that is not inside of a class or function. You should enclose your code in a class or method, e.g.
public class MyClass
{
static public void Main()
{
string solutionSet = "white|black|beige|pink|green|blue|red|yellow|orange|cyan|purple|brown";
//Rest of code goes here
}
}
By the way, if you're trying to convert solutionSet to an array, you can just write
var solutionSetArray = solutionSet.Split("|");
the problem with your code is
solutionSetArray[counter] = solutionSet.Substring(tempPos, index);
after 6 iterations tempPos=34 and index=37 which is running out of bounds of solutionSet. I would suggest to use var solutionSetArray = solutionSet.Split("|"); and also use LinqPad which can be easy for you to debug if possible,.
I have a problem to solve where given a string source and a collection of search criteria criteria, the algorithm has to return the shortest possible substring of source that contains all items of criteria.
=================================
UPDATE
The same search criteria might be in the source string multiple
times. In that case, it is required to return the sub-string
containing the particular instance of the search criteria such that
it is the shortest among all possible sub-strings.
The search items can contain spaces in them such as hello world
The order in which the search criteria are found does not matter as long as they are all in the resultant sub-string
==================================
String source = "aaa wwwww fgffsd ththththt sss sgsgsgsghs bfbfb hhh sdfg kkk dhdhtrherhrhrthrthrt ddfhdetehehe kkk wdwd aaa vcvc hhh zxzx sss nbnbn";
List<String> criteria = new List<string> { "kkk", "aaa", "sss", "hhh" };
The input above should return the following substring: kkk wdwd aaa vcvc hhh zxzx sss
Unfortunately, I spent a lot of time trying to write such an algorithm but I couldn't get it just right. Below is the code I have got so far:
public struct Extraction
{
public int Start { get; set; }
public int End { get; set; }
public int Length
{
get
{
var length = this.End - this.Start;
return length;
}
}
public Extraction(int start, int end)
{
this.Start = start;
this.End = end;
}
}
public class TextExtractor
{
private String _source;
private Dictionary<String, List<Int32>> _criteriaIndexes;
private Dictionary<String, int> _entryIndex;
public TextExtractor(String source, List<String> searchCriteria)
{
this._source = source;
this._criteriaIndexes = this.ExtractIndexes(source, searchCriteria);
this._entryIndex = _criteriaIndexes.ToDictionary(x => x.Key, v => 0);
}
public String Extract()
{
List<Extraction> possibleExtractions = new List<Extraction>();
int index = 0;
int min = int.MaxValue;
int max = 0;
bool shouldStop = false;
while (index < _criteriaIndexes.Count && !shouldStop)
{
Boolean compareWithAll = index == _criteriaIndexes.Count - 1;
if (!compareWithAll)
{
var current = _criteriaIndexes.ElementAt(index);
this.CalculateMinMax(current, ref min, ref max);
index++;
}
else
{
var entry = _criteriaIndexes.Last();
while (_entryIndex[entry.Key] < entry.Value.Count)
{
int a = min;
int b = max;
this.CalculateMinMax(entry, ref a, ref b);
_entryIndex[entry.Key]++;
Extraction ext = new Extraction(a, b);
possibleExtractions.Add(ext);
}
int k = index - 1;
while (k >= 0)
{
var prev = _criteriaIndexes.ElementAt(k);
if (prev.Value.Count - 1 > _entryIndex[prev.Key])
{
_entryIndex[prev.Key]++;
break;
}
else
{
k--;
}
}
shouldStop = _criteriaIndexes.All(x => x.Value.Count - 1 <= _entryIndex[x.Key]);
_entryIndex[entry.Key] = 0;
index = 0;
min = int.MaxValue;
max = 0;
}
}
Extraction shortest = possibleExtractions.First(x => x.Length.Equals(possibleExtractions.Min(p => p.Length)));
String result = _source.Substring(shortest.Start, shortest.Length);
return result;
}
private Dictionary<String, List<Int32>> ExtractIndexes(String source, List<String> searchCriteria)
{
Dictionary<String, List<Int32>> result = new Dictionary<string, List<int>>();
foreach (var criteria in searchCriteria)
{
Int32 i = 0;
Int32 startingIndex = 0;
var indexes = new List<int>();
while (i > -1)
{
i = source.IndexOf(criteria, startingIndex);
if (i > -1)
{
startingIndex = i + 1;
indexes.Add(i);
}
}
if (indexes.Any())
{
result.Add(criteria, indexes);
}
}
return result;
}
private void CalculateMinMax(KeyValuePair<String, List<int>> current, ref int min, ref int max)
{
int j = current.Value[_entryIndex[current.Key]];
if (j < min)
{
min = j;
}
int indexPlusWordLength = j + current.Key.Length;
if (indexPlusWordLength > max)
{
max = indexPlusWordLength;
}
}
}
I would appreciate it if someone could point out where did I go wrong in my algorithm. Moreover, I kinda feel this is a very naive implementation. Maybe there is a better approach to solve this problem than trying to try out combinations of indexes?
Thanks!
This is a much simpler algorithm that will give you the shortest substring.
void Main()
{
String source = "aaa wwwww fgffsd ththththt sss ww sgsgsgsghs bfbfb hhh sdfg kkk " +
"dhdhtrherhrhrthrthrt ddfhdetehehe kkk wdwd aaa vcvc hhh zxzx sss ww nbnbn";
List<String> criteria = new List<string> { "kkk", "aaa", "sss ww", "hhh" };
var result = GetAllSubstringContainingCriteria(source, criteria)
.OrderBy(sub => sub.Length).FirstOrDefault();
// result is "kkk wdwd aaa vcvc hhh zxzx sss ww"
}
private IEnumerable<string> GetAllSubstringContainingCriteria(
string source, List<string> criteria)
{
for (int i = 0; i < source.Length; i++)
{
var subString = source.Substring(i);
if (criteria.Any(crit => subString.StartsWith(crit)))
{
var lastWordIndex =
GetLastCharacterIndexFromLastCriteriaInSubstring(subString, criteria);
if (lastWordIndex >= 0)
yield return string.Join(" ", subString.Substring(0, lastWordIndex));
}
else
continue;
}
}
private int GetLastCharacterIndexFromLastCriteriaInSubstring(
string subString, List<string> criteria)
{
var results = criteria.Select(crit => new {
index = subString.IndexOf(crit),
criteria = crit});
return results.All(result => result.index >= 0)
? results.Select(result => result.index + result.criteria.Length).Max()
: -1;
}
Let the Java built-in classes do the work. How about converting your criteria to a regular expression Pattern. If the criteria are X or Y or Z . . ., convert this into a regular expression of the form "(X)|(Y)|(Z)|...", compile it, and execute it against the source string.
This, of course, returns the leftmost match. You could code a very straightforward loop that iterates across all occurrences, caches them, and chooses the shortest--or the leftmost shortest--or, if two or more are equally short, then all of those.
I need to implement a module which will convert a List which with splitted string values to a possible value sets.
For Example
consider the list contains following values
1
1,2
3
4
5
The module should convert the above list to list of possible value sets
1,2,3,4,5
1,1,3,4,5
thanks in advance
This will do it, although it will return your example in the opposite order:
static IEnumerable<string> Permutations(
IEnumerable<string> input,
char separator)
{
var sepAsString = separator.ToString();
var enumerators = input
.Select(s => s.Split(separator).GetEnumerator())
.ToArray();
if (!enumerators.All(e => e.MoveNext())) yield break;
while (true)
{
yield return String.Join(sepAsString, enumerators.Select(e => e.Current));
if (enumerators.Reverse().All(e => {
bool finished = !e.MoveNext();
if (finished)
{
e.Reset();
e.MoveNext();
}
return finished;
}))
yield break;
}
}
Usage:
var list = new[] { "1", "1,2", "3", "4", "5" }.ToList();
var perms = Permutations(list, ',').ToList();
Rawling's answer is pretty solid, but i don't find it easy to read and understand. Here's another way, using less Linq.
private List<string> Process(IEnumerable<string> input)
{
List<string> data = new List<string>();
int preExpandCount = 0, offset = 0;
foreach (string inputItem in input)
{
List<string> splitItems = inputItem.Split(',').ToList();
if (data.Count > 0)
preExpandCount = ExpandList(data, splitItems.Count - 1);
offset = 0;
foreach (string splitItem in splitItems)
{
if (preExpandCount == 0)
data.Add(splitItem);
else
{
for (int i = 0; i < preExpandCount; i++)
data[i + offset] = String.Format("{0},{1}", data[i + offset], splitItem);
offset += preExpandCount;
}
}
}
return data.OrderBy(e => e).ToList();
}
private int ExpandList(List<string> existing, int count)
{
int existingCount = existing.Count;
for (int i = 0; i < count; i++)
existing.AddRange(existing.Take(existingCount).ToList());
return existingCount;
}
I want to publish server-messages on Twitter, for our clients.
Unfortunately, Twitter only allows posting 140 Chars or less. This is a shame.
Now, I have to write an algorithm that concatenates the different messages from the server together, but shortens them to a max of 140 characters.
It's pretty tricky.
CODE
static string concatinateStringsWithLength(string[] strings, int length, string separator) {
// This is the maximum number of chars for the strings
// We have to subtract the separators
int maxLengthOfAllStrings = length - ((strings.Length - 1) * separator.Length);
// Here we save all shortenedStrings
string[] cutStrings = new string[strings.Length];
// This is the average length of all the strings
int averageStringLenght = maxLengthOfAllStrings / strings.Length;
// Now we check how many strings are longer than the average string
int longerStrings = 0;
foreach (string singleString in strings)
{
if (singleString.Length > averageStringLenght)
{
longerStrings++;
}
}
// If a string is smaller than the average string, we can more characters to the longer strings
int maxStringLength = averageStringLenght;
foreach (string singleString in strings)
{
if (averageStringLenght > singleString.Length)
{
maxStringLength += (int)((averageStringLenght - singleString.Length) * (1.0 / longerStrings));
}
}
// Finally we shorten the strings and save them to the array
int i = 0;
foreach (string singleString in strings)
{
string shortenedString = singleString;
if (singleString.Length > maxStringLength)
{
shortenedString = singleString.Remove(maxStringLength);
}
cutStrings[i] = shortenedString;
i++;
}
return String.Join(separator, cutStrings);
}
Problem with this
This algorithm works, but it's not very optimized.
It uses less characters than it actually could.
The main problem with this is that the variable longerStrings is relative to the maxStringLength, and backwards.
This means if I change longerStrings, maxStringLength gets changed, and so on and so on.
I'd have to make a while loop and do this until there are no changes, but I don't think that's necessary for such a simple case.
Can you give me a clue on how to continue?
Or maybe there already exists something similar?
Thanks!
EDIT
The messages I get from the server look like this:
Message
Subject
Date
Body
Message
Subject
Date
Body
And so on.
What I want is to concatenate the strings with a separator, in this case a semi-colon.
There should be a max length. The long strings should be shortened first.
Example
This is a subject
This is the body and is a bit lon...
25.02.2013
This is a s...
This is the...
25.02.2013
I think you get the idea ;)
Five times slower than yours (in our simple example) but should use maximum avaliable space (no critical values checking):
static string Concatenate(string[] strings, int maxLength, string separator)
{
var totalLength = strings.Sum(s => s.Length);
var requiredLength = totalLength - (strings.Length - 1)*separator.Length;
// Return if there is enough place.
if (requiredLength <= maxLength)
return String.Concat(strings.Take(strings.Length - 1).Select(s => s + separator).Concat(new[] {strings.Last()}));
// The problem...
var helpers = new ConcatenateInternal[strings.Length];
for (var i = 0; i < helpers.Length; i++)
helpers[i] = new ConcatenateInternal(strings[i].Length);
var avaliableLength = maxLength - (strings.Length - 1)*separator.Length;
var charsInserted = 0;
var currentIndex = 0;
while (charsInserted != avaliableLength)
{
for (var i = 0; i < strings.Length; i++)
{
if (charsInserted == avaliableLength)
break;
if (currentIndex >= strings[i].Length)
{
helpers[i].Finished = true;
continue;
}
helpers[i].StringBuilder.Append(strings[i][currentIndex]);
charsInserted++;
}
currentIndex++;
}
var unified = new StringBuilder(avaliableLength);
for (var i = 0; i < strings.Length; i++)
{
if (!helpers[i].Finished)
{
unified.Append(helpers[i].StringBuilder.ToString(0, helpers[i].StringBuilder.Length - 3));
unified.Append("...");
}
else
{
unified.Append(helpers[i].StringBuilder.ToString());
}
if (i < strings.Length - 1)
{
unified.Append(separator);
}
}
return unified.ToString();
}
And ConcatenateInternal:
class ConcatenateInternal
{
public StringBuilder StringBuilder { get; private set; }
public bool Finished { get; set; }
public ConcatenateInternal(int capacity)
{
StringBuilder = new StringBuilder(capacity);
}
}