I was working exercise3.6 of APUE2e and the code was simple. But when I tried to use fgets and fputs to print out the file content, I got problems. Then I tried to figure out the reason. It turned out to be the null or ‘’ byte of C string, which prevent fgets and fputs from behaving as I expected (fgets appends null byte to the end of input buffer, while fputs prints null-terminated string). I solved the problem by preventing write function to write the null or ‘‘ byte for a C string to the file. This was done by restricting the number of bytes to be writen (using sizeof(buffer)-1, rather than sizeof(buffer)). These findings reminded me of my careless reading, because the problem was caused by a really small mistake. Now I learned the lesson. I prefer reading to writing codes. But through writing and testing, I learn more than just reading. I think I must do more coding and don’t get satisfied just by reading the books and codes.
Attaching is the code I wrote for exercise3.6.
/*
* exercise3-6.c
*
* Created on: Sep 10, 2011
* Author: zhuhuang
*/
#include <apueerr.h>
#include <fcntl.h>
int main(void)
{
int filedes;
FILE *fileptr;
int writenum;
int readnum;
off_t seekpos;
int character;
char buffer0[] = "test";
printf("Size of buffer0: %dn", sizeof(buffer0)); //sizeof includes the ''
char buffer1[] = "The first sentence of file3-6 (Using the write function to write).n"; //an '' in the end
char buffer2[20];
char buffer3[] = "The second sentence of file3-6 (Using the write function to write).n"; //an '' in the end
char buffer4[69];
if((filedes = open("file3-6", O_CREAT | O_RDWR | O_APPEND | O_TRUNC, S_IRWXU | S_IRGRP | S_IROTH)) == -1)
err_sys("open error");
if((writenum = write(filedes, buffer1, sizeof(buffer1)-1)) == -1) //Not "sizeof(buffer1)", otherwise it will cause fputs to behave weirdly.
err_sys("write error 1");
printf("Number of characters of first write: %dn", writenum);
if((seekpos = lseek(filedes, 0, SEEK_CUR)) == -1)
err_sys("lseek error 1");
printf("Current file offset after the first write: %dn", (int)seekpos);
if((seekpos = lseek(filedes, 10, SEEK_SET)) == -1)
err_sys("lseek error 2");
printf("lseek the current file offset to: %dn", (int)seekpos);
if((readnum = read(filedes, buffer2, 20)) == -1)
err_sys("read error");
printf("The string that has been read: %sn", buffer2);
if((seekpos = lseek(filedes, 0, SEEK_CUR)) == -1)
err_sys("lseek error 3");
printf("Current file offset after the first read: %dn", (int)seekpos);
if((writenum = write(filedes, buffer3, sizeof(buffer3)-1)) == -1)
err_sys("write error 2");
printf("Number of characters of second write: %dn", writenum);
if((seekpos = lseek(filedes, 0, SEEK_CUR)) == -1)
err_sys("lseek error 4");
printf("Current file offset after the second write: %dn", (int)seekpos);
if((seekpos = lseek(filedes, 68, SEEK_SET)) == -1)
err_sys("lseek error 2");
printf("lseek the current file offset to: %dn", (int)seekpos);
if((readnum = read(filedes, buffer2, 20)) == -1)
err_sys("read error");
printf("The string that has been read: %sn", buffer2);
if((seekpos = lseek(filedes, 0, SEEK_CUR)) == -1)
err_sys("lseek error 3");
printf("Current file offset after the second read: %dn", (int)seekpos);
//Problem: fail to give the complete content of the file.
//Depending on the size of buffer4, the output can be only contents of buffer1, or contents of buffer1 and part of buffer3.
//No matter how big we have set the size of buffer4 to and whether we use the fflush and fsync function, the problem remains.
//The problem is caused by null or '' byte.
printf("The contents of the file file3-6:n");
if((fileptr = fopen("file3-6", "r")) == NULL)
err_sys("fopen error");
//fgets read at most 40-1 characters, a null or '' byte is inserted to the end.
while(fgets(buffer4, 40, fileptr) != NULL){
//fputs writes the null-terminated string.
//The null or '' causes fputs to behave unexpectedly.
if(fputs(buffer4, stdout) == EOF)
err_sys("fputs error");
//fputs(buffer4, stdout); //Not the flushing problem.
//fflush(stdout);
//fsync(STDOUT_FILENO);
}
if(ferror(fileptr))
err_sys("fgets error");
exit(0);
}