本章例程
15.1打开和关闭文件
#include <stdlib.h> #include <stdio.h> int main(int ac, char **av) { int exit_status = EXIT_SUCCESS; FILE *input; while(*++av != NULL){ input = fopen(*av, "r"); if(input == NULL){ perror(*av); exit_status = EXIT_FAILURE; continue; } if(fclose(input) != 0){ perror("fclose"); exit(EXIT_FAILURE); } } return exit_status; }
15.2把字符转换为整数
#include <stdio.h> #include <ctype.h> int read_int(){ int value; int ch; value = 0; while((ch = getchar()) != EOF && isdigit(ch)){ value *= 10; value += ch - '0'; } ungetc(ch, stdin); return value; }
15.3从一个文件向另一个文件复制文本行
#include <stdio.h> #define MAX_LINE_LENGTH 1024 void copylines(FILE *input, FILE *output) { char buffer[MAX_LINE_LENGTH]; while(fgetc(buffer, MAX_LINE_LENGTH, input) != NULL) fputs(buffer, output); }
15.4用sscanf处理行定向输入
#include <stdio.h> #define BUFFER_SIZE 100 void function(FILE *input) { int a, b, c, d, e; char buffer[BUFFER_SIZE]; while(fgets(buffer, BUFFER_SIZE, input) != NULL){ if(sscanf(buffer,"%d %d %d %d %d", &a, &b, &c, &d, &e) != 4){ printf(stderr,"Bad input!%s",buffer); continue; } //handle input... } }
15.5用sscanf处理可变格式的输入
#include <stdio.h> #include <stdlib.h> #define DEFAULT_A 1 #define DEFAULT_B 2 void function(char *buffer) { int a, b, c; if(sscanf(buffer,"%d %d %d", &a, &b, &c) != 3){ a = DEFAULT_A; if(sscanf(buffer,"%d %d", &b, &c) != 2){ b = DEFAULT_B; if(sscanf(buffer, "%d", &c) != 1){ printf(stderr,"bad input:%s",buffer); exit(EXIT_FAILURE); } } } //handle a,b,c... }
15.6随机文件访问
#include <stdio.h> #include "student_info.h" int read_random_record(FILE *f, size_t rec_number, StudentInfo *buffer) { fseek(f, (long)rec_number * sizeof(StudentInfo), SEEK_SET); return fread(buffer, sizeof(StudentInfo), 1, f); }
本章问题
1.如果对fopen函数的返回值不进行检查可能会出现什么后果?
answer: 如果由于任何原因导致打开失败,函数的返回值将是NULL,当这个值传递给后续的I/O函数时,该函数就会失败。至于程序是否失败,则取决于编译器,如果程序并不终止,那么I/O操作可能会修改内存中不可预料的位置的内容。
2.如果试图对一个从未打开过的流进行I/O操作会发生什么情况?
answer: 程序将会失败,因为你试图使用的FILE结构并没有被适当的初始化,某个不可预料的内存地址的内容将可能会被修改。
3.如果一个fclose调用失败,但程序并未对它的返回值进行错误检查可能会出现什么后果?
answer: The fact that it failed at all indicates that something is wrong;the most likely possibility is a bug in the program.By not checking,this bug goes undetected and may cause problems later,Also,the FILE structure used for that stream will not be released,There is a limited number of these available,so if this happens very often,the program will run out of them and be unable to open any more files.
(事实上程序将会失败因为有些东西是错误的,最大的可能是在程序中有一个bug,由于没有检查,这个bug没有检测到就可能导致在程序的后面出现问题,FILE这个结构对应的流并没有被释放,而可用的流数量有限,如果这种情况发生的比较多,这个程序将会消耗所有的流而导致不能打开更多的文件)
4.如果一个程序在执行时它的标准输入已重定向到一个文件,程序如何检测到这个情况?
answer: 不同的操作系统提供不同的机制来检测这种重定向,但程序通常并不需要知道输入来自文件还是键盘。操作系统负责处理绝大多数人与设备无关的输入操作的许多方面,剩余部分则由库函数负责。对于绝大多数应用程序,程序从标准输入读取的方式相同,不管输入实际来自何处。
5.如果调用fgets函数时使用一个长度为1的缓冲区会发生什么呢?长度为2呢?
answer:Space is always left for the NUL type,so with a buffer size of one there is no room for any characters from the stream,with a buffer size of two,character are read one by one.
(最左边总是会留空白给NUL字节,所以缓冲区的长度为1会没有内存存储流中的任何字符,如果长度为2,则会一个一个的读取字符)
6.为了保证下面这条sprintf语句所产生的字符串不溢出,缓冲区至少有多大?
假定你的机器上整数的长度为2个字节。
sprintf(buffer, "%d %c %x", a, b, c);
answer: The first value can take up to six characters,the second at most one,and the third at most four,counting the two spaces and the terminating NUL byte,the buffer must be at least 14 bytes long.
(第一个值最多占6位,第二个值占一位,第三个值占4位,两个空格加上末尾的NUL字节,buffer至少有14个字节长)
7.为了保证下面这条sprintf语句所产生的字符串不溢出,缓冲区至少有多大?
sprintf(buffer, "%s", a);
answer: This is quite unsafe as there is no way to tell how large the buffer must be;strings may be any length,So if the length of a is not checked prior to this statement,the buffer might be overrun no matter how large it is.
(这是非常不安全的因为没有办法知道buffer最大的长度是多少,字符串的长度可能是任何值,所以如果事先没有检查它的长度,无论buffer有多大都有可能溢出)
8.%f格式代码所打印的最后一位数字是经过四舍五入呢?还是未打印的数字被简单的裁掉?
answer:It rounds, If 3.14159 is printed with a code of %.3f the result is 3.142.
(四舍五入,如果3.14159用%3.f格式代码打印出来,结果将会是3.142)
9.你如何得到perror函数可能打印的所有错误信息列表?
answer:Write a program to store all possible integer values in erron and then call perror.you must watch the output,as garbage may be produced for values that are not legitimate error codes.
(写一个程序存储所有的erron可能出现的整型值之后调用perror,你必须要注意输出,如果没有合理的错误代码可能有垃圾值产生)
10.为什么fprintf、fscanf、fputs和fclose函数都接受一个指向FILE结构的指针作为参数而不是FILE结构本身?
answer:Becuase they changed the state of the stream,The call by value semantics of C would not allow the caller's stream variable to be changed if a copy if it were passed as the argument.
(因为它们改变了流的状态,在C中不允许通过改变一个复制的参数的值来改变流变量)
11.你希望打开一个文件进行写入,假定1)你不希望原先的文件内容丢失2)你希望能够写入到文件的任何位置,那么你该怎样设置打开模式呢?
answer:the mode r+ does the job,The w modes truncate the file,and the a modes restrict writing to the end of the file.
(r+ 这个模式可以做这个工作,w模式会截断文件,a模式会限制只能在文件末尾添加)
12.为什么需要freopen函数?
answer:It allows a particular stream to be reopened to a new file,For example,in order for a program that uses printf to begin writing to a different file,the program would have to reopen stdout,this function is the reliable way to do this.
(允许一个特殊的流被一个文件重新打开,例如,一个程序为了让输出写入到一个不同的文件中,这个程序需要重新打开标准输出流,这个函数可以办到)
13.对于绝大多数程序,你觉得有必要考虑fgetc或getchar哪个更好吗?
answer:It is not worth it, Only if a program is not fast enougn or not small enougn should you spend time thinking about things like this.
(不值得,除非一个程序不够快或者不够小到需要你花时间来考虑这个问题)
14.在你的系统上,下面的语句将打印什么内容?
printf("%d ", 3.14);
answer: The result depend on the system, but it wou't be 3.
(取决于你的编译器,但绝对不会是3)
我的想法是3.14是一个字面值常量,它可能是某个值肯定不等于3,读取的时候按照整形值读取,如果首先声明一个int变量比如int a = 3.14,然后用printf打印出来就是3没问题了。
15.请解释使用%-6.10s格式代码将打印出什么形式字符串。
The strings will be left justified.At least six characters,but no more than ten,will be printed.
(打印的字符串是左对齐,至少有6个字符但不会超过10个)
16.当一个特定的值用格式码%3.f打印时,其结果是1.405,但这个值用格式吗%.2f打印时,其结果是1.40,似乎出现了明显错误,请解释其原因。
answer:如果一个值是1.4049,格式代码%3.f打印将导致四舍五入后结果为1.405,不过当使用%.2f时,没有进行四舍五入,因为它在4的时候就被截取了。
本章练习
1.编写一个程序,把标准输入的字符逐个复制到标准输出。
answer:
#include <stdio.h> int main() { int ch; while((ch = getchar()) != EOF) putchar(ch); return 0; }
2.修改你对练习1的解决方案,使它每次读写一整行。你可以假定文件中每一行所包含的字符数不超过80个(不包括结尾的换行符)
answer:
#include <stdio.h> #define BUFFERSIZE 81 int main() { char buffer[BUFFERSIZE]; while(gets() != NULL) puts(buffer); return 0; }
3.修改你对练习2的解决方案,去除每行80个字符的限制,处理这个文件时,你仍应该每次处理一行,但对于那些长于80个字符的行,你可以每次处理其中的一段。
answer:
#include <stdio.h> #define BUFSIZE 256 main() { char buf[BUFSIZE]; while(fgets(buf, BUFSIZE, stdin) != NULL) fputs(buf, stdout); return 0; }
4.修改你对练习2的解决方案,提示用户输入两个文件名,并从标准输入读取它们。第一个作为输入文件,第二个作为输出文件,这个修改后的程序应该打开这两个文件并把输入文件的内容按照前面的方式复制到输出文件。
answer:
#include <stdio.h> #include <stdlib.h> #define BUFFERSIZE 256 FILE *Openfile(char *prompt, char *mode) { char buffer[BUFFERSIZE]; FILE *file; printf("%s filename?",prompt); if(gets(buffer) == NULL){ fprintf(stderr, "Missing %s file name. ", prompt); exit(EXIT_FAILURE); } if((file = fopen(buffer, mode)) == NULL){ perror(buffer); exit(EXIT_FAILURE); } return file; } int main() { char buffer[BUFFERSIZE]; FILE *input; FILE *output; input = Openfile("Input", "r"); output = Openfile("Output", "w"); while(fgets(buffer, BUFFERSIZE, input) != NULL) fputs(buffer, output); fclose(input); fclose(output); return 0; }
5.修改你对练习4的解决方案,使它寻找那些以一个整数开始的行,这些整数值应该进行求和,其结果应该写入到输出文件的末尾,除了这个修改之外,这个修改后的程序其他部分应该和练习4一样。
answer:
//只需要更改一部分 int value, total = 0; while(fgets(buffer, BUFFERSIZE, input) != NULL){ if(sscanf(buffer, "%d", &value) == 1) total += value; fputs(buffer, ouput); } fprintf(output, "%d ",total;
6.在第九章中,你编写了一个名为palindrome的函数,用于判断一个字符串是否回文,在这个练习中,你需要编写一个函数,判断一个整形变量的值是不是回文。例如245不是回文,但14741是回文,这个函数的原型应该如下:
int numeric_palindrome(int value);
如果value是回文返回真,否则返回假。
answer:
int numeric_palindrome(int value) { char buffer[50]; sprintf(buffer, "%d", value); return palindrome(buffer); }
7.某个数据文件包含了家庭成员的年龄,一个家庭各个成员的年龄都位于同一行,由空格分隔,例如:
45 42 22
36 35 7 3 1
22 20
描述了三个家庭的所有成员的年龄,它们分别有3个、5个和2个成员。编写一个程序,计算用这种文件表示的每个家庭所有成员的平均年龄,程序应该用格式代码%5.2f打印出平均年龄,后面是一个冒号和输入数据,你可以假定每个家庭的成员数量不超过10个。
answer:
#include <stdio.h> #include <stdlib.h> #define BUFFER_SIZE 512 int main() { char buffer[BUFFER_SIZE]; while(fgets(buffer, BUFFER_SIZE, stdin) != NULL){ int age[10]; int members; int sum; int i; members = sscanf(buffer, "%d %d %d %d %d %d %d %d %d %d", age, age+1, age+2, age+3, age+4, age+5, age+6, age+7, age+8, age+9); if(members == 0) continue; sum = 0; for(i = 0; i < members; i++) sum += age[i]; printf("%5.2f: %s",(double)sum/members, buffer); } return 0; }
8.编写一个程序,产生一个文件的十六进制倾印码,它应该从命令行接受单个参数,也就是需要进行倾印的文件名,如果命令行中未给出参数,程序就打印标准输入的倾印码。倾印码的每行都应该具有下面的格式:
所有的十六进制数应该使用大写的A-F而不是小写的a-f。
下面是一些样例,用于说明这种格式
(关于倾印码的有关知识,在网上可能找不到,不过,如果你熟悉汇编语言的话,那就应该见过,当你在debug里面查询磁盘中的某个地方存储的内容时应该就会看到所谓的倾印码形式,前面是偏移量,然后后面的32位十六进制表示每一段偏移量中存储的内容,再后面就是它的字符表示,所以你实际可以把倾印码看成这种意思,最开始的六个字节表示磁盘的某个存储位置,比如000200的话就表示距离一个磁盘起始位置200(注意是十六进制)的地方,存储了十六个字节的内容,它们用十六进制存储,而最后的那些字符显示则是打印出那些十六进制存储的实际内容(它们的字符代表),并且可以发现000210 - 000200 刚好是16个字节)
answer:
#include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <memory.h> #define BUFFER_SIZE 64 void dump(FILE *stream) { long offset; unsigned char data[16]; int len; char buffer[BUFFER_SIZE]; memset(buffer, ' ', BUFFER_SIZE - 1); buffer[45] = '*'; buffer[62] = '*'; buffer[BUFFER_SIZE - 1] = '