Search code examples
c++word-wrapistringstream

wordwap function fix to preserve whitespace between words


Some time ago I was looking for a snippet to do a wordwrap for a certain size of line length without breaking up the words. It was working fair enough, but now when I started using it in edit control, I noticed it eats up multiple white space symbols in between. I am contemplating how to fix it or get rid of it completely if wstringstream is not suitable for the task. Maybe someone out there have a similar function?

void WordWrap2(const std::wstring& inputString, std::vector<std::wstring>& outputString, unsigned int lineLength)
{
   std::wstringstream  iss(inputString);
   std::wstring line;
   std::wstring word;

   while(iss >> word)
   { 
      if (line.length() + word.length() > lineLength)
      {
         outputString.push_back(line+_T("\r"));
         line.clear();
      }
      if( !word.empty() ) {
      if( line.empty() ) line += word; else line += +L" " + word;
      }

   }

   if (!line.empty())
   { 
      outputString.push_back(line+_T("\r"));
   }
}

Wrap line delimiter symbol should remain \r


Solution

  • Instead of reading a word at a time, and adding words until you'd exceed the desired line length, I'd start from the point where you want to wrap, and work backwards until you find a white-space character, then add that entire chunk to the output.

    #include <iostream>
    #include <string>
    #include <vector>
    #include <stdlib.h>
    
    void WordWrap2(const std::wstring& inputString, 
                   std::vector<std::wstring>& outputString, 
                   unsigned int lineLength) {
        size_t last_pos = 0;
        size_t pos;
    
        for (pos=lineLength; pos < inputString.length(); pos += lineLength) {
    
            while (pos > last_pos && !isspace((unsigned char)inputString[pos]))
                --pos;
    
            outputString.push_back(inputString.substr(last_pos, pos-last_pos));
            last_pos = pos;
            while (isspace((unsigned char)inputString[last_pos]))
                ++last_pos;
        }
        outputString.push_back(inputString.substr(last_pos));
    }
    

    As it stands, this will fail if it encounters a single word that's longer than the line length you've specified (in such a case, it probably should just break in the middle of the word, but it currently doesn't).

    I've also written it to skip over whitespace between words when they happen at a line break. If you really don't want that, just eliminate the:

            while (isspace((unsigned char)inputString[last_pos]))
                ++last_pos;