Jump to: navigation, search

The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 1.18 on page 31

Write a program to remove all trailing blanks and tabs from each line of input, and to delete entirely blank lines.



Solution by Vidhan Gupta

/* Write a program to remove trailing blanks and tabs from each
line of input, and to delete entirely blank lines. */

#include <stdio.h>
#define MAXIMUM 1000

int getLine(char s[], int lim);
void removeBlanks(char s[], int len);

int main()
{
    int len;
    char line[MAXIMUM];
    while ((len = getLine(line, MAXIMUM)) > 0)
    {
        removeBlanks(line, len);
        printf("%s", line);
    }

    return 0;
}

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;
}

void removeBlanks(char s[], int len)
{
    for (int i = 0; i <= len; i++)
    {
        if (s[i] == ' ' || s[i] == '\t')
            s[i] = '\b';
        else
            i = len + 1;
    }
}
INPUT:
      What the hell is this?

OUTPUT:
What the hell is this?

Solution by Xggggg

#include <stdio.h>

#define MAXLINE 1000

int getline(char str[], int lim);

int main(void)
{
    int len;
    char line[MAXLINE];
    while ((len = getline(line, MAXLINE)) > 0)
        if (len > 1)
            printf("%s", line);
    return 0;
}

int getline(char str[], int lim)
{
    int c, i, j;
    i = j = 0;
    while ((c = getchar()) != EOF && c != '\n')
    {
        if (j < lim)
            str[j++] = c;
        if (c != ' ' && c != '\t')
            i = j;
    }
    if (c == '\n' && i++ < lim)
        str[i - 1] = '\n';
    str[(i < lim) ? i : lim - 1] = '\0';
    return i;
}

Solution by Ben Pfaff and a modification thereof by Chris Sidi

/* K&R2 1-18 p31: Write a program to remove trailing blanks and tabs
   from each line of input, and to delete entirely blank lines.

   The program specification is ambiguous: does "entirely blank lines"
   mean lines that contain no characters other than newline, or does
   it include lines composed of blanks and tabs followed by newline?
   The latter interpretation is taken here.

   This implementation does not use any features not introduced in the
   first chapter of K&R2.  As a result, it can't use pointers to
   dynamically allocate a buffer to store blanks that it has seen, so
   it must limit the number of blanks that are allowed to occur
   consecutively.  (This is the value of MAXQUEUE, minus one.)

   It is intended that this implementation "degrades gracefully."
   Even though a particular input might have 1000 or more blanks or
   tabs in a row, causing a problem for a single pass, multiple passes
   through the file will correct the problem.  The program signals the
   need for such an additional pass by returning a failure code to the
   operating system.  (EXIT_FAILURE isn't mentioned in the first
   chapter of K&R, but I'm making an exception here.) */

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

#define MAXQUEUE 1001

int advance(int pointer)
{
  if (pointer < MAXQUEUE - 1)
    return pointer + 1;
  else
    return 0;
}

int main(void)
{
  char blank[MAXQUEUE];
  int head, tail;
  int nonspace;
  int retval;
  int c;

  retval = nonspace = head = tail = 0;
  while ((c = getchar()) != EOF) {
    if (c == '\n') {
      head = tail = 0;
      if (nonspace)
        putchar('\n');
      nonspace = 0;
    }
    else if (c == ' ' || c == '\t') {
      if (advance(head) == tail) {
        putchar(blank[tail]);
        tail = advance(tail);
        nonspace = 1;
        retval = EXIT_FAILURE;
      }

      blank[head] = c;
      head = advance(head);
    }
    else {
      while (head != tail) {
        putchar(blank[tail]);
        tail = advance(tail);
      }
      putchar(c);
      nonspace = 1;
    }
  }

  return retval;
}

Solution by Chris Sidi

Chris Sidi writes:

Ben,

I thought your solution to 1-18 was really neat (it didn't occur to me
when I was doing the exercise), the way it degrades gracefully and
multiple passes can get rid of huge blocks of whitespace.

