C语言篇:文件 - 文件与流操作

547 阅读8分钟

文件操作

删除文件

int remove(const char *filename);

remove函数类似于Unix/Linux命令rm,接受一个文件名,然后删除其对应的文件。对于大多数文件系统,删除文件仅仅是删除文件索引,并不擦除硬盘内容,所以能在一瞬间完成。

函数执行成功返回0,失败返回非0。

重命名文件

int rename(const char *old, const char *new);

rename函数类似于Unix/Linux命令mv,不仅能重命名文件,也能把文件移动到其他目录,因为二者原理是一样的,都是修改目录而不改变文件本身。当oldnew对应的文件不在同一个文件系统上时,rename函数将失败。因为跨文件系统移动文件需要复制文件,和在同一个文件系统内移动文件的原理完全不同。

函数执行成功返回0,失败返回非0。

创建临时文件

FILE *tmpfile(void);

tmpfile函数会创建并打开一个不重名的临时文件,当文件关闭或者程序退出时,这个文件会自动删除。文件的打开方式为wb+,对于Unix/Linux,这个临时文件位于/tmp目录下。

函数执行成功返回临时文件对应的FILE *指针(即流),失败返回空指针。

打开文件

FILE *fopen(const char * restrict filename,
            const char * restrict mode);

fopen函数打开一个文件并关联到一个新创建的流。函数执行成功返回这个流,失败返回空指针。

mode是文件的打开方式,为一个字符串,函数将依次解析里面的每个字符。

基本的打开方式有rwa,他们是互斥的,只能出现一个,必须放在mode字符串的开头。

  • r:读取文件。如果文件不存在或没有访问权限,则打开失败。不会截断文件。
  • w:写入文件。如果文件不存在则创建文件,如果文件存在则截断文件(除非指定了x)。
  • a:追加到文件。如果文件不存在则创建文件,如果文件存在则追加到文件末尾。

此外,还有一些额外的模式限定字符,他们不能独立出现,必须依赖于上面提到的基本打开方式。它们不是互斥的,可按需组合。

  • b:以二进制模式打开。没有b则默认是文本模式。
  • +:以更新模式打开。既可以读又可以写。
  • x:以独占模式打开,只能跟w配合使用。如果文件不存在则创建文件,如果文件存在则打开失败。

以更新模式(+)打开文件时,流既可以进行读操作也可以进行写操作,但同一时刻只能处于读或写的一种状态。不同状态间文件位置指示器和缓冲区是通用的。进行读操作可以把流状态设置为读,进行写操作可以把流状态设置为写。在很多实现里,切换到读状态将刷新写状态的缓冲区。

如果以 aab 模式打开文件,则会阻止fseek等函数对文件位置指示器的修改,文件位置指示器永远指向文件末尾,所有输出内容被强制追加到文件末尾。

如果以 a+ab+ 模式打开文件,则fseek等函数可以生效,以调整读取时的文件位置。但是一旦进行写入,文件位置指示器就被设置到文件末尾,所有输出内容都会被强制追加到文件末尾。

关闭文件

int fclose(FILE *stream);

fclose函数关闭一个已打开的文件,并释放与其关联的流。fclose在关闭文件之前会先刷新缓冲区。

函数执行成功返回0,失败返回EOF

无论文件是否成功关闭,该流都将与该文件取消关联,释放该流以及自动分配的缓冲区。

当程序正常结束(main函数返回或调用exit函数)时,所有打开的流都会被关闭,效果同调用fclose

流操作

重定向流

FILE *freopen(const char * restrict filename,
              const char * restrict mode,
              FILE * restrict stream);

freopen函数关闭并取消关联原文件,然后打开一个新文件并关联到该流。

函数执行成功返回该流,失败返回空指针。

freopen函数先关闭原文件,原文件是否成功关闭不影响后续操作以及返回值。之后尝试打开指定文件,如果打开失败则返回空指针。如果成功打开并关联到一个新文件,流的错误标志以及EOF标志会被重置。

如果filename是一个空指针,freopen函数会尝试将流模式更改为mode指定的模式,不会关闭或取消关联原文件。

freopen主要用于重定向stdinstdoutstderr,但是更简洁的方法是直接打开文件并赋值给这些指针。

刷新缓冲区

