The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 1.21 on page 34
Write a program entab
that replaces strings of blanks with the minimum number of tabs and blanks to achieve the same spacing. Use the same stops as for detab
. When either a tab or a single blank would suffice to reach a tab stop, which should be given preference?
Solution by Rick Dearman Cat 0
/****************************************************** KnR 1-21 -------- Write a program "entab" which replaces strings of blanks with the minimum number of tabs and blanks to achieve the same spacing. Author: Rick Dearman email: rick@ricken.demon.co.uk ******************************************************/ #include <stdio.h> #define MAXLINE 1000 /* max input line size */ #define TAB2SPACE 4 /* 4 spaces to a tab */ char line[MAXLINE]; /*current input line*/ int getline(void); /* taken from the KnR book. */ int main() { int i,t; int spacecount,len; while (( len = getline()) > 0 ) { spacecount = 0; for( i=0; i < len; i++) { if(line[i] == ' ') spacecount++; /* increment counter for each space */ if(line[i] != ' ') spacecount = 0; /* reset counter */ if(spacecount == TAB2SPACE) /* Now we have enough spaces ** to replace them with a tab */ { /* Because we are removing 4 spaces and ** replacing them with 1 tab we move back ** three chars and replace the ' ' with a \t */ i -= 3; /* same as "i = i - 3" */ len -= 3; line[i] = '\t'; /* Now move all the char's to the right into the ** places we have removed. */ for(t=i+1;t<len;t++) line[t]=line[t+3]; /* Now set the counter back to zero and move the ** end of line back 3 spaces */ spacecount = 0; line[len] = '\0'; } } printf("%s", line); } return 0; } /* getline: specialized version */ int getline(void) { int c, i; extern char line[]; for ( i=0;i<MAXLINE-1 && ( c=getchar()) != EOF && c != '\n'; ++i) line[i] = c; if(c == '\n') { line[i] = c; ++i; } line[i] = '\0'; return i; }
Solution by Stefan Farfeleder Cat 1
/* 1-21.c */ #include <stdio.h> #define TABSTOP 4 int main(void) { size_t spaces = 0; int ch; size_t x = 0; /* position in the line */ size_t tabstop = TABSTOP; /* get this from the command-line * if you want to */ while ((ch = getchar()) != EOF) { if (ch == ' ') { spaces++; } else if (spaces == 0) /* no space, just printing */ { putchar(ch); x++; } else if (spaces == 1) /* just one space, never print a tab */ { putchar(' '); putchar(ch); x += 2; spaces = 0; } else { while (x / tabstop != (x + spaces) / tabstop) /* are the spaces reaching behind the next tabstop ? */ { putchar('\t'); x++; spaces--; while (x % tabstop != 0) { x++; spaces--; } } while (spaces > 0) /* the remaining ones are real space */ { putchar(' '); x++; spaces--; } putchar(ch); /* now print the non-space char */ x++; } if (ch == '\n') { x = 0; /* reset line position */ } } return 0; }
/*-------------------------------------------------*/ /* Another solution for review purpose */ #include <stdio.h> #define MAXCHAR 1024 #define TABSPACE 8 int gline_entab(char[],int); int CalcNextTabStop(int,int); int main(void) { int len,p; char s[MAXCHAR]; p = 0; len = 0; while ((len = gline_entab(s,MAXCHAR))>0) { printf("%s",s); /* the following code is only meant for debugging purpose */ while (s[p] != '\0') { if (s[p] == '\t') { putchar('\\'); putchar('t'); } else if (s[p] == '\n') { putchar('\\'); putchar('n'); } else if (s[p] == ' ') putchar('-'); else putchar(s[p]); p++; } putchar('\n'); p = 0; } return 0; } int gline_entab(char s[], int lim) { int i,j,k,c; int fspace,cspace; i=j=k=fspace=cspace=0; while (i<lim-1 && ((c = getchar())!= EOF) && c != '\n') { printf("i = %d\n",i); if (c == ' ') { if (fspace == 0) { //first occurence of ' ' fspace = i; cspace=1; k= CalcNextTabStop(j,TABSPACE); } else { //repeat occurence of ' ' if (j == k) { s[fspace]='\t'; i = fspace; fspace = 0; cspace = 0; } else cspace++; } } else { if (cspace>0) i = fspace; while (cspace>0) { s[i] = ' '; i++; cspace--; } s[i] = c; fspace = 0; } i++; j++; } if (c == '\n') { s[i] = c; i++; } s[i] = '\0'; return i; } int CalcNextTabStop(int p,int sz) { return (((p/sz)+1)*sz)-1; }
Solution by TheSunShadow
Well, another simpler solution similar to that one of exercise 20. Please, review it.
#include <stdio.h> #define TAB 5 // Number of spaces of one tab. main() { int c, i = 0, j; while((c = getchar()) != EOF){ if(c == ' '){ ++i; // This is a counter of white spaces. if((i % TAB) == 0) // Every group of a number of 'TAB' putchar('\t'); // spaces is replaced by a tab. } else{ for(j = 0; j < (i % TAB); ++j) // Every group smaller than putchar(' '); // 'TAB' spaces is untouched. putchar(c); // Well, there exist other characters but spaces. if(i != 0) // Once some text is found, the counter is reset. i = 0; } } }
Solution by Amarendra Godbole
using the IN and OUT macros introduced earlier.
#include <stdio.h> #define TABSTOP 4 #define IN 1 #define OUT 0 int main(void) { int spc, i, c, nspace; int state, ntab; state = OUT; nspace = 0; ntab = 0; spc = 0; while ((c = getchar()) != EOF) { if (c == ' ') { state = IN; nspace++; } else { if (state == IN) { if (nspace >= TABSTOP) { ntab = nspace / TABSTOP; spc = nspace % TABSTOP; for (i = 0; i < ntab; i++) putchar('\t'); for (i = 0; i < spc; i++) putchar(' '); } else { for (i = 0; i < nspace; i++) putchar(' '); } nspace = 0; putchar(c); state = OUT; } else { putchar(c); } } } return 0; }
Solution by Pilcrow
Again, I did not need getline. Hope I got it right. I did, of course, test it.
#include <stdio.h> #define TAB 5 int main(void) { int p = 0; int blanks = 0; char chunk[TAB+2]; int c; while((c=getchar()) != EOF) { chunk[p++] = c; if(c == ' ') ++blanks; else blanks = 0; if(p == TAB || c == '\n') { chunk[p] = '\0'; if(blanks > 1) { p -= blanks; chunk[p] = '\t'; chunk[p+1] = '\0'; } printf("%s", chunk); p = blanks = 0; } } if(p > 0) { chunk[p] = '\0'; printf("%s", chunk); } return 0; }
Solution by FlippySquirrel
Here's one I came up with that I think is short and elegant, thought it might have issues with lines that wrap.
#include <stdio.h> /* Exercise 1-21 */ /* Write a program _entab_ that replaces strings of blanks by the minimum * number of tabs and blanks to achieve the same spacing. Use the same tab * stops as for _detab_. When either a tab or a single blank would suffice * to reach a tab stop, which should be given preference? */ #define TABSTOP 8 /* spaces in a tab stop */ void main() { int c; /* current character */ int intab; /* are we counting spaces */ int space; /* number of spaces */ intab = space = 0; /* set initial values */ while((c = getchar()) != EOF) { // if c is a space, start counting for tab stops if(c == ' ') { if(intab = 1) { ++space; // we hit the magic number for a tab // at the *next* character if (space == (TABSTOP - 1)) { putchar('\t'); space = 0; } } else { intab = 1; ++space; } } //if we hit a tab, reset the tab stops else if (c == '\t') { // are there spaces to resolve? if (space > 0) while(space > 0) { putchar(' '); --space; } space = 0; putchar(c); } else { //hit the end of a line if(c == '\n' || c == '\0') { space = 0; } // are there spaces to resolve? else if (space > 0) while(space > 0) { putchar(' '); --space; } //a non space, non tab, non end of line character putchar(c); } } }
Solution by unknown
I use the spaceCount variable to count the number of space, if space number is 4, just print the tab, else we print the space
/** Name: entab Details: replaces strings of blanks by the minimum number of tabs and blanks to achieve the same spacing */ #include <stdio.h> #define TABSIZE 4 int main() { int c = 0; int i = 0; int spaceCount = 0; while ( (c = getchar()) != EOF ) { if (c == ' ') { ++spaceCount; if (spaceCount == TABSIZE) { spaceCount = 0; putchar('\t'); } } else { for (i = 0; i < spaceCount; ++i) { putchar(' '); } spaceCount = 0; putchar(c); } } return 0; }
Solution by CakeOFTrust
#include <stdio.h> #define HOM2ADD TCOL * (1 + (co[0] / TCOL)) - co[0] // HELLO /* " ' int main(void) { int ch, co[2], TCOL = 8; co[0] = co[1] = 0; while ((ch = getchar()) != EOF) { if (ch != ' ') { if (co[1] > 0 && TCOL > 0) { if (co[1] >= HOM2ADD) { putchar('\t'); co[1] = co[1] - HOM2ADD; co[0] = 0; /* The same as co[0]' " = co[0] + HOM2ADD; */ } /* in the sense of remainder. (co[0] mod TCOL) */ for (; co[1] - TCOL >= 0;) { /* Does not change the co[0] mod TCOL */ putchar('\t'); co[1] = co[1] - TCOL; } if (co[1] < 0) co[1] = 0; if (ch != '\t') for (; co[1] > 0; --co[1]) { putchar(' '); ++co[0]; } else co[1] = 0; } if (ch == '\t' || ch == '\n') co[0] = 0; else ++co[0]; putchar(ch); } else ++co[1]; } /* Check if there are any blanks left before EOF */ if (co[1] > 0 && TCOL > 0) { if (co[1] >= HOM2ADD) { putchar('\t'); co[1] = co[1] - HOM2ADD; } for (; co[1] - TCOL >= 0;) { putchar('\t'); co[1] = co[1] - TCOL; } for (; co[1] > 0; --co[1]) { putchar(' '); } } return 0; }
Solution by Luke Panayi
Admittedly not a simple solution, but a working one none the less. I think it's lack of brevity stems from storing the characters to an array to be printed at the end rather than simply putting them to the console one at a time.
#include <stdio.h> #define tabStop 4 #define maxInput 10000 int main(){ int i, c, b, n, t, index, nonBlank; char input[maxInput]; for (i = 0; i<maxInput-1 && (c=getchar()) != EOF; ++i){ if (c != ' ') input[i] = c; else { input[i] = c; /*add the first blank to the array*/ index = i; /*stores location in character array at start of blanks*/ for (b = 1; (c=getchar()) == ' '; ++b, ++i); /*counts the number of blanks (b) until the next non-blank character. b starts at 1 as blank has already been seen*/ nonBlank = c; /*saves the first seen non-blank character to be re-added later (as getchar() will have taken it in from input and will now stay at that point of the input)*/ if (b >= tabStop){ /*if the tab space fits inside the number of blanks, otherwise there is nothing to be done*/ for (n = 0, t = 0; (t+tabStop) <= b; ++n) /*counts the culmnative total of tab 'spaces' (t) and the number of tab stops (n) that fit in number of blanks*/ t += tabStop; i = index; for (int x = 0; x < n; ++x, ++i) /*loops over the number of tabs to be added starting from the first blank in the array found up until the first non-blank*/ input[i] = '\t'; while (t < b){ /*if true, there are remaining blanks not filled in by tabs*/ input[i] = ' '; ++t; ++i; } } input[i] = nonBlank; /*puts the first seen non-blank character into place, as getchar() has already covered it and so it wouldn't otherwise be added like other non-blanks*/ } } input[i] = '\0'; printf("%s", input); return 0; }
Solution by Revc
First we have to figure out what the functionality of the TAB is, my interpretation of TAB is that the cursor moves to the next position where the number of column is a multiple of some integer when we press TAB key. To minimize the number of tabs and blanks, we need replace blanks with tabs whenever possible. Our strategy here is that we print a tab to substitute for a chain of blanks if which are continuous blanks, the number of blanks is less than TABSIZE and the column number of the last space is some multiple of TABSIZE minus 1.
int main() { int offset = 0; int ch = 0; int padding = 0; int blank_counter = 0; while ((ch = getchar()) != EOF) { if (ch == '\n') { print_blanks(blank_counter); blank_counter = 0; offset = 0; putchar(ch); } else if (ch == ' ') { if (blank_counter == 0) { padding = TABSIZE - (offset % TABSIZE); } else { /**/ } offset += 1; blank_counter += 1; if (blank_counter == padding) { putchar('\t'); blank_counter = 0; } else {/**/} } else if (ch == '\t') { print_blanks(blank_counter); blank_counter = 0; offset += TABSIZE - (offset % TABSIZE); putchar(ch); } else { print_blanks(blank_counter); blank_counter = 0; offset += 1; putchar(ch); } } } void print_blanks(int n) { for (int i = 0; i < n; i++) { putchar(' '); } }
Solution by Marvelcoder
* entab.c * * Created on: 10-Aug-2020 * Author: Marvelcoder * *
#include<stdio.h> #define MAXLINE 1000 #define TABSTOPS 8 int main(){ char line[MAXLINE]; char ch; int i,counter,curenttabstop,nexttabstop,blankcounter,tabs; i=counter=curenttabstop=nexttabstop=blankcounter=tabs=0; while((ch=getchar())!=EOF && i<MAXLINE){ line[i] = ch; ++i; } for(int j=0;j<i && line[j]!=EOF;j++){ if(counter%TABSTOPS==0){ curenttabstop=nexttabstop; nexttabstop=curenttabstop+TABSTOPS; } if(line[j]==' '){ ++blankcounter; ++counter; if(counter==nexttabstop){ printf("\t"); blankcounter=0; } }else if(line[j]=='\n'){ printf("%c",line[j]); counter=blankcounter=curenttabstop=nexttabstop=0; } else{ while(blankcounter>0){ printf(" "); --blankcounter; } printf("%c",line[j]); ++counter; } } return 0; }
Solution by Foowar
/* exercis 1-21 * * Write a program entab that replaces strings of blanks by the minimum number * of tabs and blanks to archieve the same spacing. Use the same tab stops as * for detab. When either a tab or a single blank would suffice to reach a * tab stop, which should be given preference? */ /* * blanks should be given preference when a either a tab or a single blank would * suffice to reach a tab stop. * let's assume we set tabstop to 4 and we want to write this phrase * "to be" but I make a typo and write "too be" * my intention here is to insert a single space between "to" and "be" just * to seperate the two words. * * if we insert a tab we would get this "too\tbe" and it would be displayed * like "too be", but when I correct my typo it would display like "to be" * and we don't want that. */ #include <stdio.h> #define TABSTOP 4 #define IN_BLANK 0 #define OUT_BLANK 1 int main() { int c; int col; int nblank; int state; state = OUT_BLANK; col = 0; nblank = 0; while ((c = getchar()) != EOF) { if (state == OUT_BLANK && c == ' ') { state = IN_BLANK; } else if (state == IN_BLANK && c != ' ') { state = OUT_BLANK; if ((col+1) % TABSTOP == 0) { /* here a single blank would suffice to reach the tab stop */ putchar(' '); ++col; --nblank; } while ((col % TABSTOP) + nblank >= TABSTOP) { int disp = TABSTOP - (col % TABSTOP); col += disp; nblank -= disp; putchar('\t'); } while (nblank > 0) { putchar(' '); --nblank; } } if (state == OUT_BLANK) { putchar(c); ++col; if (c == '\n') { col = 0; } if (c == '\t') { while (col % TABSTOP != 0) { ++col; } } } else { /* state == IN_BLANK */ ++nblank; } } return 0; }