Wrap text to the next line when it exceeds a certain length?
Asked Answered
U

11

17

I need to write different paragraphs of text within a certain area. For instance, I have drawn a box to the console that looks like this:

/----------------------\
|                      |
|                      |
|                      |
|                      |
\----------------------/

How would I write text within it, but wrap it to the next line if it gets too long?

Usance answered 10/5, 2012 at 19:47 Comment(5)
What you you tried? what went wrong?Saccharometer
I take it you don't want to split words up?Dorcasdorcea
@Saccharometer I tried reading the length of the string, and splitting it if it exceeds the width of the box, but that would require me to split words. And it just didn't seem to work all that well.Usance
@ryansworld10 this is the third link when I google split words max width. Is that so hard?Saccharometer
check out my recursion solution at the endAmabil
F
17

Split on last space before your row length?

int myLimit = 10;
string sentence = "this is a long sentence that needs splitting to fit";
string[] words = sentence.Split(new char[] { ' ' });
IList<string> sentenceParts = new List<string>();
sentenceParts.Add(string.Empty);

int partCounter = 0;

foreach (string word in words)
{
    if ((sentenceParts[partCounter] + word).Length > myLimit)
    {
        partCounter++;
        sentenceParts.Add(string.Empty);
    }

    sentenceParts[partCounter] += word + " ";
}

foreach (string x in sentenceParts)
    Console.WriteLine(x);

UPDATE (the solution above lost the last word in some cases):

int myLimit = 10;
string sentence = "this is a long sentence that needs splitting to fit";
string[] words = sentence.Split(' ');

StringBuilder newSentence = new StringBuilder();


string line = "";
foreach (string word in words)
{
    if ((line + word).Length > myLimit)
    {
        newSentence.AppendLine(line);
        line = "";
    }

    line += string.Format("{0} ", word);
}

if (line.Length > 0)
    newSentence.AppendLine(line);

Console.WriteLine(newSentence.ToString());
Frankfort answered 10/5, 2012 at 20:22 Comment(7)
That has the possibility of splitting words in half, which I don't want.Usance
Some great examples hereFrankfort
It should not split words in half, because it adds only full words until the length limit is reached. However, it would be better to use StringBuilder object to build up the final line and use String.Format method like this: String.Format("{0} ", word);. Using enumerator for the list instead of getting elements by indices would also be nice :). Those upgrades can make the code more efficient, however, it should work as it is as well.Irena
This works perfectly! @Lucas Could you please post some of those modifications? :)Usance
Jim updated his response with my suggestions, however, depending on how you want to use it, it may be more useful to build array of lines (as in the first code snippet), but using a StringBUilder (as in the second one).Irena
Yes, Lucas. Thanks for you idea. How would you do it with the string[]? Could there be a better ways using regexp possibly?Frankfort
I suppose that regex is not needed in this case. However, when talking about string variables, you could use StringBuilder for creating a single line and after a whole line is built, save it to list of lines (of List<String> type).Irena
S
5

Here's one that is lightly tested and uses LastIndexOf to speed things along (a guess):

    private static string Wrap(string v, int size)
    {
        v = v.TrimStart();
        if (v.Length <= size) return v;
        var nextspace = v.LastIndexOf(' ', size);
        if (-1 == nextspace) nextspace = Math.Min(v.Length, size);
        return v.Substring(0, nextspace) + ((nextspace >= v.Length) ? 
        "" : "\n" + Wrap(v.Substring(nextspace), size));
    }
Senhorita answered 24/10, 2018 at 12:59 Comment(3)
Ahh useful! I added a solution to this myself using recursion, but only now saw this solution.Amabil
above code doesn't work when the input string already contains newlinesChipper
@Chipper You could replace any newlines with spaces before the conversion. v = v.Replace("\n"," ")Brewis
M
4

I started with Jim H.'s solution and end up with this method. Only problem is if text has any word that longer than limit. But works well.