int fflush(FILE *stream);

fflush函数会刷新给定输出流的缓冲区,包括以写模式、追加模式打开,或者以更新模式打开且处于写状态的流。缓冲区中的内容会立刻写入对应文件中。

函数执行成功返回0,失败返回EOF

对输入流调用fflush的行为是未定义的。C标准没有提供刷新输入缓冲区的方法。

如果stream是空指针,那么fflush会刷新所有的输出流。

设置缓冲区

设置缓冲区应该在流关联到一个文件之后,且进行任何其他操作之前。否则,行为是未定义的。

当流关联到一个文件,在进行任何其他操作之前,缓冲区是未分配的,此时设置缓冲区是安全的,不会造成内容丢失。

setvbuf

int setvbuf(FILE * restrict stream,
            char * restrict buf,
            int mode, size_t size);

setvbuf函数将把buf所指的内存区域作为流的缓冲区,size表示缓冲区大小,mode表示缓冲方式。

函数执行成功返回0,失败返回非0。

如果buf是空指针,setvbuf函数将自动分配给流一个缓冲区,size指定该缓冲区的大小。

mod接受三个预定义的宏:

  • _IOFBF:全缓冲
  • _IOLBF:行缓冲
  • _IONBF:无缓冲

setbuf

void setbuf(FILE * restrict stream,
            char * restrict buf);

setbuf函数与setvbuf函数功能相近,不过默认把宏 _IOFBF作为缓冲方式,把宏BUFSIZ作为缓冲区大小。如果buf是空指针,把宏 _IONBF作为缓冲方式。

setbuf函数没有返回值。

文件位置操作

对于支持位置请求的流(比如关联到普通文件的流,而不是关联到终端的流),包含一个文件位置指示器,可以进行文件位置操作。

在很多实现里,设置文件位置指示器将刷新输出缓冲区。

ftell

long int ftell(FILE *stream);

ftell函数获取文件位置指示器的值,即当前位置距离文件开头的偏移量。从0开始,单位是字节。

函数执行成功返回表示偏移量的整数值,失败返回-1。

fseek

int fseek(FILE *stream, long int offset, int whence);

fseek函数用来设置文件位置指示器的值。whence表示起点,offset表示距离起点的偏移量。

函数执行成功返回0,失败返回非0。

whence接受三个预定义宏:

  • SEEK_SET:文件开头,偏移量为0
  • SEEK_CUR:当前位置,即当前文件位置指示器的值
  • SEEK_END:文件末尾,在最后一个字节之后

offset的值可以是正数,也可以是负数或0。

如果fseek函数执行成功,将重置流的EOF标志

rewind

void rewind(FILE *stream);

rewind函数将文件位置指示器退回到文件开头。该函数没有返回值。

如果rewind函数执行成功,将重置流的EOF标志以及错误标志

fgetpos

int fgetpos(FILE * restrict stream,
            fpos_t * restrict pos);

fgetpos函数将文件位置指示器的值以及宽字符流的解析状态存入pos所指的对象中。

函数执行成功返回0,失败返回非0。

fsetpos

int fsetpos(FILE *stream, const fpos_t *pos);

fsetpos函数将根据pos所指的对象,设置文件位置指示器的值以及宽字符流的解析状态。

函数执行成功返回0,失败返回非0。

pos所指的对象应该是上次对同一个流调用fgetpos产生的结果。如果fsetpos函数执行成功,将重置流的EOF标志

流状态操作

feof

int feof(FILE *stream);

feof函数检查流的EOF标志是否被设置。

如果流的EOF标志被设置,返回非0;否则返回0。

ferror

int ferror(FILE *stream);

ferror函数检查流的错误标志是否被设置。

如果流的错误标志被设置,返回非0;否则返回0。

clearerr

void clearerr(FILE *stream);

clearerr函数重置流的EOF标志错误标志。该函数没有返回值。

perror

void perror(const char *s);

perror函数将错误标志的序号映射成错误消息字符串,并输出到stderr。该函数没有返回值。

错误消息字符串的末尾包含一个换行符。如果s是一个有效的字符串(不是空指针且不指向'\0'),那么会先输出该字符串的内容,跟着输出一个": "(冒号和空格),然后输出错误消息字符串的内容。