Welcome to Honest Illusion Sign in | Join | Help

Wordwrapping in C#

Some time ago, I needed a function that would take a block of text, and word-wrap it at a specific line length. As apparently you have now done, I googled for it, and found a blog with a seemingly appropriate algorithm. 

Except it wasn't.  I immediately noticed that it wasn't very efficient -- but that was merely annoying.  It wasn't grave enough to actually effect the running time of my program noticeably.  However, I soon noticed something worse -- it just didn't work.  It would occasionally drop the final line of the block.  So, I spent an hour or so rewriting it - fixing that problem and giving it a much more efficient algorithm while I was at it. (plus adding a feature I needed for my project).

The key to my speed-up was to copy characters and create new strings as little as possible.  In the original, he was concatenating strings merely to measure how long they would be. When a line got to be too long, he throws away the string, and used the save int value of the previous length.  This could be don't not only faster, but also simpler by counting the characters and subtracting.

The revised code is given below.  Very little is left from the original (although I think I'm still using some of the original variable names).  The key to the speed-up is the avoid creating new string except when absolutely necessary.

Since the blog is probably going to manage the source code, it's available here.

 

using System;
using System.Text;
 
namespace Curran.Utils
{
    /// <summary>
    /// Class to hold Wrap function.
    /// </summary>
    public class WordWrap
    {
        #region Public Methods
        /// <summary>
        /// Wraps the specified original text.
        /// </summary>
        /// <param name="originalText">The original text.</param>
        /// <param name="maxWidth">Width of the max.</param>
        /// <returns></returns>
        public static string Wrap(string originalText, int maxWidth)
        {
            return Wrap(originalText, maxWidth, "");
        } /* Wrap */
 
        /// <summary>
        /// Wraps the specified original text, and prepends the 
        /// specified prefix to each line
        /// </summary>
        /// <param name="originalText">The original text.</param>
        /// <param name="maxWidth">Width of the max.</param>
        /// <param name="preFix">The prefix.</param>
        /// <returns></returns>
        public static string Wrap(string originalText, int maxWidth, string preFix)
        {
            // Convert exist CRs into line feeds.
            // (We'll respect existing LFs as forced line breaks)
            originalText = originalText.Replace("\r\n", "\n");
            originalText = originalText.Replace("\r", "\n");
            // Remove all existing tabs.
            originalText = originalText.Replace("\t", " ");
 
            string[] textParagraphs = originalText.Split('\n');
            StringBuilder wrappedBlock = new StringBuilder();
 
            for (int i = 0; i < textParagraphs.Length; i++)
            {
                string line = textParagraphsIdea;
 
                // In the code below:
                // begin is the character position of the first char of the current line.
                // end is the position of the last possible character in the current line.
                // a & b will be the actual beginning position & length of the current line.
 
                int begin = 0;
                int end = maxWidth;
                int a = 0;
                int b = 0;
 
                while (end < line.Length)
                {
                    // First, we look for the last space before the max size.
                    int pos = line.LastIndexOf(' ', end);
 
                    if (pos < begin)
                    {   // If there is no space in that range, we just take all of it.
                        a = begin;
                        b = maxWidth;
                        begin = begin + maxWidth + 1;
                    }
                    else
                    {   // otherwise, we take everything between begin & that space.
                        a = begin;
                        b = pos - begin;
                        begin = pos + 1;
                    }
 
                    // Here's where we build the text block. The second Append is
                    // the important line.  It grabs the range of character from the 
                    // middle of the line string.
                    wrappedBlock.Append(preFix);
                    wrappedBlock.Append(line, a, b);
                    wrappedBlock.Append(Environment.NewLine);
 
                    // Finally, we reset end to our new max position.
                    end = begin + maxWidth;
 
                }
                // Don't forget the final line.
                wrappedBlock.Append(preFix);
                wrappedBlock.Append(line.Substring(begin));
                wrappedBlock.Append(Environment.NewLine);
            }
 
//          wrappedBlock.Length-= Environment.NewLine.Length;        // remove final \r\n
            return wrappedBlock.ToString();
        }
        #endregion
    } /* WordWrap */
}
kick it on DotNetKicks.com
Share this post: Email it! | bookmark it! | digg it! | reddit!
Readability Stats: Word Count: 682; Sentence Count: 59; Grade Level: 5.9, more info...
Published Friday, October 06, 2006 5:59 PM by James
Filed under: , , ,

Comments

# http://spietrek.blogspot.com/

Friday, December 22, 2006 8:37 AM by TrackBack

# Wordwrapping in C#

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Friday, February 16, 2007 11:48 AM by DotNetKicks.com
New Comments to this post are disabled