tags:

views:

671

answers:

5

Is this a safe way to trim the newline character off of a line after reading it in from a file?

while ( fgets(buffer, 1024, fp) != NULL )
{
    buffer[strlen(buffer)-1] = '\0';
    fprintf (stderr, "%s\n", buffer);
}

It doesn't give me any seg faults but could it cause problems down the road? Should I do something like this instead?

while ( fgets(buffer, 1024, fp) != NULL )
{
    tmp = (char *) malloc(strlen(buffer));
    strncpy(tmp, buffer, strlen(buffer) - 1);
    tmp[strlen(buffer)-1] = '\0';
    fprintf (stderr, "%s\n", tmp);
}
A: 

There won't be a trailing newline if the line doesn't fit into the buffer, so your approach could delete a valid character. So to be safe, you should:

int len = strlen(buffer);
if (len > 0 && buffer[len-1] == '\n')
    buffer[len-1] = 0;

Of course, that will not work on a Mac or in Windows (which use \r and \r\n respectively).

Aaron Digulla
Mac software nowadays is Unix very few programs use the old \r
Mark
If the OP did `fopen(..., "rt")`, the platform's native newlines should get translated to `'\n'`.
Martin B
The mode "rt" is only available in Python, not in standard C.
Aaron Digulla
Standard C does have "text mode" for files, which translates the native line-ending to `\n`. It's the default, unless you specifically ask `fopen()` for binary mode.
caf
+6  A: 

I see no real problems with your first option -- if you own buffer, you're allowed to modify it. Just a couple of minor issues:

  • fgets may not actually return a newline if the last line of the file does not end with one (edit: or if the line doesn't fit into the buffer, as pointed out by Aaron) -- so you should check that there is really a newline there.

  • Also, I would check that strlen(buffer)>0 before accessing buffer[strlen(buffer)-1].

Note that your second option has a bug -- it allocates one byte too few (you need an extra byte for the null character).

Martin B
A: 

Assuming...
1) that buffer is a 1024 char array and...
2) that the buffer you are reading doesn't have a line that is 1024 characters long and then a \n.

3) that you are in UNIX and \n is the terminating character. \r\n in Windows and \r on Macs. So in other words, your code isn't really portable... But if that's not an issue then no worries.

if 2 is a possibility then you have a few issues...
A) You will be nulling out a letter instead of a \n.
B) The strlen() will most likely not be < 1024 which will lead to modifying memory outside of your buffer. Memory corruption in other words.

The first solution is fine otherwise.

The second solution is unnecessary and you'll find that dynamic memory allocation is extremely slow in comparison.

""The fgets() function shall read bytes from stream into the array pointed to by s, until n-1 bytes are read, or a <newline> is read and transferred to s, or an end-of-file condition is encountered. The string is then terminated with a null byte."" http://www.opengroup.org/onlinepubs/009695399/functions/fgets.html so strlen won't walk off the end of it without encountering nul.
Pete Kirkham
As long as the file is opened in text mode (the default), `\n` will be the line terminator everywhere.
caf
A: 

Depending on what you're reading there, and how processing does continue, you could make sure what you're going to trim off is actually a whitespace using isspace() from <ctype.h>.

DevSolar
Yes, you can check the char at `buffer[strlen(buffer)-1]` to make sure it's a `'\n'`, or even use `isspace()`, before setting it to `'\0'`.
Loadmaster
+1  A: 

Use strchr() to search the buffer for a newline:

if (fgets(buffer, sizeof buffer, stdin))
{
  char *c = strchr(buffer, '\n');
  if (c)
    *c = 0;
  ...
}
John Bode
Since fgets reads until a new line is encountered ( or EOF ), you'd be better off scanning backwards from the end rather than forwards using strchr.
Pete Kirkham
Pete: scanning backwards necessitates scanning forwards first to find the end of the string, so it'll be much the same.
caf