However, if there is a huge block of non-trailing whitespace (eg "A",2000
spaces, "B\n") your program returns an error when there's not a need for
it.  And if someone were to use your program till it passes it will loop
infinitely:

  $ perl -e 'print "A"," "x2000,"B\n";' > in
  $ until ./a.out < in > out; do echo failed, running another pass; cp out
     in; done
  failed, running another pass
  failed, running another pass
  failed, running another pass
  [snip]

Below I have added a variable spaceJustPrinted to your program and check
to see if the spaces printed early are trailing.  I hope you like the
minor improvement.  (Though I can understand if you don't give a [1] :))

[1] expletive deleted - RJH.

/* K&R2 1-18 p31: Write a program to remove trailing blanks and tabs
   from each line of input, and to delete entirely blank lines.

   The program specification is ambiguous: does "entirely blank lines"
   mean lines that contain no characters other than newline, or does
   it include lines composed of blanks and tabs followed by newline?
   The latter interpretation is taken here.

   This implementation does not use any features not introduced in the
   first chapter of K&R2.  As a result, it can't use pointers to
   dynamically allocate a buffer to store blanks that it has seen, so
   it must limit the number of blanks that are allowed to occur
   consecutively.  (This is the value of MAXQUEUE, minus one.)

   It is intended that this implementation "degrades gracefully."
   Even though a particular input might have 1000 or more trailing
   blanks or tabs in a row, causing a problem for a single pass,
   multiple passes through the file will correct the problem.  The
   program signals the need for such an additional pass by returning a
   failure code to the operating system.  (EXIT_FAILURE isn't mentioned
   in the first chapter of K&R, but I'm making an exception here.) */

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

#define MAXQUEUE 1001

int advance(int pointer)
{
  if (pointer < MAXQUEUE - 1)
    return pointer + 1;
  else
    return 0;
}

int main(void)
{
  char blank[MAXQUEUE];
  int head, tail;
  int nonspace;
  int retval;
  int c;
  int spaceJustPrinted; /*boolean: was the last character printed whitespace?*/

  retval = spaceJustPrinted = nonspace = head = tail = 0;

  while ((c = getchar()) != EOF) {
    if (c == '\n') {
      head = tail = 0;
      if (spaceJustPrinted == 1) /*if some trailing whitespace was printed...*/
        retval = EXIT_FAILURE;

      if (nonspace) {
        putchar('\n');
        spaceJustPrinted = 0; /* this instruction isn't really necessary since
                              spaceJustPrinted is only used to determine the
                              return value, but we'll keep this boolean
                              truthful */
        nonspace = 0;  /* moved inside conditional just to save a needless
                       assignment */
      }
    }
    else if (c == ' ' || c == '\t') {
      if (advance(head) == tail) {
        putchar(blank[tail]); /* these whitespace chars being printed early
                              are only a problem if they are trailing,
                              which we'll check when we hit a \n or EOF */
        spaceJustPrinted = 1;
        tail = advance(tail);
        nonspace = 1;
      }

      blank[head] = c;
      head = advance(head);
    }
    else {
      while (head != tail) {
        putchar(blank[tail]);
        tail = advance(tail);
      }
      putchar(c);
      spaceJustPrinted = 0;
      nonspace = 1;
    }
  }

  /* if the last line wasn't ended with a newline before the EOF,
  we'll need to figure out if trailing space was printed here */
  if (spaceJustPrinted == 1) /*if some trailing whitespace was printed...*/ 
    retval = EXIT_FAILURE;

  return retval;
}


Solution by Pilcrow

/***********************************************
The other code is so complicated.  Here is mine
-- Pilcrow
***********************************************/

/*

Perhaps the others are complicated because they are correct, or at least less broken than yours?  (Well, apart from the one from TheSunShadow, perhaps?)

First, this strips all _preceding_ spaces on a line.  Second it searches for and strips (not tested) NULLs.  Third it outputs a NULL after every output line-break.

None are part of the spec.

-- Nick

*/

#include <stdio.h>
#define MAXLINE 10000

char line[MAXLINE+1];
int getline(void);

int main(void)
{
    extern char line[];
    int len, head, tail, inn;
    while((len=getline()) > 0) {
        for(head = 0; line[head] == ' ' ||
            line[head] == '\t'; head++);
        for(tail = len; line[tail] == ' ' ||
            line[tail] == '\t' ||
            line[tail] == '\n' ||
            line[tail] == '\0';tail--);
        if(tail - head >= 0){
            for(inn = head; inn <= tail; inn++)
                putchar(line[inn]);
            putchar('\n');
            putchar('\0');
        }
    }
    return 0;
}

int getline(void)
{
    extern char line[];
    int c, i;
    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 Nngnna

minimal fix of Pilcrow's solution

/***********************************************
The other code is so complicated.  Here is mine
-- Pilcrow
***********************************************/

#include <stdio.h>
#define MAXLINE 10000

char line[MAXLINE+1];
int getline(void);

int main(void)
{
    extern char line[];
    int len, tail, inn;
    while((len=getline()) > 0) {
        for(tail = len-1; line[tail] == ' ' ||
            line[tail] == '\t' ||
            line[tail] == '\n' ;tail--);
        if(tail >= 0){
            for(inn = 0; inn <= tail; inn++)
                putchar(line[inn]);
            putchar('\n');
        }
    }
    return 0;
}

/* no changes to getline() */

Solution by TheSunShadow

/***********************************************
I wrote a version that does not depend of any
maximum length of input lines
-- TheSunShadow
***********************************************/

/*
...and that is fundamentally broken!

Test it with a file of ten spaces followed by a line-break, or with ten (or more) spaces at the end of a line whose length is a multiple of ten not including the line-break.

Kinda obvious, really...

Whoops!

-- Nick

*/

#include <stdio.h> 

/* We are getting pieces of lines of arbitrary length 
without the need of getting the full line at once. */
#define MAXLINE 10

int getline(char s[], int lim);

main(void)
{
	char line[MAXLINE];    				  
	int i, j, k, len, max = 0;
	
	while((len = getline(line, MAXLINE)) > 0){
		
		/* There are two kind of pieces of lines, whether they
		are at the end of the line or not. */
		
		if(line[len - 1] != '\n')		// This is not an end of line,
			printf("%s", line);		// then write the input as is.
		
		else{					// This is an end of line.
			
			j = 0; 	 	 
			
			/* Count the number of spaces or tabs at the end. */
			while((line[len - 2 - j] == ' ') || (line[len - 2 - j] == '\t'))
				++j;
			
			/* Write the input without the unwanted characters. */
			if(j < len - 1)
				for(k = 0; k < (len - 1 - j); ++k)
					printf("%c", line[k]);	
										
			printf("\n");			// This is to recover the right
							// format at the end of line.
		}
	}	
}

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;
		s[++i] = '\0';
    }

    else if(c == EOF && i > 0){
        s[i] = '\n'; 
		s[++i] = '\0';
    }

	return i;
}

