On 02/15/2018 01:55 PM, jak wrote:
> Il 15/02/2018 18:34, James Kuyper ha scritto:
...
Part of the problem is that you probably don't have access to any system
where line endings are indicated in a manner that would cause serious
problems for fgets(). Neither do I, so I can't demonstrate the problem
directly, but I can tell you what I would expect your program to do on
systems using some of the more popular alternatives:
> #include <stdio.h>
> int main(void)
> {
> char str[3];
This array is uninitialized.
> // Write a new line in text mode.
> FILE *file = fopen("line_ending.txt", "wb");
> putc('\n', file);
> fclose(file);
>
> // Read and hexdump the file in binary mode.
> file = fopen("line_ending.txt", "rb");
> fgets(str, 3, file);
> fputs("Line ending:", stdout);
> for(int c = 0; c < 3; c++)
> printf(" %#x", str[c]);
Unless your code filled up the entire array (which is possible), you're
printing at least one uninitialized value. For character types, that's
safe, but in general that's something to be avoided. I'll ignore the
uninitialized values in my discussion below.
> putchar('\n');
>
> return 0;
> }
Let's consider what your code would do in systems with a variety of
different ways of representing the end of a line.
1. End of line is indicated by a newline character
Your program would cause str[0] to end up containing '\n', while str[1]
would contain a terminating '\0'.
2. End of line is indicated by "\n\r".
In text mode, fgets() would read both the '\n' and the '\r', but would
convert them into a single '\n' before storing it in str[0], the same as
in case 1.
However, since you're using binary mode, fgets would stop after reading
in '\n', storing it in str[0], leaving the '\r' unread, and would place
a terminating '\0' in str[1], exactly the same as in text mode.
Therefore, it would NOT have read the entire line.
3. End of line is indicated by "\r\n".
In text mode, fgets would read both the '\r' and the '\n', but would
convert them into a single '\n' before storing it in str[0].
However, in binary mode, fgets() would simply read in the '\r' directly
into str[0], then reading the '\n' into str[1], at which point it would
stop and place a terminating '\0' in str[2]. In this case, it would read
the entire line.
4. End of line is indicated by '\r'.
In text mode, fgets() would read in the '\r' and convert it to '\n'
before storing it in str[0].
However, in binary mode, it would read the '\r' and then reach the end
of the file without having read in a newline character, so the contents
of str[] would be unchanged, and it would return a null pointer,
indicating failure. Since your code doesn't bother checking whether
fgets() failed, it wouldn't notice. To be fair, I suppressed all error
checking in my code, too - but my code wouldn't result in this error
condition.
5. Text files are stored in fix sized blocks of length 256 bytes, with
the first byte of each block indicating how many bytes of that block are
used. The blocks are padded with blanks. The result of writing a '\n'
character to a file is a block containing a use-count of 0 followed by
254 blank characters.
In text mode, fgets() would read in the entire block, notice that the
use count is 0, and write only a single '\n' to str[0], and a
terminating '\0' to str[1].
However, in binary mode, it would read the use count of 0 into str[0],
and the first padding blank character into str[1]. It would NEVER read
in a new-line character from a text file. It would, however, reach the
limit of 3 characters that you gave fgets(), so it would insert a
terminating '\0' in str[2], without having read in the entire line.
Exercise for the student: If text files are stored in fix-sized blocks
of length 256, with the end of a line indicated by padding the rest of
the block with null characters, what would jak's program do?