There is a line: Quisque velit nisi, pretium ut lacinia in, elementum id enim. Nulla porttitor accumsan tincidunt. Quisque velit nisi, pretium ut lacinia in, elementum id enim. Nulla porttitor accumsan tincidunt.

It is necessary to break it into three parts, but save whole words and move it to the next line only after the end of the word. For example, we break into three lines:

 Quisque velit nisi, pretium ut lacinia in, elementum id enim. Nulla porttitor accumsan tincidunt. 

Now I use this simple code, but it cuts whole words:

 int chunkSize = string.Length / 3; int stringLength = string.Length; for (int i = 0; i < stringLength; i += chunkSize) { if (i + chunkSize > stringLength) chunkSize = stringLength - i; Console.WriteLine(string.Substring(i, chunkSize)); } 

It turns out at the output:

 Quisque velit nisi, pretium ut l acinia in, elementum id enim. Nu lla porttitor accumsan tincidunt . 

Tell me how can this be easier to implement?

UPDATE

Important: the original string may differ from the above. May contain any characters.

Found a simple solution here https://stackoverflow.com/a/17571171/2127124

 public static class ExtensionMethods { public static string[] Wrap(this string text, int max) { var charCount = 0; var lines = text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); return lines.GroupBy(w => (charCount += (((charCount % max) + w.Length + 1 >= max) ? max - (charCount % max) : 0) + w.Length + 1) / max) .Select(g => string.Join(" ", g.ToArray())) .ToArray(); } } 
  • If the line contains N words (or N - 1 separator ' ' ) and it needs to be divided into K parts, then the number of possible partitions is (N - K + 2) * (N - K + 1) / 2 provided that N >= K . What partition of these will suit you? Anyone? Or do you need the most optimal (with the lengths of the pieces closest to str.Length / K )? - Andrei NOP
  • Desirable optimal. I thought about splitting the source line into spaces through split, and then forming the necessary set of the resulting list. - pwb
  • I would break into words, then find the number of words in the line and bring it all out - Arthur
  • either it is banal to go in the usual cycle, to look, if they come to the end but the word has not ended - we go to the end of the word and carry over, and then again also - Arthur

2 answers 2