Solution by Amarendra Godbole

#include <stdio.h>

#define MAXLINE 1000

/* delete trailing blanks, tabs, and blank lines */

int
getline(char s[], int max);

int
main(void)
{
    int len, i;
    char line[MAXLINE], longest[MAXLINE];

    while ((len = getline(line, MAXLINE)) != 0) {
        if (len > 1) {
            for (i = len-1; (line[i] == ' ' || line[i] == '\t' ||
                line[i] == '\n'); i--)
                ;
            line[++i] = '\n';
            line[++i] = '\0';
            printf("%s", line);
        }
    }

    return 0;
}

int
getline(char s[], int max) {
    int i, c;
    for (i=0; i<max-1 && (c=getchar())!=EOF && c!='\n'; ++i) {
        s[i] = c;
    }
    if (c == '\n') {
        s[i] = c;
        i++;
    }
    s[i] = '\0';
    return i;
}

Solution by CakeOfTrust

The simple yet flawed version. Its major problem so far is that it does not recreate the order in which the blanks and tabs were positioned. Instead it formats EVERYTHING into 'tabs first, blanks second' which might come in handy but more often causes frustration.

#include <stdio.h>

int main(void)
{
  int c[2], blnk[2];

  blnk[1] = blnk[0] = 0;

  c[0] = '\n';

  while ((c[1] = getchar()) != EOF)
  {
      if (c[1] == ' ')
          ++blnk[1];

      else if (c[1] == '\t')
          ++blnk[0];

      else {
          if (c[1] != '\n' && (blnk[0] + blnk[1]) > 0)
          {
              for (; blnk[0] > 0; --blnk[0])
                  putchar('\t');

              for (; blnk[1] > 0; --blnk[1])
                  putchar(' ');

              putchar(c[1]);
          }

          else if (c[1] == '\n' && c[0] == '\n')
              ;

          else
              putchar(c[1]);

          if (blnk[0] > 0)
              blnk[0] = 0;

          if (blnk[1] > 0)
              blnk[1] = 0;

          c[0] = c[1]; /* To delete empty lines after detabbing and blank deletion one must put { after
                          the first 'else' and } after this assignment operator.
                          To restore them one would need to remove {} mentioned before. */
      }
  }

  return 0;
}

