4.15. Converting Between Tabs and Spaces in a Text File

Problem

You have a text file that contains tabs or spaces, and you want to convert from one to the other. For example, you may want to replace all tabs with three spaces, or you may want to do just the opposite and replace occurrences of some number of spaces with a single tab.

Solution

Regardless of whether you are replacing tabs with spaces or spaces with tabs, use the ifstream and ofstream classes in <fstream>. In the first (simpler) case, read data in with an input stream, one character at a time, examine it, and if it’s a tab, write some number of spaces to the output stream. Example 4-23 demonstrates how to do this.

Example 4-23. Replacing tabs with spaces

#include <iostream>
#include <fstream>
#include <cstdlib>

using namespace std;

int main(int argc, char** argv) {

   if (argc < 3)
      return(EXIT_FAILURE);

   ifstream in(argv[1]);
   ofstream out(argv[2]);

   if (!in || !out)
     return(EXIT_FAILURE);

   char c;
   while (in.get(c)) {
      if (c == '\t')
         out << "   "; // 3 spaces
      else
         out << c;
   }
   
   out.close();

   if (out)
      return(EXIT_SUCCESS);
   else
      return(EXIT_FAILURE);
}

If, instead, you need to replace spaces with tabs, see Example 4-24. It contains the function spacesToTabs that reads from an input stream, one character at a time, looking for three consecutive spaces. When it finds three in a row, it writes a tab to the output stream. For all other characters, or for fewer than three spaces, whatever is read from the input stream is written to the output stream.

Example 4-24. Replacing spaces with tabs

#include <iostream>
#include <istream>
#include <ostream>
#include <fstream>
#include <cstdlib>

using namespace std;

void spacesToTabs(istream& in, ostream& out, int spaceLimit) {

   int consecSpaces = 0;
   char c;

   while (in.get(c)) {
      if (c != ' ') {
         if (consecSpaces > 0) {
            for (int i = 0; i < consecSpaces; i++) {
               out.put(' ');
            }
            consecSpaces = 0;
         }
         out.put(c);
      } else {
         if (++consecSpaces == spaceLimit) {
            out.put('\t');
            consecSpaces = 0;
         }
      }
   }
}

int main(int argc, char** argv) {

   if (argc < 3)
      return(EXIT_FAILURE);

   ifstream in(argv[1]);
   ofstream out(argv[2]);

   if (!in || !out)
      return(EXIT_FAILURE);

   spacesToTabs(in, out, 3);

   out.close();

   if (out)
      return(EXIT_SUCCESS);
   else
      return(EXIT_FAILURE);
}

Discussion

The mechanism for both of these solutions is the same; only the algorithms differ. Read characters from an input stream using get, and put them to an output stream with put. Put your logic for doing the translation between calls to these two functions.

You probably noticed in Example 4-24 that in main I declared in and out to be of types ifstream and ofstream, respectively, and that the parameters to spacesToTabs are actually istream and ostream. I did this to allow spacesToTabs to work on any kind of input or output streams (well, not any kind of stream—ones that inherit from basic_istream or basic_ostream), and not just file streams. For example, you may have the text you want to reformat in a string stream (istringstream and ostringstream in <sstream>). In that case, do something like this:

istringstream istr;
ostringstream ostr;

// fill up istr with text...

spacesToTabs(istr, ostr);

As with strings, streams are actually class templates that are parameterized on the type of character the stream operates on. For example, an ifstream is a typedef for basic_ifstream<char>, and a wifstream is a typedef for basic_ifstream<wchar_t>. Thus, if you need spacesToTabs from Examples Example 4-23 or Example 4-24 to work on a stream of any kind of character, you can use the class templates instead of the typedefs:

template<typename T>
void spacesToTabs(std::basic_istream<T>& 




in,
                  std::basic_ostream<T>& out,
                  int spaceLimit) { //...

Get C++ Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.