C语言篇:文件 - I/O操作

537 阅读8分钟

对于关联到终端的输入流,如果读完了缓冲区中最后一个字节,并尝试继续读取,则程序阻塞,等待终端输入数据并刷新缓冲区。

格式化输入输出

关于格式控制的文档或者教程网络上随处可见,我就不再赘述,将直接跳过这些内容。

fprintf

int fprintf(FILE * restrict stream,
            const char * restrict format, ...);

fprintf函数把字符写入流中,数据由格式字符串及后面的参数提供。

函数返回实际写入的字符数,如果发生错误则返回一个负数。

如果所给参数多于格式控制需要的参数,则多余的参数会被忽略。如果少于格式控制需要的参数,行为是未定义的。

fprintf是一个可变参数函数。对于参数列表以...结尾的可变参数函数,或者以传统方式申明而不是以函数原型申明的函数,调用函数时存在默认参数提升。所有长度小于int的整数都被提升为int,所有的float都被提升成double。输出整数或浮点数时,函数传值而不是传地址,再经过参数提升,不需要关心原类型的长度。这就是为什么floatdouble都使用%f格式,而charshort等都可以使用%d格式,连警告都不会有。

fscanf

int fscanf(FILE * restrict stream,
           const char * restrict format, ...);

fscanf从流中读取字符,并根据格式控制把转换后的数据写入后面参数指向的位置。

函数返回成功写入的参数个数,可能为0或任意正整数。如果在成功读取任何字符前直接遇到文件末尾或错误,返回EOF

如果所给参数多于格式控制需要的参数,则多余的参数会被忽略。如果少于格式控制需要的参数,行为是未定义的。

格式字符串后面传递的参数应该都为指针,函数处理时会把它们的值都当作指针对待。从流中读取数据存在一个解析的过程,解析按流方向依次进行,如果输入的内容符合格式控制,满足写入条件时就把字符转换成对应数据写入参数指向的位置;如果从某个字符开始不符合格式控制,则解析失败,函数终止,所有已经写入的数据会被保留,未处理的参数会被忽略。解析失败不会产生错误,文件位置指示器会被置于解析失败的字符处。

fscanf也是可变参数函数。但与fprintf不同,fscanf几乎只接受指针,默认参数提升对它几乎没有影响。如果要在某块内存写入数据,就必须明确数据类型与数据长度,所以fscanf严格区分floatdoublecharshortint等也不能混用。

printf

int printf(const char * restrict format, ...);

printf函数同fprintf函数,只不过默认流是stdout

scanf

int scanf(const char * restrict format, ...);

scanf函数同fscanf函数,只不过默认流是stdin

snprintf

int snprintf(char * restrict s, size_t n,
             const char * restrict format, ...);

snprintf函数类似于fprintf函数,但是数据不写入流,而是写入字符数组中。

s表示接受输出的数组,n表示数组大小。如果输出内容多于n-1个字节,则多余的内容会被丢弃。snprintf会在写入数组的数据末尾加一个空字符'\0'

如果s是空指针或者n为0,则不会写入任何内容。

因为数组不带有缓冲区,所以写入的内容会立刻出现在数组中。

snprintf函数返回实际写入的字符数(不包括末尾的空字符),如果发生错误返回一个负数。

sprintf

int sprintf(char * restrict s,
            const char * restrict format, ...);

sprintf函数类似于snprintf函数,但没有数组大小限制。

sscanf

int sscanf(const char * restrict s,
           const char * restrict format, ...);

sscanf函数类似于fscanf函数,但是不读取流,而是读取字符串。

sscanf函数会把字符串末尾的空字符'\0'当作文件末尾。由于字符串不是流,不存在文件位置指针或缓冲区,每次以相同方式读取同一个字符串的结果是相同的。

函数返回成功写入的参数个数。如果在成功读取任何字符前直接遇到空字符或错误,返回EOF

字符输入输出

fputc

int fputc(int c, FILE *stream);

