文本文件 I/O


文件的输出入定义在 stdio.h 标头文件,若要开启文件,可以使用fopen,其函数原型定义如下:

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

FILE是个结构类型:

typedef struct _iobuf {
    char*    _ptr;
    int    _cnt;
    char*    _base;
    int    _flag;
    int    _file;
    int    _charbuf;
    int    _bufsiz;
    char*    _tmpfname;
} FILE;

fopen返回FILE实例的地址,若将FILE的地址传给fgetcfputcfgetsfputs等函数,字符串流只是在读取或写入的过程,会进行文本编码的转换,例如int数字 9,在写入的操作中,会转换为编码 57 的字节数据,至于本身是char的数据,就直接以对应的字节写出,也就是所谓的纯文本文件读写。

fopen第一个参数用来指定文件名称,第二个参数用来指定输出入模式,模式基本上就是读、写、扩充等,分别可使用rwa等设定,若加上+, 表示扩充文件读写能力。例如,以下是可设定的模式:

  • r:只读模式开启文件,若文件不存在,返回 NULL。
  • w:唯写模式创建文件,若文件不存在,创建新文件,若文件存在,就删除其内容。
  • a:附加模式开启文件,若文件不存在,创建新文件,若文件存在,从文件尾端写入。
  • r+:开启文件进行读写,若文件不存在,返回 NULL,若文件存在,从文件开头进行读写。
  • w+:创建文件进行读写,若文件不存在,创建新文件,若文件存在,就删除其内容。
  • a+:开启文件进行读写,若文件不存在,创建新文件,若文件存在,从文件尾端写入。

可以选择性地加上b表示以二进制模式,在 POSIX 系统上会忽略,Windows 会不处理\n\x1A

例如,以下可开启一个文件进行读取:

FILE *fp = fopen("test.txt", "r");

可以使用以下片段来测试文件是否开启成功:

if(!fp) {
    perror("文件开启失败"); // 将消息输出至 stderr
    return EXIT_FAILURE;
}

开启文件之后,可以使用fgetc来读取文件中的字符,使用fputc来将字符写入文件,以读取为例:

int c; 
while ((c = fgetc(fp)) != EOF) { 
   putchar(c);
}

ferror可以检查文件读写是否有误,feof可检查读取到文件尾:

if (ferror(fp)) {
    puts("读取时发生错误");
}
else if (feof(fp)) {
    puts("读取成功");
}

fopen会使用缓冲区减少对磁碟的读写,不使用文件时,记得关闭文件,关闭文件会将缓冲区中的数据写入磁碟,若忘了关闭文件,可能会造成数据遗失,可以使用fclose来关闭文件:

fclose(fp);

下面这个程序示范如何读取来源文件并将内容写为另一文件:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    FILE* src = fopen(argv[1], "r");
    if(!src) {
        perror("无法开启来源文件");
        return EXIT_FAILURE;
    }

    FILE* dest = fopen(argv[2], "w");
    if(!dest) {
        perror("无法创建目标文件");
        return EXIT_FAILURE;
    }

    int c;
    while ((c = fgetc(src)) != EOF) { 
       fputc(c, dest);
    }

    if (ferror(src) || ferror(dest)) {
        puts("读写时发生错误");
    }

    fclose(src);
    fclose(dest);
}

也可以使用fgets来读取整个字符串,使用fputs来写入整个字符串:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    FILE* src = fopen(argv[1], "r");
    if(!src) {
        perror("无法开启来源文件");
        return EXIT_FAILURE;
    }

    FILE* dest = fopen(argv[2], "w");
    if(!dest) {
        perror("无法创建目标文件");
        return EXIT_FAILURE;
    }

    char buf[8];
    while (fgets(buf, sizeof(buf), src) != NULL) { 
       fputs(buf, dest);
    }

    if (ferror(src) || ferror(dest)) {
        perror("读写时发生错误");
    }

    fclose(src);
    fclose(dest);
}

fgets第一个参数为用来存储读入的数据,第二个参数为读入的字符长度,第三个参数为FILE地址值,而fputs第一个参数为写入的数据,第二个参数为FILE地址值。

以下的程序使用fgetsfputs改写以上范例:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[]) {
    FILE* src = fopen(argv[1], "r");
    if(!src) {
        perror("无法开启来源文件");
        return EXIT_FAILURE;
    }

    FILE* dest = fopen(argv[2], "w");
    if(!dest) {
        perror("无法创建目标文件");
        return EXIT_FAILURE;
    }

    char buf[8];
    while (fgets(buf, sizeof(buf), src) != NULL) { 
       fputs(buf, dest);
    }

    if (ferror(src) || ferror(dest)) {
        perror("读写时发生错误");
    }

    fclose(src);
    fclose(dest);
}

在程序执行过程开启的标准输出stdout、标准输入stdin、标准错误stderr,事实上也是文件的特例,在 C 程序中,也常见到以下的方式,以便直接控制这三个标准输入、输出、错误:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[]) {
    FILE* src = fopen(argv[1], "r");
    if(!src) {
        fputs("无法开启来源文件", stderr);
        return EXIT_FAILURE;
    }

    FILE* dest = fopen(argv[2], "w");
    if(!dest) {
        fputs("无法创建目标文件", stderr);
        return EXIT_FAILURE;
    }

    char buf[8];
    while (fgets(buf, sizeof(buf), src) != NULL) { 
        fputs(buf, dest);
    }

    if (ferror(src) || ferror(dest)) {
        fputs("读写时发生错误", stderr);
    }

    fclose(src);
    fclose(dest);
}




展开阅读全文