views:

355

answers:

5

Accommodating legacy database tables that were designed specifically for the IBM mainframe screens they represent has caused me much aggravation. Often, I find the need to break a string into multiple lines to fit the table column width as well as the users who are still viewing the data with terminal emulators. Here are two functions I've written to perform that task, accepting a string and a line width as parameters and returning some string enumerable. Which do you think is the better function and why? And by all means share the super-easy-fast-efficient way that I totally overlooked.

  public string[] BreakStringIntoArray(string s, int lineWidth)
  {
   int lineCount = ((s.Length + lineWidth) - 1) / lineWidth;
   string[] strArray = new string[lineCount];
   for (int i = 0; i <= lineCount - 1; i++)
   {
    if (((i * lineWidth) + lineWidth) >= s.Length)
     strArray[i] = s.Substring(i * lineWidth);
    else
     strArray[i] = s.Substring(i * lineWidth, lineWidth);
   }
   return strArray;
  }

vs.

  public List<string> BreakStringIntoList(string s, int lineWidth)
  {
   List<string> lines = new List<string>();
   if (s.Length > lineWidth)
   {
    lines.Add(s.Substring(0, lineWidth));
    lines.AddRange(this.BreakStringIntoList(s.Substring(lineWidth), lineWidth));
   }
   else
   {
    lines.Add(s);
   }
   return lines;
  }

For example, passing in ("Hello world", 5) would return 3 strings:

"Hello"
" worl"
"d"
A: 

The first one is way better.

The second one will produce tons of temporary objects which is completely unnecessary if you can pre-determine the amount of target lines.

And for having something overlooked: Depending on the speed requirements you could likely use pointers to get a considerable speedup (depends on what you actually want to do with the result). But this will be WAY MORE complex than it is now.

Just to quantify the "tons of temporary objects". If you have a 1MB String (and worst case line-length of 1) the first approach needs 1MB of String-contents memory allocations. The second will need 500GB of String-contents memory allocations.

Foxfire
Excellent analysis. My own tests showed that while a short string of 100 characters ran about equally fast (practically immediately on my machine) from either function, longer strings proved the array method stunningly faster and more memory efficient.
StarTrekRedneck
A: 
public List<string> BreakStringIntoLines(string s, int lineWidth)
{
     string working = s;
     List<string> result = new List<string>(Math.Ceil((double)s.Length / lineWidth));
     while (working.Length > lineWidth)
     {
          result.add(working.Substring(0, lineWidth);
          working = working.Substring(5);
     }

     result.Add(working);

     return result;
}

That's probably how I would do it. A List is more flexible than a string array IMHO.

Also, I would avoid recursion like the plague. It is far easier to create and debug something that uses a simple loop.

nasufara
working = working.Substring(5);keep creating string instance.. i'd avoid that..
Itay
A: 
Regex.Replace(input, ".{5}", x => x.Value + "\n").Split(new char [] {'\n'})
leppie
A: 

EDIT: sorry, i thought you want one string devided into lines ^__^"

depends on the length of the string.. but for long ones:

    string BreakStringIntoLines(string s, int lineWidth)
    {
        StringBuilder sb = new StringBuilder(s);
        for (int i = lineWidth; i < sb.Length; i += lineWidth)
        {
            sb.Insert(i, Environment.NewLine);
        }
        return sb.ToString();
    }
Itay
A: 

I like the solution from Itay, but it does not produce the right output, so here's my edited version.

protected void Page_Load(object sender, EventArgs e)
{
    string a = "12345678901234567890123456789012345";
    TextBox1.Text = a;
    TextBox2.Text = BreakStringIntoLinesVer2(a, 10);

}

string BreakStringIntoLinesVer2(string s, int lineWidth)
{
    StringBuilder sb = new StringBuilder(s);
    int last = (sb.Length % lineWidth == 0) ? sb.Length - lineWidth : sb.Length - (sb.Length % lineWidth);

    for (int i = last; i > 0; i -= lineWidth)
    {
        sb.Insert(i, Environment.NewLine);
    }
    return sb.ToString();
}
mangokun