fputc函数将一个字节写入流中。注意参数是int类型,写入时会转换成unsigned char类型。

函数返回它实际写入的内容,返回值类型也是int。发生错误返回EOF

putc

int putc(int c, FILE *stream);

putc函数同fputc函数,但是一般实现为宏。所以参数不应该带有副作用。

putchar

int putchar(int c);

putchar函数同putc函数,只不过默认流是stdout

fgetc

int fgetc(FILE *stream);

fgetc函数从流中读取一个字节,然后从unsigned char类型转换成int类型并返回。

函数返回读取的内容,如果遇到文件末尾或错误,返回EOF

getc

int getc(FILE *stream);

getc函数同fgetc函数,但是一般实现为宏。所以参数不应该带有副作用。

getchar

int getchar(void);

getchar函数同getc函数,只不过默认流是stdin

fgets

char *fgets(char * restrict s, int n,
            FILE * restrict stream);

fgets函数从流中读取最多n-1个字节,存入数组s中。如果遇到换行符'\n'或者文件末尾,则停止读取(换行符会被读取并存入)。fgets会在存入数组的数据末尾加一个空字符'\0'

函数执行成功返回参数s。如果在成功读取任何字符前直接遇到文件末尾或错误,返回空指针。

fputs

int fputs(const char * restrict s,
          FILE * restrict stream);

fputs函数将字符串s的内容写入流中,遇到空字符'\0'则停止,空字符不会被写入。

函数执行成功返回一个非负整数,失败返回EOF

puts

int puts(const char *s);

puts函数类似于fputs,不过默认流是stdout,并且在结尾输出一个换行符'\n'

由于存在安全问题,gets函数已经在C11标准中被删除,本文不再讨论它。

ungetc

int ungetc(int c, FILE *stream);

ungetc函数将一个字符(由int类型转换为unsigned char类型)放回流中,如果继续进行读操作,则按照放回顺序的逆序读取这些字符。

函数执行成功返回实际放回的字符,失败返回EOF

ungetc函数的表现就好像是逆向写入并覆盖了原来流中的数据,它每次写入当前位置的前一个字节,并递减文件位置指示器。因此,成功执行ungetc函数后,文件位置指示器始终指向刚刚放回的字符。

ungetc函数并没有真正将字符放入流中,而是存入了另一个临时的缓冲区。任何ungetc函数放入的字符都不会被保存到文件中,并且一旦调用fseekfsetposrewind等设置文件位置指针的函数,所有没有被读取的由ungetc函数放回的字符都会被丢弃。在很多实现里,执行写操作也会丢弃ungetc函数放回的字符。

如果参数cEOF,则函数失败返回。如果函数执行成功,将会重置流的EOF标志

ungetc函数的设计和机制比较诡异,应谨慎使用。

直接输入输出

直接输入输出多用于二进制文件的读写。

fread

size_t fread(void * restrict ptr,
             size_t size, size_t nmemb,
             FILE * restrict stream);

fread函数从流中读取nmemb个元素,每个元素size个字节,读取的内容依次写入ptr指向的内存区域。

函数返回成功读取的元素个数,即小于或等于nmemb的非负整数。

fread函数最多读取nmemb * size个字节。如果流中没有足够的数据,则读到文件末尾停止,此时如果最后一个元素不满足size个字节,则此元素不被计入返回值。因为写入ptr指向的内存的数据没有确定的结尾符,为了知道函数读取了多少字节,size应该设置为1

fwrite

size_t fwrite(const void * restrict ptr,
              size_t size, size_t nmemb,
              FILE * restrict stream);

fwrite函数向流中写入nmemb个元素,每个元素size个字节,写入的数据来自ptr指向的内存区域。

函数返回成功写入的元素个数,即小于或等于nmemb的非负整数。

fwrite函数最多写入nmemb * size个字节。如果遇到写入错误,则函数返回,此时如果最后一个元素不满足size个字节,则此元素不被计入返回值。size也建议设置为1