Best way to split string into lines with maximum length, without breaking words

Mark Farr picture Mark Farr · Mar 13, 2014 · Viewed 12.4k times · Source

I want to break a string up into lines of a specified maximum length, without splitting any words, if possible (if there is a word that exceeds the maximum line length, then it will have to be split).

As always, I am acutely aware that strings are immutable and that one should preferably use the StringBuilder class. I have seen examples where the string is split into words and the lines are then built up using the StringBuilder class, but the code below seems "neater" to me.

I mentioned "best" in the description and not "most efficient" as I am also interested in the "eloquence" of the code. The strings will never be huge, generally splitting into 2 or three lines, and it won't be happening for thousands of lines.

Is the following code really bad?

private static IEnumerable<string> SplitToLines(string stringToSplit, int maximumLineLength)
{
    stringToSplit = stringToSplit.Trim();
    var lines = new List<string>();

    while (stringToSplit.Length > 0)
    {
        if (stringToSplit.Length <= maximumLineLength)
        {
            lines.Add(stringToSplit);
            break;
        }

        var indexOfLastSpaceInLine = stringToSplit.Substring(0, maximumLineLength).LastIndexOf(' ');
        lines.Add(stringToSplit.Substring(0, indexOfLastSpaceInLine >= 0 ? indexOfLastSpaceInLine : maximumLineLength).Trim());
        stringToSplit = stringToSplit.Substring(indexOfLastSpaceInLine >= 0 ? indexOfLastSpaceInLine + 1 : maximumLineLength);
    }

    return lines.ToArray();
}

Answer

3vts picture 3vts · Apr 12, 2017

Even when this post is 3 years old I wanted to give a better solution using Regex to accomplish the same:

If you want the string to be splitted and then use the text to be displayed you can use this:

public string SplitToLines(string stringToSplit, int maximumLineLength)
{
    return Regex.Replace(stringToSplit, @"(.{1," + maximumLineLength +@"})(?:\s|$)", "$1\n");
}

If on the other hand you need a collection you can use this:

public MatchCollection SplitToLines(string stringToSplit, int maximumLineLength)
{
    return Regex.Matches(stringToSplit, @"(.{1," + maximumLineLength +@"})(?:\s|$)");
}

NOTES

Remember to import regex (using System.Text.RegularExpressions;)

You can use string interpolation on the match:
$@"(.{{1,{maximumLineLength}}})(?:\s|$)"

The MatchCollection works almost like an Array

Matching example with explanation here