public static List<string> GetWordGroups(string text, int limit)
{
    var words = text.Split(new string[] { " ", "\r\n", "\n" }, StringSplitOptions.None);

    List<string> wordList = new List<string>();

    string line = "";
    foreach (string word in words)
    {
        if (!string.IsNullOrWhiteSpace(word))
        {
            var newLine = string.Join(" ", line, word).Trim();
            if (newLine.Length >= limit)
            {
                wordList.Add(line);
                line = word;
            }
            else
            {
                line = newLine;
            }
        }
    }

    if (line.Length > 0)
        wordList.Add(line);

    return wordList;
}
Mahratta answered 26/12, 2016 at 11:53 Comment(0)
P
3

I modified the version of Jim H such that it supports some special cases. For example the case when the sentence does not contain any whitespace character; I also noted that there is a problem when a line has a space at the last position; then the space is added at the end and you end up with one character too much.

Here is my version just in case someone is interested:

public static List<string> WordWrap(string input, int maxCharacters)
{
    List<string> lines = new List<string>();

    if (!input.Contains(" "))
    {
        int start = 0;
        while (start < input.Length)
        {
            lines.Add(input.Substring(start, Math.Min(maxCharacters, input.Length - start)));
            start += maxCharacters;
        }
    }
    else
    {
        string[] words = input.Split(' ');

        string line = "";
        foreach (string word in words)
        {
            if ((line + word).Length > maxCharacters)
            {
                lines.Add(line.Trim());
                line = "";
            }

            line += string.Format("{0} ", word);
        }

        if (line.Length > 0)
        {
            lines.Add(line.Trim());
        }
    }

    return lines;
}
Propraetor answered 21/5, 2014 at 20:41 Comment(0)
R
3

This is a more complete and tested solution.

  • The bool overflow parameter specifies, whether long words are chunked in addition to splitting up by spaces.
  • Consecutive whitespaces, as well as \r, \n, are ignored and collapsed into one space.
  • Edge cases are throughfully tested

public static string WrapText(string text, int width, bool overflow)
{
    StringBuilder result = new StringBuilder();

    int index = 0;
    int column = 0;

    while (index < text.Length)
    {
        int spaceIndex = text.IndexOfAny(new[] { ' ', '\t', '\r', '\n' }, index);

        if (spaceIndex == -1)
        {
            break;
        }
        else if (spaceIndex == index)
        {
            index++;
        }
        else
        {
            AddWord(text.Substring(index, spaceIndex - index));
            index = spaceIndex + 1;
        }
    }

    if (index < text.Length) AddWord(text.Substring(index));

    void AddWord(string word)
    {
        if (!overflow && word.Length > width)
        {
            int wordIndex = 0;
            while (wordIndex < word.Length)
            {
                string subWord = word.Substring(wordIndex, Math.Min(width, word.Length - wordIndex));
                AddWord(subWord);
                wordIndex += subWord.Length;
            }
        }
        else
        {
            if (column + word.Length >= width)
            {
                if (column > 0)
                {
                    result.AppendLine();
                    column = 0;
                }
            }
            else if (column > 0)
            {
                result.Append(" ");
                column++;
            }

            result.Append(word);
            column += word.Length;
        }
    }

    return result.ToString();
}
Rheumatoid answered 26/2, 2019 at 10:18 Comment(0)
A
2

I modified Manfred's version. If you put a string with the '\n' character in it, it will wrap the text strangely because it will count it as another character. With this minor change all will go smoothly.

public static List<string> WordWrap(string input, int maxCharacters)
    {
        List<string> lines = new List<string>();

        if (!input.Contains(" ") && !input.Contains("\n"))
        {
            int start = 0;
            while (start < input.Length)
            {
                lines.Add(input.Substring(start, Math.Min(maxCharacters, input.Length - start)));
                start += maxCharacters;
            }
        }
        else
        {
            string[] paragraphs = input.Split('\n');

            foreach (string paragraph in paragraphs)
            {
                string[] words = paragraph.Split(' ');

                string line = "";
                foreach (string word in words)
                {
                    if ((line + word).Length > maxCharacters)
                    {
                        lines.Add(line.Trim());
                        line = "";
                    }

                    line += string.Format("{0} ", word);
                }

                if (line.Length > 0)
                {
                    lines.Add(line.Trim());
                }
            }
        }
        return lines;
    }
