tags:

views:

209

answers:

4

This is not homework. I'm just trying to learn :)

I'm on to K&R's Exercise 1-18

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

This is what I've came up with so far

#include <stdio.h>

#define MAXLINE 1000

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

int main () {

    int len;
    char line[MAXLINE];

    while (getline(line, MAXLINE) > 0) {
            printf("%s", line);
    }
    return 0;
}


int getline(char s[], int lim) {
    int c, i, lastNonBlankIndex;
    lastNonBlankIndex = 0;

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

        if (c != ' ' && c != '\t') {
            lastNonBlankIndex = i + 1;

        } 

        s[i] = c;
    }

    if (i != lastNonBlankIndex) {
        i = lastNonBlankIndex;
        c = '\n';
    }

    if (c == '\n') {
        s[i] = c;   
        ++i;
    }
    s[i] = '\0';
    return i;
}

The second part sounded hard, as I wasn't sure what I should return if the line only has blanks or tabs. After all, if I return 0, it will halt the getline() calling. Would this be where I should set up a #define, such as ALL_BLANKS.

Anyway, to actual main question, is this a correct way to remove trailing blanks and tabs from lines? I ran a few inputs through, and it seemed to work. However, if I copy and paste text with newlines into the CL, it appears all strung together. And when I type a line into the CL and push enter, it automatically prints it. Should I be building an array of lines, and then looping through and printing them when done ?

+5  A: 

Your code looks correct, but I think it would be better if you separate the operations of reading a line from stdin and stripping the line of trailing whitespace (decoupling). Then you can use the unmodified getline from the book (code reuse) and won't have the problem of halting on returning 0.

And if you are interested in other solutions, the CLC-wiki has an almost complete list of K&R2 solutions.

#include <stdio.h>
#define MAXLINE 1024

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

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

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

This is the category 1 solution I wrote some time ago. getline is as on page 28 of the book. It might be nicer to put the removal of whitespace in a separate function rstrip, but I leave this as an exercise for the reader.

schot
Thanks for the link!
alex
@alex Just don't cheat ;) BTW, I have an old solution of myself I could add to my answer, but only if you think its useful.
schot
@schot Don't worry, I'm trying to learn for *myself*, so looking at an answer and copying it won't help me! Feel free to post your solution, thanks. I'm also trying the [next exercise](http://stackoverflow.com/questions/3468568/help-with-reversing-a-string-in-c) if you'd like to take a look!
alex
A: 

Your basic design is sound. It is better, as you did, to print a stripped line as soon as you've built it, so that your program only needs to keep one line at a time in memory and not the whole file.

There is a small problem with your code: it doesn't implement the second part of the question (“delete entirely blank line”). That's because you always tack a '\n' at the end of the string. This is easy to fix, but remember that you must return a nonzero value to your caller since a blank line doesn't indicate the end of the file.

Gilles
A: 
#include <stdio.h>

#define MAXLINE 1000

size_t getline(char *s,size_t lim)
{
  if( fgets(s,lim,stdin) )
  {
    while( *s && strchr(" \t\n",s[strlen(s)-1]) )
      s[strlen(s)-1]=0;
    return strlen(s);
  }
  return 0;
}

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

    while (getline(line,sizeof line)) {
            printf("%s", line);
    }
    return 0;
}
I haven't covered a lot of that yet! I'm sure I'll understand it one day soon...
alex
schot
A: 

getline should return -1 (a negative value in general) if there is an error or if EOF is reached. Then your loop conditional can check that it returns something >= 0 and still allow for 0 length lines.

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

I almost never include an assignment within a loop conditional. I would rather add 10 lines of code to get around doing that because it's difficult to read. I would especially refrain from using them with complicated conditionals.

int i = 0;

while (i < lim) {
   c = getchar();
   if (c == EOF || c == '\n') {
       break;
   }
   line[i] = (char)c;
   i++;
}
line[i] = '\0'; // Null terminate the string

This code should read in a line for you. I would separate the reading in of the line from the removal of the trailing white space. You could very easily work backwards from the end of the string to remove white spaces at the location where I null terminated the line, since after having read in the line you now know its length. Essentially you grow the string and then you prune it back down after it has finished growing.

nategoose
alex