This solution takes inspiration from Ben Paff's solution. It is different in the way it deals with array of tabs overflow. It can delete the exactly specified number of whitespaces, not the one less by 1. It is also more lengthy which is not good.

#include <stdio.h>

#define MAXLEN 6
#define YES 1
#define NO 0

int advance(int p);

int advance(int pointer)
{
  if (pointer < MAXLEN - 1)
      return pointer + 1;

  else
      return 0;
}

int main(void)
{
  char ws[MAXLEN];
  int c[2], end, start, overflow = NO;

  c[0] = '\n';

  end = start = -1;

  while ((c[1] = getchar()) != EOF)
  {
      if (c[1] == ' ' || c[1] == '\t')
      {
          if (end < start) {
              putchar(ws[start]);

              start = advance(start);

              ws[end = end + 1] = c[1];
          }

          else {
              if (end < MAXLEN - 1)
                  ++end;

              else {
                  end = 0;
                  putchar(ws[end]);

                  if (overflow == NO)
                      overflow = YES;

                  start = advance(start);

              }


              ws[end] = c[1];

              if (start == -1)
                  ++start;
          }
      }

      else {
          if (c[1] != '\n')
          {
              if (end > start)

                  for (; start <= end; ++start)
                      putchar(ws[start]);

              else if (end < start)
              {
                  for (; start < MAXLEN; ++start)
                      putchar(ws[start]);

                  for (start = end; end > -1; --end)
                      putchar(ws[start - end]);
              }

              else if (end > -1)
                  putchar(ws[end]);

              putchar(c[1]);
          }

          else if (c[1] == '\n' && c[0] == c[1])
          {
              if (overflow == YES)
                  putchar(c[1]);
          }

          else
              putchar(c[1]);

          c[0] = c[1];

          if (start != -1)
              end = start = -1;

          if (overflow == YES)
              overflow = NO;
      }
  }

  return 0;
}

Solution by ifox12

#include <stdio.h>

#define MAXLINE 1024

int main() {
  int c;
  char buf[MAXLINE];
  int count = 0;

  while ((c = getchar()) != EOF) {
    // read chars till newline
    if (c != '\n' && count - 1 < MAXLINE) {
      buf[count++] = c;
    } else {
      // skip trailing whitespace
      while(buf[count - 1] == ' ' || buf[count - 1] == '\t') {
	count--;
      }
      // add nl and \0
      buf[count] = c;
      buf[count + 1] = 0;
      if (count != 0) { // after skipping all other whitespace is there something left besides the nl?
	printf("%s", buf);
	count = 0;
      }
    }
  }
}


Solution by Tobar Gadvesi

#include <stdio.h>
#define MAXLINE 1000

int get_line(char line[], int maxline);

main()
{
        int len;
        char line[MAXLINE];

        while ((len = get_line(line, MAXLINE)) > 0) {
                if (line[len-1] == '\n')
                        --len;
                while (line[len-1] == ' ' || line[len-1] == '\t') {
                        line[len-1] = '\n';
                        line[len] = '\0';
                        --len;
                }
                if (line[0] == '\n')
                        ;
                else
                        printf("%s", line);
        }
        return 0;
}

int get_line(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;
}

Solution by Krasimir Berov

A seemingly correct solution handling all corner cases and lines with unlimited length. Using only what we know from this chapter so far.

#include <stdio.h>
/* Exercise 1-18. Write a program to remove trailing blanks and tabs
 * from each line of input, and to delete entirely blank lines. */