The idea is to find the indexes of the characters that need to be broken ideally (into equal parts). Find all possible splits of the string and select the option with the smallest error. For an error I decided to accept the sum of the absolute values ​​of the deviations of the selected indices from the ideal ones.

 static int Abs(int x) => x >= 0 ? x : -x; static void Main(string[] args) { // Π’Ρ…ΠΎΠ΄Π½ΠΎΠΉ тСкст var str = "Quisque velit nisi, pretium ut lacinia in, elementum id enim. Nulla porttitor accumsan tincidunt."; // ΠšΠΎΠ»ΠΈΡ‡Π΅ΡΡ‚Π²ΠΎ Π²Ρ‹Ρ…ΠΎΠ΄Π½Ρ‹Ρ… строк var numOfParts = 3; // ΠŸΠ΅Ρ€Π΅Ρ‡Π΅Π½ΡŒ Ρ€Π°Π·Π΄Π΅Π»ΠΈΡ‚Π΅Π»Π΅ΠΉ var delimeters = " "; // Π˜Π½Π΄Π΅ΠΊΡΡ‹, ΠΏΠΎ ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ Π½Π°Π΄ΠΎ Ρ€Π°Π·Π±ΠΈΡ‚ΡŒ Π² ΠΈΠ΄Π΅Π°Π»Π΅ var idealParts = Enumerable.Range(1, numOfParts - 1) .Select(x => ((str.Length - numOfParts + 1) * x + numOfParts / 2) / numOfParts) .ToArray(); // индСксы всСх ΠΈΠΌΠ΅ΡŽΡ‰ΠΈΡ…ΡΡ мСст, ΠΏΠΎ ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ ΠΌΠΎΠΆΠ½ΠΎ Ρ€Π°Π·Π±ΠΈΡ‚ΡŒ var indices = str.Select((c, i) => (c: c, i: i)) .Where(t => delimeters.Contains(tc)) .Select(t => ti) .ToArray(); // indices.Length ^ numOfParts var numOfSp = Enumerable.Range(0, numOfParts - 1) .Aggregate(1, (m, x) => m * indices.Length); // Π“Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅ΠΌ всС Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Π΅ ΠΊΠΎΠΌΠ±ΠΈΠ½Π°Ρ†ΠΈΠΈ var splits = new int[numOfSp][]; for (int d = 0; d < numOfSp; ++d) { var z = d; splits[d] = new int[numOfParts - 1]; for (int i = numOfParts - 2; i >= 0; --i) { splits[d][i] = z % indices.Length; z /= indices.Length; } } // Для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ ΠΊΠΎΠΌΠ±ΠΈΠ½Π°Ρ†ΠΈΠΈ подсчитываСм ΠΎΡˆΠΈΠ±ΠΊΡƒ var spWithEr = splits.Select(s => (s: s, er: s.Select((x, i) => Abs(indices[x] - idealParts[i])).Sum())) .ToArray(); // Находим ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡŒΠ½ΡƒΡŽ ΠΎΡˆΠΈΠ±ΠΊΡƒ var minEr = spWithEr.Min(t1 => t1.er); // Π’Ρ‹Π±ΠΈΡ€Π°Π΅ΠΌ ΠΊΠΎΠΌΠ±ΠΈΠ½Π°Ρ†ΠΈΡŽ с наимСньшСй ошибкой var minSp = spWithEr.First(t => t.er == minEr).s; // ΠžΡ‚Π±ΠΈΡ€Π°Π΅ΠΌ индСксы var sp = new[] { -1 }.Concat(minSp.Select(x => indices[x]) .Concat(new[] { str.Length })) .ToArray(); // Π Π°Π·Π±ΠΈΠ²Π°Π΅ΠΌ Π½Π° строки ΠΏΠΎ индСксам ΠΈΠ· sp for (int i = 0; i < sp.Length - 1; ++i) Console.WriteLine(str.Substring(sp[i] + 1, sp[i + 1] - sp[i] - 1)); Console.ReadLine(); } 

Conclusion:

 Quisque velit nisi, pretium ut lacinia in, elementum id enim. Nulla porttitor accumsan tincidunt. 

Is it overhead - it's up to you

  • Thanks for the answer and the code! You are right, for my task it is redundant. - pwb

We break a line into words using the Split method and set the maximum number of characters in a line. Then we build each line of words, separating them with spaces and checking whether the current line has not gotten the maximum length.
The code is quite compact, and let it speak for itself)

 var inputString = @"Quisque velit nisi, pretium ut lacinia in, elementum id enim. Nulla porttitor accumsan tincidunt"; var words = inputString.Split(new Char[] { ' ' }); var maxLengthString = 53; int wordIndex = 0; var spaceLetter = " "; var currentLine = new StringBuilder(); while (true) { if (currentLine.Length + words[wordIndex].Length + 1 > maxLengthString)// ΠžΠΏΡ€Π΅Π΄Π΅Π»ΡΠ΅ΠΌ Π½Π΅ привысила Π»ΠΈ тСкущая строка ΠΌΠ°ΠΊΡΠΈΠΌΠ°Π»ΡŒΠ½ΡƒΡŽ Π΄Π»ΠΈΠ½Ρƒ { Console.WriteLine(currentLine); currentLine.Remove(0, currentLine.Length); } currentLine.Append(words[wordIndex]); currentLine.Append(spaceLetter); wordIndex++; if(wordIndex == words.Length) { Console.WriteLine(currentLine); break; } } 
  • @pwb I think this method should be suitable for your task - Andrei S.
  • It is good that you found. Nevertheless, the question was asked and the answers were heard :) - Andrei S.
  • Tell me, what about commas and other characters in your example? My output is without commas and some characters, for example, cutting the symbol / :( - pwb
  • Added punctuation to regular expression var words = new Regex (@ "\ w (? <! \ D) [\ w '-,;.?!] *"). Matches (inputString); - Andrei S.
  • Can I have more numbers? For example Cheese, soft Serbian Brynza 35% 250g Kuč mlekara, Serbia For a good text should be the same. - pwb