C输入输出与文件
一、终端I/O
1.单字符I/O:getchar(),putchar()
(1)单字符输入(get character):
【 int getchar();】
- 返回值为输入的字符(ASCII)。可以接受任何字符,包括非打印字符。当一次键入多个字符时按下回车键后getchar开始逐个读取所有字符(包括回车符)。
- 在某些编译环境下,因为scanf()函数不读取回车符且将其留在输入队列中,下次调用scanf()时会由于先读取到回车符而在读取数据前过早结束输入,所以常在scanf后用【getchar();】读取并丢弃这个回车符。
(2)单字符输出(put character):
【int putchar(int);】
- 返回值为输出的字符,参数为要输出的字符(ASCII)。是在stdio.h中定义的含参宏。
2.格式化输入输出
(1)格式化输出(print format):
【 int printf(格式控制,输出表列);】
- 格式控制是一个字符串常量,由普通(转义)字符,转换说明符,转换修饰符组成。普通(转义)字符直接输出(控制光标),转换说明符会按顺序替换为输出表列中的量。常用的格式说明符有:
<1> %d %ld 整型或长整型
<2> %c 字符型,参数可以为对应ASCII码或单引号内的字符
<3> %f %lf 浮点数,double型常用%lf。默认6位小数,自动进行四舍五入
<4> %s 字符串型,不输入输出'\0'
<5> %e 用e计数法(科学计数法)输出浮点数
<6> %x 输出16进制数,%#x输出0x格式
<7> %o 无符号8进制数
<8> 浮点数%m.nf,m是说明总宽度,正数代表左对齐负数代表右对齐。默认用空格补位,如果m的最高位为0则用0补位。
C通过变量类型来将变量值存在堆栈(stack)中,但printf函数在读取时依据转换说明符。如果较长的数据使用较短的说明符只会读取一部分(转换为十进制后可能变成古怪的数字),较短的数据使用较长的说明符会读取下一内存单元中的字符而输出不可预知的结果。
printf函数成功输出会返回输出的字符数(包括打印字符和非打印字符),失败返回一个代表失败类型的负值。
在指定输出位数时可以用*代替数字,但在输出表列的对应位置要有一个对应的int值。
printf("%.*f",2,2.333);
(2)格式输入(scan format)
【int scanf(格式控制,地址表列);】
- 格式控制与printf函数相似,%与格式字符中间的数值代表读取的位数,%.ns中的n可以限定最大输入长度。地址表列代表读取数据储存的地址,可以是&+变量名,指针。当读取字符串时直接写字符数组名不加&,因为它本身就是一个地址。
- scanf()中除s外[…]和[^…]都可以用于字符串输入。[…]只读取[…]中出现的字符,读取到未包含的字符时将停止读取(可以用来让scanf()也读取空格)。[^…]正好相反,只读取其中不包含的字符,读取到其中包含的字符时停止读取。
scanf输入机制
- scanf函数按照格式控制逐一读取输入字符,当发现与格式控制匹配的输入时就将其存储到指定地址中,直到读取到指定字符数或第一个不符合格式控制的字符。但是scanf会将第一个不符合格式控制的字符(包括为确认输入而输入的回车符)留在输入队列(输入缓冲区)中,下次读取将从这个被舍弃的字符开始。
- 在多次调用scanf时要将上次调用时留在输入队列中的回车符取出(用getchar()函数),否则scanf会因为这个回车而认为本次输入了空字符串(回车是确认输入的标志)而结束读取。
scanf函数返回值
- scanf返回它成功读取的项目数,当它未能成功读取时返回0。当读取到文件结尾时(end of file)返回EOF(EOF是在stdio.h中定义的常量,一般为-1)。scanf的返回值可用来输入个数未知的数据或检测处理异常输入。示例:【while (scanf("%d", &a) != EOF)】或 【while (scanf("%d", &a) == 1)】。因为EOF通常定义为-1被认为是真,所以不能采用 while(scanf(…));。
scanf函数输入字符串
- scanf函数读取字符串由两种方法决定结束读取。都是从第一个非空白字符开始,读入指定字符数或读到(但不包括)下一个空白字符时结束。
- *在%和格式字符中间表示滞后赋值,scanf将跳过这些格式说明直接读取后面的值。
3.字符串输入或输出
(1)字符串输入(get string):
【char *gets(char *);】
- gets()函数读取字符串以指针参数值为首地址建立字符数组。 gets()返回的地址与传递给它的是同一个地址,也就是字符数组的首地址。当读取失败或读到文件结尾时将返回一个空指针(NULL)。gets()将读取换行符前所有字符并在最后加一个空字符('\0'),把字符串首地址交给调用它的程序。
与scanf()比较:
- gets()函数将读取换行符并把它丢弃,scanf()不读取换行符,并把它留在输入队列中。gets()回读取空格等字符并存储到字符串中,scanf()遇到空格会停止读取。
- gets()函数不能指定输入字符数,多出的字符将会溢出到相邻内存区。scanf()语句可以指定输入字符数。
(2)字符串输出(put string):
【int puts(const char *str);】
- 向终端输出字符串,并自动输出换行符返回字符数。
二、文件I/O
- 文件:存储在外部介质上的数据集合。 在UNIX(c的发源地)中所有设备都是当做文件统一处理的(一切都是文件)。
- 流:以规定顺序被读取一次的数据序列,可以理解为在输入输出过程中产生的一个数据序列。
- 文件标识:由文件路径,主文件名,文件后缀组成。
- 文件缓冲区:内存中的一片区域,对文件的I/O数据流必须先装满缓冲区再送到程序数据区(程序变量)或外存。
- 文件信息区:是一个FILE类型的结构体变量。FILE类型在stdio.h文件中定义用来存放数据缓冲区位置,缓冲区状态,文件状态等文件信息。
- 文件指针:指向文件信息区的指针。标准文件指针:stdin 从终端(键盘)读取,stdout 向终端(显示器)输出,stderr 向终端(显示器)发送错误信息。它们都是在stdio.h中的定义的FILE指针(FILE *),可以作为标准/O函数的参数。
- 文件指针-->文件信息区-->文件缓冲区<-(操作系统)->文件
1.文件的打开关闭与重定向
- 文件的打开和关闭是与文件关联的文件信息区和数据缓冲区建立与撤销的过程。无说明的函数都在stdio.h头文件中。
(1)打开文件(file open):
【(FILE *) fopen(文件名,模式字符串);】
- 模式字符串:
1)"r" 只读 "w"只写 "a" 追加
2)"b" 二进制文件
3)"+" 可读写
- 在同一个字符串中将三部分按顺序写出。
1)必须写出,当目标文件不存在时,"r","a"出错,"w"将建立一个新文件。
2)可不写,缺省时打开文本文件。
3)可不写,缺省时只读只写。
- 关于r、a、w
1)"r+"在文件头开始读写但不会清除文件内容(类似改写模式)。
2)"a+"为从文件尾进行读写,并自动移动EOF(a模式下不会自动移动EOF,可能会在某些环境下无法读取追加的内容故推荐a+)。
3)"w+"清空文件后重写。
- 函数返回值为指向文件的指针(FILE *),可用于调用文件。当打开失败时返回NULL。
(2)关闭文件(file close):
【int fclose(文件指针);】
- 撤销文件信息区,数据缓冲区,并使文件指针不再指向文件。关闭成功返回0否则返回EOF。
- 应该先把缓冲区中的数据输出到文件,再撤销缓冲区。
关闭全部文件:
【int fcloseall(void);】
(3)重定向(free open??):
【FILE *freopen(const char* new,const char *type, FILE *stream);】
其中:
new:新流的名称
type:打开方式(同fileopen函数)
stream:原有流。
成功返回新流的指针,失败返回NULL。
重定向函数向原有流的读写将被向新流的读写替代,可以在不改变其它代码的情况下使得程序的默认读写对象改变常用于将stdin(stdout, stderr)替换为文件使得scanf或printf等函数实现向文件读写。
若要恢复标准流,可以重新打开标准控制台设备文件,只是这个设备文件的名字是与操作系统相关的。
DOS/Windows:
【freopen("CON", "r", stdin);】
Linux:
【freopen("/dev/console", "r", stdin);】
(4)刷新缓冲区(file flush):
【int fflush(FILE *fp);】
- 将缓冲区所有未写的数据发送到指定文件中,即刷新缓冲区。如果参数为空指针则清空缓冲区。
刷新所有缓冲区:
【int fflushall(void);】
(5)退出(exit):
【void exit(int);】
- 关闭所有打开的文件并结束程序,相当于在最初调用(即由操作系统命令调用的main而非其他函数调用的main)main函数中return语句。包含在stdlib.h头文件中。
2.文件的顺序读写
(1)从文件输入字符(file get character):
【int fgetc(FILE *fp);】
- 从文件中读取一个字符,并返回它的ASCII码。
(2)向文件输出字符(file put character):
【int fputc(int ch,FILE *fp)】
向文件写字符ch。
(3)从文件输入字符串(file get string):
【char *fgets(char *,int,FILE *fp);】
- 从文件中读字符串。参数一为字符串地址;参数2为允许的最大字符数,如果值为n则读取n-1个字符,并自动添加一个空字符。参数3为读取文件指针,从键盘读取数据时可以使用stdin(standard input)作为该参数,这个标识符在stdio.h中定义。返回值与gets()函数相同。fgets()本来是为处理文件I/O而设计也可以用于从终端输入。fgets()读取到换行符会将其存放到字符串中而不丢弃。
(4)向文件输出字符串(file put string):
【int fputs(const *char, FILE *fp)】
- 向文件中写字符串。参数1为要输出的字符串常量,参数2为要写到的文件。进行显示时可以使用stdout(standard output)作为参数,这个标识符在stdio.h中定义。函数返回值为成功输出的字符数。
- 面向文件的字符串输入输出语句不会自动增删换行符。
(5)向文件格式化输出(file print format):
【int fprintf(文件指针,格式字符串,输出表列);】
- 向文件格式化输出。
(6)从文件格式化输入(file scanf format):
【int fscanf(文件指针,格式字符串,地址表列);】
- 这些文件读写函数读到文件结尾时同样返回EOF,当输入个数不确定的数据和处理异常输入时,可以参照scanf函数的用法。
- 用二进制方式向文件读写一段数据:
- 在程序中不仅需要一次输入输出一个数据且常常需要一次输入输出一组数据(如数组或结构体),C允许直接将一段内存复制到文件中或从文件复制到内存。
(7)从文件读取数据块(file read)
【size_t fread(void *buffer,size_t size, size_t count, FILE *fp);】
(8)向文件输出数据块(file write):
【size_t fwrite(const void *buffer,size_t size, size_t count, FILE *fp);】
其中:
buffer:向文件写出(从文件读入)的内存区域首地址。
size:要读写的每个数据项的字节数。
count:要读写的数据项的个数。
fp:文件指针。
在打开文件时以二进制文件打开,就可以读写任何类型的数据。
3.文件的随机读写
- 文件的顺序读写是按照数据在文件中物理位置次序进行读写,先读取前面的数据再读取后面的数据。而文件的随机读写可以访问任意位置的数据,显然这种方法比顺序读写高效得多。(类比数组与链表的读写)
- 为了对文件读写进行控制,系统对每个文件设置了文件读写位置标记(简称文件位置标记或文件标记),用来指示要读写的下一个字符的位置。打开文件时文件标记指向文件头,完成读写后指向下一个字符处(类似记事本中的光标)。
改变文件读写标记:fseek()和rewind()
(1)读写标记定位(file seek):
【int fseek(FILE *filename, long int offset, int from);】
其中:
filename:操作的文件对象
offset:偏移量(长整型,要在数字末尾加一个L),正值 表示向文件结尾移动,负值表示向文件开头移动。
from:移动的起始点,C中定义:
0 - SEEK_SET 文件开头
1 - SEEK_CUR 当前位置
2 - SEEK_END 文件结尾
(2)读写标记重置(rewind):
【void rewind(FILE *filename);】
- 使文件标记指向文件头部。当以读写模式打开字符串时需要特别注意标记的位置。
(3)判断文件标记位置(file tell):
【long int ftell(FILE *fp);】
(4)返回文件位置标记的位置(end of file):
【int feof(FILE *fp);】
- 当文件位置标记位于文件结尾时返回一个非0值,否则返回0。
(5)文件读写出错检测(file error):
【ferror(fp);】
- 返回0表示未出错,返回非0值表示出错。
- 当在数据长度未知时需要边读取边判断是否到达EOF,由于有时边读取边处理,当输入函数读取到EOF时遍停止读取不更新变量,可能出现最后一组数据被处理并输出了两次的情况。这时不仅需要使用feof来控制循环次数,也要同时在读取后使用feof判断是否提前跳出循环。特别地,fread函数读取到EOF时会发生错误此时需要用ferror来进行判断。
(6)清除错误标记(clear error):
【void clearerr(FILE *fp);】
- 使文件错误标志和文件结尾标志置为0。只要出现文件读写错误标志它就一直存在,直到调用clearerr,rewind函数,或下一次进行输入输出。