错误报告
perror函数简化向用户报告特定错误的过程。它的原型定义于studio.h,如下:
void perror(char const *message);
如果message不是NULL并且指向一个非空的字符串,perror函数就打印出这个字符串,后面跟一个分号和一个空格,然后打印出一条用于解释errno(标准库函数用一个外部整型变量来保存错误码)。
终止执行
exit函数用来终止一个程序,它的原型定义于stdlib.h,如下:
void exit(int status);
status参数返回给操作系统,用于提示程序是否正常完成。这个值和main函数返回的整型状态值一样,预定义符号EXIT_SUCCESS和EXIT_FALIURE分别提示程序的终止成功还是失败,虽然程序可以使用其他值,但它们的具体含义取决于编译器。
注意这个函数没有返回值。当exit函数结束时,程序已经消失,所以它无处可返。
标准I/O函数库
头文件stdio.h包含了与ANSI函数库的I/O部分有关的声明。
流
就C程序而言,所有的I/O操作只是简单地从程序移进或移出字节的事情,这些字节流便被称为流。
流I/O总览
I/O函数以三种基本的形式处理数据:单个字符,文本行和二进制数据对于每一种都有特定的函数对他们进行处理。
执行字符,文本行和二进制I/O的函数
数据类型 | 输 入 | 输 出 | 描 述 |
---|---|---|---|
字符 | getchar | putchar | 读取(写入)单个字符 |
文本行 | gets | puts | 文本行未格式化的输入(输出) |
文本行 | scanf | printf | 格式化的输入(输出) |
二进制数据 | fread | fwrite | 读取(写入)二进制数据 |
打开流
fopen用于打开一个特定的文件,并把一个流和这个文件相关联,它的原型如下:
File *fopen(char const *name,char const *mode);
参数name是打开的文件名,mode是打开文件的方式(见下表),File*类型的变量用来保存fopen的返回值,并不影响哪个文件被打开。
数据格式 | 读 取 | 写 入 | 添 加 |
---|---|---|---|
文本 | "r" | "w" | "a" |
二进制 | "rb" | "wb" | "ab" |
如果一个文件打开是用于读取的,那么他应该是原先已经存在的;
如果一个文件打开是用于写入的,那么它原先的内容就会被删除,如果它原先不存在,那么就创建一个新文件;
如果一个文件打开用于天机的文件原先并不存在那么它将被创建,如果它原先已经存在,那么它原先的内容不会被删除,无论那种情况都只能在文件的尾部写入。
freopen用于打开特定(重新打开)的文件流,它的原型如下:
File *freopen(char const *filename,char const *mode,File *stream);
最后一个参数就是要打开的流,它可能是一个先前用fopen函数打开的流,也可能是stdin,stdout或是stderr。
关闭流
int fclose(File *f);
对于输出流,fclose函数在文件关闭前刷新缓冲区,如果它执行成功,fclose返回零值,否则返回EOF。
字符I/O
字符输入是由getchar函数家族执行的,他们的原型如下:
int fgetc(File *stream);
int getc(File *stream);
int getchar(void);
这些函数都用于读取字符,但它们都返回一个int型但是不是char型。
字符输出是由putchar函数家族执行的,它们的原型如下:
int fputc(int character,File *stream);
int putc(int character,File *stream);
int putchar(int character);
fgetc和fputc都是真正的函数,但getc,putc,getchar,putchar,都是通过宏定义的。
撤销字符I/O
函数原型如下:
int ungetc(int character,File*stream);
函数ungetc把一个先前读入的字符返回流中,这样它可在以后被重新读入。
未格式化的行I/O
行I/O的执行方式分为两类——未格式化或格式化,这两种形式都用于操作字符串。
gets和puts家族函数的原型如下:
char *fgets(char *buffer,int buffer_size,File *stream);
char gets(char *buffer);
int fputs(char *buffer,File *stream);
int puts(char const *buffer);
fgets函数从制定的stream中读取字符并把它们复制到buffer中,当它读取到一个换行符并存储到缓冲区之后就不在读取。
fgets函数从制定的流中读取buffer_size字符和一个NULL字符(尾零)。
fgets函数返回它的第一个参数(执行缓冲区的指针),如果在未读取任何字符就达到文件尾,fgets返回NULL。
fputs函数的缓冲区必须包含一个以NULL结尾的字符串,所以该函数没有字符串的长度参数,这个字符串是逐字写入的,如果它不包含一个换行符,就不会写入换行符。如果包含好几个换行符,所有的换行符都会被写入。
函数gets和puts几乎和函数fgets和fputs相同,区别在于:
1.gets在读取一行输入时,它并不在缓冲区中存储结尾的换行符。
2.puts在写入一个字符串时,它在字符串写入之后向输出在添加一个换行符。
格式化的I/O
scanf和printf函数家族负责格式化行的输入与输出。
scanf家族
scanf函数家族的原型中的省略号表示一个可变长的指针列表。从输入转换而来的值逐个存储到这些指针参数所指向的内存位置。
int fscanf(File *stream,char const *fomat,...);
inr scanf(char const *fomat,...);
int sscanf(char const *string,char const *format,...);
这些函数都是从输入源读取字符并根据format字符串个出的格式码对他们进行转换,fscanf的输入源就是stream(流),scanf的输入源就是stdin,而sscanf则从第一个参数给出的字符串中读取字符。
当格式化字符串达到末尾或者读取的输入不在匹配格式字符串所指定的类型事,输入就停止,在任何一种情况下,被转换的输入值的数目作为函数的返回值。如果在任何输入值被转换之前就达到尾部,函数就返回常量值EOF。
scanf格式代码
scanf函数家族中的format字符串可以是下列内容:
- 空白字符——他们与输入中的零个或多个空白符匹配,在处理过程中被忽略。
- 格式代码——它们指定函数如何解释接下来的输入字符。
- 其他字符——当任何其他字符出现在格式字符串中,下一个输入字符必须与他匹配。如果匹配,该输入字符随后被丢弃,如果不匹配,函数就不在读取直接返回。
scanf函数家族的格式字符串通常以一个%号开始,有下列内容组成:
- 可选的星号(*)——将转换后的值丢弃而不是存储;
- 一个可选的宽度——限制输入字符的个数,如果宽度未给出,函数就连续输入知道遇见输入中的下一个空白字符;
- 一个可选的限定符——修改有些格式代码的含义;
- 格式代码。
printf函数家族
printf函数家族用于创建格式化输出。
int fprintf(File *stream,char const *format,...);
int printf(char connst *format,...);
int sprintf(char *buffer,char const *format,...);
prinnt结果输出送到标准输出(stdout);
fprintf可以使用任何输出流;
sprintf把它的的结果作为一个NULL结尾的字符串存储到指定的buffer缓冲区,而不是流;
printf家族函数的格式码通常以%开始由下列要素组成:
- 零个或多个标志字符——修改转换的执行方式;
- 一个可选的最小字段——如果值的字符小于字段宽度,就对它进行填充增加长度,标志决定填充使用空白还是零以及它出现在值得左边还是右边;
- 一个可选的精度——指定将出现结果中的最小的数字个数并覆盖零标志,精度缺省为零;
- 一个可选的修改字符;
- 转换类型;
二进制I/O
把数据写入到文件效率最高的是二进制形式写入。二进制输出避免了在数值转换成字符串过程的所涉及的开销和精度损失。
fread函数用于读取二进制数据,fwrite函数用于写入二进制,他们的原型如下:
size_t fread(void *bffer,size_t size,sizet_cout,File *stream);
size_t fwrite(void *bffer,size_t size,sizet_cout,File *stream);
buffer是一个指向用于保存数据的内存位置的数据,size是缓冲区每个元素的字节数,count是读取或写入的元素数,stream是数据读取或写入的流。
函数的返回值是实际读取或写入的元素数目。
刷新或定位函数
- 函数fflush——它迫使一个输出流的缓冲区内的数据进行物理写入,不管它是否已经写满,原型如下:
int fflush(File *stream);
当我们需要立即把数据缓冲区的内容吴莉莉写入时,就可以使用这个函数。
C语言支持随机I/O,也就是以任意的顺序访问文件中的不同位置,随机访问时通过在读取或写入前定位到文件中所需要的位置来实现的,ftell和fseek函数来实现这项操作。
long ftell(File *stream);
int fseek(File *stream,long offset,int from);
ftell函数返回当前流的位置,也就是说,下一个读取或写入将要开始的位置距离文件起始位置的偏移量。
ftell函数的返回值总是可以作为函数fseek的参数,作为一个距离文件起始位置的偏移量。
函数fseek允许你在一个流中定位,这项操作将改变下一个读取或写入的位置,它的第二第三个参数如下表。
数据格式 | 你将定位到的是 |
---|---|
SEEK_SET | 从流的起始位置起offset个字节,offset必须是一个非负值 |
SEEK_CUR | 从流的当前位置起offset个字节,offset可正可负 |
SEEK_END | 从流的尾部位置起offset个字节,offset可正可负,如果是正直将定位到文件尾的后面 |
改变缓冲方式
在流上的缓冲凡是有时候并不适合,下面两个函数可以改变流的缓冲方式,这两个函数只有当指定的流被打开还没有在它上面执行任何操作时才能够被调用。
void setbuf(File *stream,char *buf);
int setvbuf(File *stream,char *buf,int mode,size_t size);
setbuf函数设置了一个数组,用于对流进行缓冲。这个数组的长度必须是BUFSIZE(它在stdio中定义)如果以一个NULL参数调用这个函数,setbuf会将这个流关闭。
setvbuf函数中的mode参数用于指定缓冲类型,_IOFBF指定一个完全缓冲流,_IONBF指定一个不缓冲的流,_IOLBF指定一个行缓冲流(当一个换行符写入到缓冲区时,缓冲区就进行刷新)
文件操纵函数
有两个函数用于操纵文件但不执行任何输入/输出操作,执行成功函数返回值为零,如果失败返回非零。
int remove(char const *filename);
int rename(char const *oldname,char const *newname);
函数remove用于删除一个指定的文件呢,文件处于打开状态,其结果取决于编译器。
函数rename用于重命名文件名。