Monday, 24 January 2011

The C Programming Language (K&R) 01x17—Tab for Spaces—Exercise 1-20

Exercises 1-20 of “The C Programming Language” by Brian Kernighan and Dennis Ritchie aka K&R deal with parsing text entered by a user.

Exercise 1-20. Write a program detab that replaces tabs in the input with the proper number of blanks to space to the next tab stop. Assume a fixed set of tab stops, say every n columns. Should n be a variable or a symbolic parameter?

By this point in the book, writing this code, even from scratch, should be straightforward, but there is some work to figure out tab stops since a tab moves to fixed stops. It’s not the case that every tab is replaced with n number of spaces. The number of spaces will vary. Second, to replace a tab ‘\t’, which is one character, with several spaces, means stretching the char array. You can avoid this issue by outputting the text immediately and not storing any data in a character array.

On my computer, a tab takes up 8 characters. Here is an example.

Column:       1234567891123456789212345678931234567940
Element:      0123456789112345678921234567893123456794
              abcdT   abcabcT abcT    abc...

The first tab appears at element 4 or position 5 on the screen. The modulus of 5 and 8 is 5 and the number of spaces to add is 3. The tab width, 8, less 5 is three.

The tab character has to be replaced with a space so the number of spaces to add is: 1+ Tab width (8) less (Element position + 1) mod 8. This equation can be simplified since 1 mod 8 is one and subtracting it from 1 + Tab width leaves: Tab width less (Element position mod 8). The simplification of the equation is true for all values of tab width.

As for how to store the value of the tab width, it’s better to create a variable, TabWidth. If it’s a variable, it can be dynamically changed based on a user’s system without recompiling the program. If the value is stored as a symbolic constant, the value can only be changed by recompiling the program. The symbolic constant (e.g., #DEFINE TABWIDTH 8) is replaced by its literal value during the pre-compile process.


Sample Code.

I am using Visual C++ 2010 and created the sample code as a console application.

// Function prototype.

// The standard library includes the system function.
#include <cstdlib>

// Standard I/O library.
#include <cstdio>


int main()
{
     int c, i = 0;
     char line[80];

     // Get user input from keyboard.
     while ((c = getchar()) != EOF)
     {
           // Store char.
           if (c != '\n')
           {
                line[i] = c;
                ++i;
           }
           else
           {
                // Store null char to mark end.
                line[i] = '\0';
                // Parse text to replace tabs w spaces.
                detab(line);
                // Output text.
                printf("%s\n\n", line);
                // Reset counter.
                i = 0;
           }
     }

     // Keep console window open.
     system("pause");

     // Return some value.
     return 0;

} // end main
// Remove tabs and replace
int detab(char text[])
{
     int iFrom, iTo = 0;
     char temp[80];
     int SpaceCount = 0;
     int TabWidth = 8;

     for (iFrom = 0; text[iFrom]; ++iFrom)
     {
           // Find tab.
           if (text[iFrom] == '\t')
           {
                // Replace tab with spaces.
                // Calc the number of spaces to fill up to tab stop.
                // SpaceCount = 1 + TabWidth - ((iTo + 1) % TabWidth);
                // Equiv. stmt.
                SpaceCount = TabWidth - (iTo % TabWidth);
                for (; SpaceCount ; --SpaceCount, ++iTo)
                     temp[iTo] = ' ';
           }
           else
           {
                // Not a tab.
                // Store as is.
                temp[iTo] = text[iFrom];
                ++iTo;
           }
     }

     // Store null char to mark end.
     temp[iTo] = '\0';

     // Copy temp to text.
     iTo = 0;
     while ((text[iTo] = temp[iTo]) != '\0')
           ++iTo;

     // Store null char to mark end.
     text[iTo] = '\0';

     // Return something.
     return 0;
}
 
Output.


No comments:

Post a Comment