Jump to: navigation, search

The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 1.20 on page 34

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?



Solution by Richard Heathfield

Thanks to Rick Dearman for pointing out that my solution used fgets() which has not been introduced by page 34. I've fixed the solution to use K&R's getline() function instead. Further thanks to Roman Yablonovsky who, in Oct 2000, pointed out that the solution was buggy, and hinted at a fix. Basically, the problem he spotted was that my solution failed to keep track of the cumulative effect of multiple tabs in a single line. I've adopted his fix (which was in fact also slightly buggy, but I've fixed that too).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_BUFFER   1024
#define SPACE        ' '
#define TAB          '\t'

int CalculateNumberOfSpaces(int Offset, int TabSize)
{
   return TabSize - (Offset % TabSize);
}

/* K&R's getline() function from p29 */
int getline(char s[], int lim)
{
  int c, i;

  for(i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
    s[i] = c;
  if(c == '\n')
  {
    s[i] = c;
    ++i;
  }
  s[i] = '\0';

  return i;
}

int main(void)
{
  char  Buffer[MAX_BUFFER];
  int TabSize = 5; /* A good test value */

  int i, j, k, l;

  while(getline(Buffer, MAX_BUFFER) > 0)
  {
    for(i = 0, l = 0; Buffer[i] != '\0'; i++)
    {
      if(Buffer[i] == TAB)
      {
        j = CalculateNumberOfSpaces(l, TabSize);
        for(k = 0; k < j; k++)
        {
          putchar(SPACE);
          l++;
        }
      }
      else
      {
        putchar(Buffer[i]);
        l++;
      }
    }
  }

  return 0;
}


Solution by unknown

In answer to the question about whether nshould be variable or symbolic, I'm tempted to offer the answer 'yes'. :-) Of course, it should be variable, to allow for modification of the value at runtime, for example via a command line argument, without requiring recompilation.

Another solution not posted on Richard's original site but bundled in the source archive (with a corrected error of using NULL in place of 0) is:

#include <stdio.h>
#include <stdlib.h>

#define TAB 7

int main(void) {
  int c,i;

  i = 0;

  while ((c = getchar()) != EOF) {
    i++;
    if (c == '\n')
      i = 0; /* reset column counter */
    if (c == '\t') {
      while ((i % TAB) != 0) {
        putchar(' ');
        i++;
      }
    } else {
      putchar(c);
    }

  }
  return(0);
}

Solution by Balgo

This is my solution.

#include <stdio.h>

#define MAXLINE 1000    /* maximum characters of a line */
#define TABWIDTH 4      /* tabsize */

int getline(char line[], int maxline);
void detab(char to[], char from[]);

int main(void)
{
    int len;                /* current line length */
    char line[MAXLINE];     /* current input line */
    char nline[MAXLINE];    /* detabed line saved here */

    while ((len = getline(line, MAXLINE)) > 0) {
        detab(nline, line);
        printf("%s", nline);
    }

    return 0;
}

/* getline: reads a line s, return line length */
int getline(char s[], int lim)
{
    int i, c;

    for (i = 0; i < lim-1 && (c = getchar()) != EOF && c != '\n'; ++i)
        s[i] = c;
    if (c == '\n') {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';

    return i;
}

/* detab: replaces tabs with blanks */
void detab(char to[], char from[])
{
    int i, j, n;

    i = j = n = 0;
    while ((to[j] = from[i]) != '\0') {
        if (to[j] == '\t')
            for (n = 0; n < TABWIDTH; ++n, ++j)
                to[j] = ' ';
        else
            ++j;
        ++i;
    }
}


Solution by Flash Gordon

Here is a solution not limited to the facilities introduced at this point, not limited to a given line length, and taking the tab spacing from the command line (defaulting to 8 if not specified).

#include <stdio.h>
#include <stdlib.h>

int main(int argc,char *argv[])
{
  unsigned long tab=8, pos=1;
  int c;

  if (argc>1)
    tab = strtoul(argv[1],NULL,10);
  if (tab==0)
    tab=8;

  while ((c=getchar()) != EOF) {
    switch (c) {
      case '\t': c=' ';
                 while (pos++ < tab)
                   putchar(' ');
      case '\n': pos=0;
    }
    putchar(c);
    if (pos==tab) pos=1;
    else pos++;
  }
  return (ferror(stdin) || ferror(stdout))?EXIT_FAILURE:EXIT_SUCCESS;
}


Solution by Sudeep Mandal

This is a solution that modifies the getline() function in K&R2 to insert the blanks in place of tabs in a single loop. It should be more efficient than running two separate loops, once to read the line and then again to insert the blanks. Only uses concepts introduced in the first chapter.


"--i" in function getline should be commented off. --Tao Wang 11:50, 27 February 2017 (EST)


#include <stdio.h>

#define MAXLEN 1000 /*Max length of string*/
#define SPACE '#'
#define TAB	'\t'

int tabsize;
int getline(char s[], int lim);
void reverse(char s[]);

main() 
{
	extern int tabsize;/* No of spaces per tab */
	int len;
	char line[MAXLEN];

	tabsize = 5;
	while ((len = getline(line,MAXLEN))>1)
		printf ("\n%s\n",line);

}

/* Returns length of modified string array */
int getline(char s[], int lim)
{
	extern int tabsize;
	int c,i,j;
	i=0;
	while (i < lim - 1 && (c = getchar()) != EOF && c != '\n') {
		if (c == '\t') { /* Replace tab with spaces */
			for (j=0; j<tabsize; j++) {
				s[i]=SPACE;
				++i;
			}
			--i;
		}
		else {
			s[i] = c;
			++i;
		}
	}
	if(c == '\n')
	{
		s[i] = c;
		++i;
	}
	s[i] = '\0';
	return i;
}

Solution by Pilcrow

This is really very simple. getline is not needed (no buffer overflow problems). Whether to #define TAB, or get it from the command line is just a quibble.

#include <stdio.h>

#define TAB 5

int main(void)
{
	int c, x, r;
	x = 0;

	while((c = getchar())!= EOF) {
		if(c == '\n') {
			putchar(c);
			x = 0;
		}
		else if (c == '\t') {
			r = TAB - x % TAB;
			while(r-- != 0) {
				putchar(' ');
				x++;
			}
		}
		else {
			putchar(c);
			x++;
		}
	}
	return 0;
}



I use format string in printf to print the spaces

/**
 Name: detab
 Details: replaces tabs in the input with the proper
          number of blanks to space to the next tab stop
 */
#include <stdio.h>

#define TABSIZE 4
#define SPACE_STR "        "


int main()
{
    int c = 0;
    while ( (c = getchar()) != EOF )
    {
        if (c == '\t')
        {
            // print the number of space base on TABSIZE and SPACE_STR
            printf("%.*s",TABSIZE,SPACE_STR);
        }
        else
            putchar(c);
    }
    return 0;
}

Solution by Pyne.peter

I went about things a little differently, counting down from the TABSPAN, then using that value as our number of spaces.

#include <stdio.h>
#define TABSPAN 8                       /* Number of columns between tab stops */

int t = (TABSPAN - 1);                  /* Added -1 here due to odd behaviour of multi tab first line in windows CMD */
int c = 0;

int main(){
    while ((c = getchar()) != EOF){
        if (c == '\n' || t == 0) {
            t = TABSPAN;                /* Reset tabcount if a newline is encountered or tabspan hits 0 */
        }
        if (c == '\t') {                /* If a tab is detected... */
            c = 0;                      /* Don't write the tab */
            if (t == TABSPAN)           /* Handle tabs on the border */
                t = 0;
            for (; t != 0; --t) {       /* Write the amount of spaces based on tabspan */
                putchar(' ');
            }
            t = TABSPAN;                /* Update tabcount after doing spaces */
        }
        putchar(c);                     /* Otherwise write the character */
        --t;                            /* Count down from tabspan */
    }
}

Solution by Scopych

/* 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? */

#include <stdio.h>
#define MAXLINE 1000    /* maximum input line lenght */
#define SPACES   4    /* how much chars to place */

/* replace tab on 4 spacese */

int main()
{
    char c;
    char wotab[MAXLINE]; /* line without tabs */
    int i, sps;

    i = 0;
    while((c = getchar()) != EOF){
            if(i == (MAXLINE - 2)){    /* protects against overflow */
                    wotab[i] = '\n';
                    ++i;
                    wotab[i] = '\0';
                    printf("%s\n", wotab);
                    printf("LINE IS GREATER THAN 1000 CHAR\n");
            }
            else if(c == '\t'){
                    for(sps = SPACES; sps > 0; --sps){
                            wotab[i] = ' ';
                            ++i;
                    }
            }
            else if(c == '\n'){
                    wotab[i] = '\n';
                    ++i;
                    wotab[i] = '\0';
                    printf("%s", wotab);
                    i = 0;
            }
            else{
                    wotab[i] = c;
                    ++i;
    }}
    return 0;
}



Solution by CakeOFTrust

A simple solution. Does not use symbolic parameters as it would make changing tab / spaces ratio hard if not impossible after compilation.

#include <stdio.h>

int main(void)
{
  int ch, co = 0, TCOL = 8;

  while ((ch = getchar()) != EOF)
  {
      if (ch != '\t')
      {
          if (ch != '\n') {
              ++co;
          }

          else {
              co = 0;
          }

          putchar(ch);
      }

      else
          for (ch = TCOL * (1 + (co / TCOL)) - co, co = 0; ch > 0; --ch)
              putchar(' ');
  }

  return 0;
}

Solution by JohnBretella

A simple solution, with for instead while

#include <stdio.h>

#define CHARS 4

int main()
{
    int i, c;
    while((c=getchar()) != EOF)
    {
        if(c == '\t')
        {
            for(i = 0; i < CHARS; ++i)
                putchar(' ');
        }
        else
        {
            putchar(c);
        }
    }

    return 0;
}

Solution by Luke Panayi

Straight forward solution

#include <stdio.h>
#define tabStop 10 /*Saw no reason for a constant value to be stored as a variable.*/
#define maxInput 1000

int main(){
	int c, i;
	char input[maxInput]; /*Book doesn't specify what it wants you to do with the modified input, so I'm just storing it.*/

	for (i = 0; i<maxInput-1 && (c=getchar()) != EOF; ++i){
		if (c != '\t')
			input[i] = c;
		else{
			for (int j = 0; j <= tabStop; ++j, ++i){
				input[i] = ' ';
			}
			--i; /*Need to deincrement i to compensate for the incremention as the nested for loop returns to the original for loop.*/
		}
	}

	input[i] = '\0';

	return 0;
}

The shortest way I could think to do this:

#include <stdio.h>

#define TABSTOPS 8

int main(void){
        int c;
	//distance from tab stop
	int dts=0;
	while((c=getchar())!=EOF){
		if(c=='\n')
			dts=0;
		else if(c=='\t'){
			while((++dts)%TABSTOPS!=0)putchar(' ');
			c=' ';
		}else
			++dts;
		putchar(c);
	}
	return 0;
}

Solution by Marvelcoder

* detab.c
*
*  Created on: 8-Aug-2020
*      Author: Marvelcoder
*
#include<stdio.h>

#define MAXLINE 1000
#define TABSTOPS 8

int main(){

    char line[MAXLINE];
    char ch;
    int i,linecounter,counter,curenttabstop,nexttabstop,blanks;
    i=linecounter=counter=curenttabstop=nexttabstop=blanks=0;

   while((ch=getchar())!=EOF && i<MAXLINE){
        line[i] = ch;
        ++i;
    }

    for(int j=0;j<i && line[j]!=EOF;j++){
        if(linecounter%TABSTOPS==0){
            curenttabstop=nexttabstop;
            nexttabstop=curenttabstop+TABSTOPS;
        }
        if(line[j]=='\t'){
            if(counter<curenttabstop){
                blanks = curenttabstop - counter;
            }else if(counter>=curenttabstop && counter<nexttabstop){
                blanks = nexttabstop - counter;
            }

            while(blanks>0){
                printf(" ");
                --blanks;
                ++linecounter;
                ++counter;
            }
            blanks=0;
        }else if(line[j]=='\n'){
            printf("%c",line[j]);
            linecounter=curenttabstop=nexttabstop=counter=0;
        }
        else{
            printf("%c",line[j]);
            ++linecounter;
            ++counter;
        }
    }


    return 0;

}

Solution by Foowar

#include <stdio.h>

#define TABSTOP 2

int
main()
{
	int c;
	int col;

	/* the value of `col` is the column that we will print to next time
	 * so (col-1) is the column we just printed to
	 * counting columns start from 1 */
	col = 1;
	while ((c = getchar()) != EOF) {
		if (c == '\n') {
			putchar(c);
			col = 1;
			continue;
		}

		if (c != '\t') {
			putchar(c);
			++col;
			continue;
		}

		/* c == '\t' */
		do {
			putchar(' ');
			++col;
		} while ((col-1) % TABSTOP != 0);
	}

	return 0;
}
Personal tools