Annam answered 30/5, 2014 at 17:46 Comment(0)
P
1

Other answers didn't consider East Asian languages, which don't use space to break words.

In general, a sentence in East Asian languages can be wrapped in any position between characters, except certain punctuations (it is not a big problem even if ignore punctuation rules). It is much simpler than European languages but when consider mixing different languages, you have to detect the language of each character by checking the Unicode table, and then apply the break lines by space algorithm only for European languages parts.

References: https://en.wikipedia.org/wiki/Line_wrap_and_word_wrap https://en.wikipedia.org/wiki/Line_breaking_rules_in_East_Asian_languages

https://en.wikibooks.org/wiki/Unicode/Character_reference/0000-0FFF

Proselytize answered 6/3, 2021 at 12:23 Comment(0)
S
0

This code will wrap the paragraph text. It will break the paragraph text into lines. If it encounters any word which is even larger than the line length, it will break the word into multiple lines too.

private const int max_line_length = 25;

private string wrapLinesToFormattedText(string p_actual_string) {

    string formatted_string = "";
    int available_length = max_line_length;

    string[] word_arr = p_actual_string.Trim().Split(' ');

    foreach (string w in word_arr) {

        string word = w;
        if (word == "") {
            continue;
        }


        int word_length = word.Length;

        //if the word is even longer than the length that the line can have
        //the large word will get break down into lines following by the successive words 
        if (word_length >= max_line_length)
        {
            if (available_length > 0)
            {
                formatted_string += word.Substring(0, available_length) + "\n";
                word = word.Substring(available_length);
            }
            else
            {
                formatted_string += "\n";
            }
            word = word + " ";
            available_length = max_line_length;
            for (var count = 0;count<word.Length;count++) {
                char ch = word.ElementAt(count);

                if (available_length==0) {
                    formatted_string += "\n";
                    available_length = max_line_length;
                }

                formatted_string += ch;
                available_length--;
            }                    
            continue;
        }




        if ((word_length+1) <= available_length)
        {
            formatted_string += word+" ";
            available_length -= (word_length+1);
            continue;
        }
        else {
            available_length = max_line_length;
            formatted_string += "\n"+word+" " ;
            available_length -= (word_length + 1);
            continue;                    
        }

    }//end of foreach loop

    return formatted_string;
}
//end of function wrapLinesToFormattedText

Blockquote

Spark answered 25/3, 2018 at 7:10 Comment(0)
B
0

Here is a small piece of optimized code for wrapping text according to float sentence length limit written in Visual Basic9.

    Dim stringString = "Great code! I wish I could found that when searching for Print Word Wrap VB.Net and other variations when searching on google. I’d never heard of MeasureString until you guys mentioned it. In my defense, I’m not a UI programmer either, so I don’t feel bad for not knowing"
    Dim newstring = ""
    Dim t As Integer = 1
    Dim f As Integer = 0
    Dim z As Integer = 0
    Dim p As Integer = stringString.Length
    Dim myArray As New ArrayList
    Dim endOfText As Boolean = False REM to exit loop after finding the last words
    Dim segmentLimit As Integer = 45

    For t = z To p Step segmentLimit REM you can adjust this variable to fit your needs
        newstring = String.Empty
        newstring += Strings.Mid(stringString, 1, 45)

        If Strings.Left(newstring, 1) = " " Then REM Chr(13) doesn't work, that's why I have put a physical space
            newstring = Strings.Right(newstring, newstring.Length - 1)
        End If

        If stringString.Length < 45 Then
            endOfText = True
            newstring = stringString

            myArray.Add(newstring) REM fills the last entry then exits
            myArray.TrimToSize()
            Exit For
        Else
            stringString = Strings.Right(stringString, stringString.Length - 45)
        End If

        z += 44 + f
        If Not Strings.Right(newstring, 1) = Chr(32) Then REM to detect space
            Do Until Strings.Right(newstring, z + 1) = " "
                If Strings.Right(newstring, z + f) = " " OrElse Strings.Left(stringString, 1) = " " Then
                    Exit Do
                End If

                newstring += Strings.Left(stringString, 1)
                stringString = Strings.Right(stringString, stringString.Length - 1) REM crops the original 
                p = stringString.Length REM string from left by 45 characters and additional characters

                t += f
                f += 1
            Loop

            myArray.Add(newstring) REM puts the resulting segments of text in an array
            myArray.TrimToSize()

            newstring = String.Empty REM empties the string to load the next 45 characters
        End If
        t = 1
        f = 1
    Next
    
    For Each item In myArray
        MsgBox(item)
        'txtSegmentedText.Text &= vbCrLf & item
    Next