#define MAX 70
int get_line(char line[]);
int main() {
  int len; /* current line length */
  char line[MAX];
  while ((len = get_line(line)) > -1)
    if (len != 0)
      printf("%s", line);
  return 0;
}

/* read a line into s, return length */
int get_line(char s[]) {
  int c, i = 0;
  char previous = 'A'; /* some default value */
  /* copy each character into the corresponding `s`'s slot */
  for (i; i<MAX && ((c = getchar()) != EOF) && (c != '\n'); ++i) {
    /* do not add repeating blanks and tabs */
    if ((previous == ' ' || previous == '\t') && (c == ' ' || c == '\t')) {
      --i;
    } else {
      s[i] = c;
      previous = c;
    }
  }
  /* remove trailing tabs and spaces */
  if (s[i - 1] == '\t' || s[i - 1] == ' ') {
    s[i - 1] = '\n';
    s[i] = '\0';
    return i;
  }
  /* File is exhausted */
  if (c == EOF)
    return -1;
  /* an empty line */
  else if (c == '\n' && i == 0)
    return i;
  else if (c == '\n') {
    s[i] = c;
  }
  /* finish the string - an array of characters in C */
  s[++i] = '\0';
  return i;
}

Example input:

Exercise 1-17. Write a program to print all input lines that are longer than 80 characters.
Exercise 1-18. Write a program to remove trailing blanks and tabs from
each line of input, and to delete entirely blank lines.



iтри празни реда, posledwani ot повтарящи се            празноти i 				  табове
i		            
I            

Exercise 1-19. Write a function reverse (s) that reverses the character string s. Use it to write a program that reverses its input a line at a time.

Example output:

Exercise 1-17. Write a program to print all input lines that are longer than 80 characters.
Exercise 1-18. Write a program to remove trailing blanks and tabs fromeach line of input, and to delete entirely blank lines.
iтри празни реда, posledwani ot повтарящи се празноти i табове
i
I
Exercise 1-19. Write a function reverse (s) that reverses the character string s. Use it to write a program that reverses its input a line at a time.

Solution by vladkir

#include <stdio.h>
#define MAXLEN 1024
int main() {
	int c, len = 0, i;
	char line[MAXLEN];
	while (len < MAXLEN - 1 && (c = getchar()) != EOF) {
		if (c != '\n')
			line[len++] = c;
		else {
			for (i = len - 1; i >= 0 && (line[i] == ' ' || line[i] == '\t'); --i)
				;
			if (i >= 0) {
				line[i + 1] = '\0';
				printf("%s|\n", line);
			}
			len = 0;
		}
	}
}

Solution by Nurmareko

#include <stdio.h>
#define MAXLINE 10000

int getline(char s[], int lim);

main() {
	char line[MAXLINE];
	int i, last_char, len;

	while((len = getline(line, MAXLINE)) > 0) {
		last_char = line[len-2];

		/* is the current line contain only a newline and a null?
		 * if true, then the line is entirely blank, skip it (in other word, delete.) 
		 * note: when the current line is entirely blank it only contain newline character therefore len is 1, however,
		 *       when there is atleast one character, the line will be compose of some character and a newline, therefore len is 2 or greater. */
		if (len == 1)
			;

		/* is the last character of the current line excluding newline and null, not a blank? 
		 * if true, then there is no trailing blank, nothing to remove, print the current line unchanged. */
		else if (last_char != ' ' && last_char != '\t')
			printf("%s", line);

		/* there is trailing blank. */
		else {
			/* get the index of the last non blank character.
			 * note: when the line compose of blanks and tabs, i will be -1 */
			for(i = len-2; line[i] == ' ' || line[i] == '\t'; --i)
				;
			
			/* put null after the last non blank character to mark the end of line without trailling blank. */
			line[++i] = '\0';

			/* is the line index after removing trailling blank greater than 0?
			 * if true, the line atleast have a character to print.
			 * if not, the line is entirely blank, skip it.                     */
			if (i > 0)
				printf("%s\n", line);	
		}
	}
	return 0;
}

/* read a line into s, return length */
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;
}
Personal tools