下列两个函数执行二进制I/O操作。
#include <stdio.h> size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); // 两个函数的返回值:读或写的对象数
size为元素(结构、数组等)大小,nmemb为元素个数。
这些函数有两种常见的用法:
(1) 读或写一个二进制数组。
(2) 读或写一个结构。
fread和fwrite返回读或写的对象数。对于读,如果出错或到达文件尾端,则此数字可以少于nmemb。在这种情况下,应调用ferror或feof以判断究竟属于哪一种情况。对于写,如果返回值少于所要求的nmemb,则出错。
使用二进制I/O的基本问题是,它只能用于读同一系统上已写的数据。在一个系统上写的数据,要在另一个系统上进行处理,这种环境下,这两个函数可能就不能正常工作,其原因是:
(1) 在同一个结构中,同一成员的偏移量可能因编译器和系统而异。某些编译器有一个选项,选择它的不同值,或者使结构中的各成员紧密包装,或者准确对齐。这意味着即使在同一个系统上,一个结构的二进制存放方式也可能因编译器选项的不同而不同。
(2) 用来存储多字节整数和浮点值的二进制格式在不同的机器体系结构间也可能不同。
(PS:32位系统和64位系统中,基本数据类型长度可能不一样)
三种方法定位标准I/O流。
(1) ftell和fseek函数。它们假定文件的位置可以存放在一个长整型中。
(2) ftello和fseeko函数。文件偏移量不必一定使用长整型。它们使用off_t数据类型代替了长整型。
(3) fgetpos和fsetpos函数。这两个函数是由ISO C引入的。它们使用一个抽象数据类型fpos_t记录文件的位置。这种数据类型可以定义为记录一个文件位置所需的长度。
#include <stdio.h> long ftell(FILE *stream); // 返回值:若成功则返回当前文件位置指示,若出错则返回-1L int fseek(FILE *stream, long offset, int whence); // 返回值:若成功则返回00,若出错则返回非0值 void rewind(FILE *stream);
对于一个二进制文件,其文件位置指示器是从文件起始位置开始度量,并以字节为计量单位。
为了用fseek定位一个二进制文件,必须指定一个偏移量offset,以及解释这种偏移量的方式。whence的值:SEEK_SET表示从文件的起始位置开始,SEEK_CUR表示从当前位置开始,SEEK_END表示从文件尾端开始。ISO C并不要求实现一个对二进制文件支持SEEK_END的规范说明,其原因是某些系统要求二进制文件的长度是某个幻数的整数倍,而非实际内容部分则填充0。但是在UNIX中,对于二进制文件,SEEK_END是受支持的。
对于文本文件,它们的文件当前位置可能不以简单的字节偏移量来度量。这主要也是在非UNIX系统中,它们可能以不同的格式存放文本文件。为了定位一个文本文件,whence一定要是SEEK_SET,而offset只能有两种值:0(绕回到文件的起始位置),或是对该文件调用ftell所返回的值。使用rewind函数也可将一个流设置到文件的起始位置。
除了offset的类型是off_t而非long以外,ftello函数与ftell相同,fseeko函数与fseek相同。
#include <stdio.h> off_t ftello(FILE *stream); // 返回值:若成功则返回当前文件位置指示,若出错则返回-1 int fseeko(FILE *stream, off_t offset, int whence); // 返回值:若成功则返回0,若出错则返回非0值
fgetpos和fsetpos这两个函数是C标准引进的。
#include <stdio.h> int fgetpos(FILE *stream, fpos_t *pos); int fsetpos(FILE *stream, fpos_t *pos); // 两个函数的返回值:若成功则返回0,若出错则返回非0值
fgetpos将文件位置指示器的当前值存入由pos指向的对象中,在以后调用fsetpos时,可以使用此值将流重新定位至该位置。
执行格式化输出处理的是4个printf函数。
#include <stdio.h> int printf(const char *format, ...); int fprintf(FILE *stream, const char *format, ...); // 两个函数的返回值:若成功则返回输出字符数,若输出出错则返回负值 int sprintf(char *str, const char *format, ...); int snprintf(char *str, size_t size, const char *format, ...); // 两个函数返回值:若成功则返回存入数组的字符数,若编码出错则返回负值
printf将格式化数据写到标准输出,fprintf写至指定的流,sprintf将格式化的字符送入数组buf中。sprintf在数组的尾端自动加一个null字节,但该字节不包括在返回值中。
sprintf函数可能会造成由buf指向的缓冲区的溢出。为了解决缓冲区溢出问题,引入了snprintf函数。在该函数中,缓冲区长度是一个显式参数,超过缓冲区尾端写的任何字符都会被丢弃。如果缓冲区足够大,snprintf函数就会返回写入缓冲区的字符数。与sprintf相同,该返回值不包括结尾的null字节。若snprintf函数返回小于缓冲区长度n的正值,那么没有截短输出。若发生了一个编码错误,snprintf则返回负值。(PS:编码错误是什么?)
格式说明符控制其余参数的编写和显式。每个参数按照转换说明符编写,转换说明以字符%开始,除转换说明外,格式字符串中的其他字符按照原样,不经任何修改地被复制输出。
%[flags][fldwidth][precision][lenmodifier]convtype
flags:标志。
fldwidth:说明转换的最小字符宽度。
precision:说明整型转换后最少输出数字位数、浮点数转换后小数点后的最少位数、字符串转换后的最大字符数。
lenmodifier:说明参数长度
convtype不是可选的,它控制如何解释参数。
(具体说明省略。。。)
下列4中printf族的变体类似于上面的4种,但是可变参数表(...)代换成了arg。
#include <stdio.h> #include <stdarg.h> int vprintf(const char *format, va_list ap); int vfprintf(FILE *stream, const char *format, va_list ap); // 两个函数的返回值:若成功则返回输出字符数,若输出出错则返回负值 int vsprintf(char *str, const char *format, va_list ap); int vsnprintf(char *str, size_t size, const char *format, va_list ap); // 两个函数返回值:若成功则返回存入数组的字符数,若编码出错则返回负值
执行格式化输入处理的是三个scanf函数。
#include <stdio.h> int scanf(const char *format, ...); int fscanf(FILE *stream, const char *format, ...); int sscanf(const char *str, const char *format, ...); // 三个函数返回值:指定的输入项数;若输入出错或在任一变换前已到达文件结尾则返回EOF
scanf族用于分析输入字符串,并将字符序列转换成指定类型的变量。format之后的各参数包含了变量的地址,以用转换结果初始化这些变量。
格式说明控制如何转换参数,以便对它们赋值。转换说明以%字符开始。除转换说明和空白字符外,格式字符串中的其他字符必须与输入匹配。若有一个字符不匹配,则停止后续处理,不再读入输入的其余部分。
一个转换说明有三个可选部分:
%[*][fldwidth][lenmodifier]convtype
*:可选的前导星号用于抑制转换。按照转换说明的其余部分对输入进行转换,但转换结果并不存放在参数中。
fldwidth:说明最大宽度。
lenmodifier:说明要用转换结构初始化的参数大小。
convtype:类似于printf族的转换类型字段,但两者之间还有些差别。一个差别是,存储在无符号类型中的结果可在输入时带上符号。例如,-1可被转换成4 294 967 295赋予无符号整型变量。
与printf族一样,scanf族也支持函数使用由<stdarg.h>说明的可变参数表。
#include <stdio.h> #include <stdarg.h> int vscanf(const char *format, va_list ap); int vsscanf(const char *str, const char *format, va_list ap); int vfscanf(FILE *stream, const char *format, va_list ap); // 三个函数返回值:指定的输入项数;若输入出错或在任一变换前达到文件结尾则返回EOF