Bruckner answered 23/7, 2020 at 19:9 Comment(0)
A
0

I know I am a bit late, But I managed to get a solution going by using recursion. I think its one of the cleanest solutions proposed here.

Recursive Function:

public StringBuilder TextArea { get; set; } = new StringBuilder();

public void GenerateMultiLineTextArea(string value, int length)
{
    // first call - first length values -> append first length values, remove first length values from value, make second call
    // second call - second length values -> append second length values, remove first length values from value, make third call
    // third call - value length is less then length just append as it is

    if (value.Length <= length && value.Length != 0)
    {

        TextArea.Append($"|{value.PadRight(length)}" + "|");
    }
    else
    {
        TextArea.Append($"|{value.Substring(0, length).ToString()}".PadLeft(length) + "|\r\n");
        value = value.Substring(length, (value.Length) - (length));
        GenerateMultiLineTextArea(value, length);
    }
}

Usage:

string LongString = 
"This is a really long string that needs to break after it reaches a certain limit. " +
"This is a really long string that needs to break after it reaches a certain limit." + "This is a really long string that needs to break after it reaches a certain limit.";

GenerateMultiLineTextArea(LongString, 22);
Console.WriteLine("/----------------------\\");
Console.WriteLine(TextArea.ToString());
Console.WriteLine("\\----------------------/");

Outputs:

/----------------------\
|This is a really long |
|string that needs to b|
|reak after it reaches |
|a certain limit. This |
|is a really long strin|
|g that needs to break |
|after it reaches a cer|
|tain limit.This is a r|
|eally long string that|
| needs to break after |
|it reaches a certain l|
|imit.                 |
\----------------------/
Amabil answered 31/8, 2020 at 11:44 Comment(0)
O
0

This is the real word wrapping sample:

 public static IEnumerable<string> WordWrap(this string text, int maxLen = 80)
 {
     var loop = true;

     if (text == null) {
         // throw new ArgumentNullException("text cannot be null");
         loop = false;            
     }
     if (maxLen < 1) { 
         // throw new ArgumentException("maxLen must be greater than 0"); 
         loop |= false;  
     }

     while (loop)
     {
         var line = "";

         if (text.Length <= maxLen)
         {
             line = text;
             yield return line;
             break; // while
         }

         // find a space if any from right to left
         for (int i = maxLen; i>=0; i--)
         {
             if (text.Substring(i, 1) == " ")
             {
                 line = text.Substring(0, i);
                 text = text.Substring(i + 1);

                 yield return line;
                 break; // for
             }
         }

         // If no space found; return all chars
         if(String.IsNullOrEmpty(line))
         {
            line = text.Substring(0, text.Length < maxLen ? text.Length : maxLen);
            if(text.Length > maxLen)
            {
                text = text.Substring(maxLen, text.Length - maxLen);
            }
         
            yield return line;


         }
     }
 }
Oliana answered 24/4 at 8:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.