Skip to content

读写文件

文件的顺序读写

  • 按照字符读写文件: fgetc() , fputc()
  • 按照行读写文件: fputs() , fgets()
  • 按照块读写文件: fread() , fwirte()
  • 按照格式化读写文件: fprintf() , fscanf()
  • 按照随机位置读写文件: fseek() , ftell() , rewind()

按照字符读写文件

写文件fputc

c
#include <stdio.h>
int fputc(int ch, FILE * stream);

功能:将ch转换为unsigned char后写⼊stream指定的文件中

参数

  • ch :需要写⼊文件的字符
  • stream :文件指针

返回值

  • 成功:成功写⼊文件的字符
  • 失败:返回 -1
c
char buf[] = "this is a test for fputc"; 
int i = 0; 
int n = strlen(buf);
for (i = 0; i < n; i++){
    //往文件fp写⼊字符buf[i]
    int ch = fputc(buf[i], fp);
    printf("ch = %c\n", ch);
}

文件结尾

在C语⾔中,EOF表⽰文件结束符(end of file)。在while循环中以EOF作为文件结束标志,这种以EOF作为文件结束标志的文件,必须是文本文件。 在文本文件中,数据都是以字符的ASCII代码值的形式存放。我们知道,ASCII代码值的范围是0~127,不可能出现-1,因此可以用EOF作为文件结束标志。

c
#define EOF (-1)

当把数据以二进制形式存放到文件中时,就会有-1值的出现,因此不能采用EOF作为二进制文件的结束标志。为解决这一个问题,ANSI C提供一个feof函数,用来判断文件是否结束。feof函数既可用以判断二进制文件又可用以判断文本文件。

c
#include <stdio.h>
int feof(FILE * stream);

功能:检测是否读取到了文件结尾。判断的是最后一次“读操作的内容”,不是当前位置内容 (上一个内容)。

参数

  • stream :文件指针

返回值

  • 非0值:已经到文件结尾
  • 0 :没有到文件结尾

命令行中输入EOF

windows:⾸先在最后一行结束后(此时未换行)输⼊ENTER键,新起一行,再输⼊ctrl+z,再输⼊ENTER键即可。

Linux:直接按CTRL+D

读文件fgetc

c
#include <stdio.h>
int fgetc(FILE * stream);

功能:从stream指定的文件中读取一个字符

参数

  • stream :文件指针

返回值

  • 成功:返回读取到的字符
  • 失败: -1
c
char ch;
#if 0 
while ((ch = fgetc(fp)) != EOF){ 
    printf("%c", ch); 
} 
printf("\n"); 
#endif

//文件没有结束,则执行循环 
while (!feof(fp)) {
    ch = fgetc(fp);
    printf("%c", ch); 
} 
printf("\n");

按照行读写文件

写文件fputs

c
#include <stdio.h>
int fputs(const char * str, FILE * stream);

功能:将str所指定的字符串写⼊到stream指定的文件中,字符串结束符 '\0' 不写⼊文件。

参数

  • str :字符串
  • stream :文件指针

返回值

  • 成功:0
  • 失败: -1
c
char *buf[] = { "123456\n", "bbbbbbbbbb\n", "ccccccccccc\n" };
int i = 0; 
int n = 3;
for (i = 0; i < n; i++){
    int len = fputs(buf[i], fp);
    printf("len = %d\n", len);
}

读文件fgets

c
#include <stdio.h>
char * fgets(char * str, int size, FILE * stream);

功能:从stream指定的文件内读⼊字符,保存到str所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1个字符为⽌,最后会⾃动加上字符 '\0' 作为字符串结束。

参数

  • str :字符串
  • size :指定最⼤读取字符串的长度(size - 1
  • stream :文件指针

返回值

  • 成功:成功读取的字符串
  • 读到文件尾或出错: NULL
c
char buf[100] = 0;

//文件没有结束 
while (!feof(fp)) {
    memset(buf, 0, sizeof(buf)); 
    char *p = fgets(buf, sizeof(buf), fp); 
    if (p != NULL){
        printf("buf = %s", buf);
    }
}

强化训练

一、实现vi及cat命令

实现vi
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
    char buf[1024] = {0};
    FILE *fd = fopen(argv[1], "w+");
    while (1) {
        memset(buf, 0x00, 1024);
        fgets(buf, 1024, stdin);
        if (buf[0] == ':' && buf[1] == 'w' && buf[2] == 'q' && buf[3] == '\n') { break; }
        fputs(buf, fd);
    }

    fclose(fd);

    return 0;
}
实现cat
c
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {
    FILE *fd = fopen(argv[1], "r");
    char buf[1024];
    while (1) {
        fgets(buf, 1024, fd);
        if (feof(fd)) { break; }
        fputs(buf, stdout);
    }

    fclose(fd);

    return 0;
}

二、文件版四则运算

有个文件⼤⼩不确定,每行内容都是一个四则运算表达式,还没有算出结果,写一个程序,⾃动算出其结果后修改文件。

实现
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

int randi(void) { return rand() % 100; }

char randc(void) {
    int a = rand() % 4;

    if (a == 0) {
        return '+';
    } else if (a == 1) {
        return '-';
    } else if (a == 2) {
        return '*';
    } else {
        return '/';
    }
}

int writ(FILE *fd) {
    for (int i = 0; i < 10; i++) {
        int ret1 = randi();
        int ret2 = randi();

        if (ret1 == 0) { ret1 += randi(); }
        if (ret2 == 0) {
            ret2 += randi();
        }
        fprintf(fd, "%d%c%d=\n", ret1, randc(), ret2);
    }

    return 0;
}

float ret(char ch, int a, int b, float c) {
    switch (ch) {
        case '+':
            c = a + b;
            break;
        case '-':
            c = a - b;
            break;
        case '*':
            c = a * b;
            break;
        case '/':
            c = ((float) a) / b;
            break;
    }
    return c;
}

void read(FILE *fd) {
    char buf[1024] = {0};
    char buf2[1024] = {0};
    char buf3[4096] = {0};

    int a, b;
    float c;
    char ch;
    rewind(fd);

    while (1) {
        fgets(buf, 1024, fd);
        if (feof(fd)) { break; }
        sscanf(buf, "%d%c%d=\n", &a, &ch, &b);
        c = ret(ch, a, b, c);
        sprintf(buf2, "%2d%c%2d=%.2f\n", a, ch, b, c);
        strcat(buf3, buf2);
    }
    rewind(fd);
    fputs(buf3, fd);
}

int main(int argc, char *argv[]) {
    srand((unsigned int) time(NULL));
    FILE *fd = fopen("02.txt", "w+");
    if (fd == NULL) { return -1; }
    writ(fd);
    read(fd);
    fclose(fd);

    return 0;
}

按照格式化读写文件

写文件fprintf

c
#include <stdio.h> 
int fprintf(FILE * stream, const char * format, ...);

功能:根据参数 format 字符串来转换并格式化数据,然后将结果输出到 stream 指定的文件中,指定出现字符串结束符 '\0' 为⽌。

参数

  • stream :已经打开的文件
  • format :字符串格式,用法和printf()一样

返回值

  • 成功:实际写⼊文件的字符个数
  • 失败: -1
c
fprintf(fp, "%d %d %d\n", 1, 2, 3);

读文件fscanf

c
#include <stdio.h> 
int fscanf(FILE * stream, const char * format, ...);

功能:从stream指定的文件读取字符串,并根据参数format字符串来转换并格式化数据。

参数

  • stream :已经打开的文件
  • format :字符串格式,用法和scanf()一样

返回值

  • 成功:参数数目,成功转换的值的个数
  • 失败: -1
c
int a = 0; 
int b = 0; 
int c = 0; 
fscanf(fp, "%d %d %d\n", &a, &b, &c); 
printf("a = %d, b = %d, c = %d\n", a, b, c);

强化训练

文件版排序

实现
c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

int randi(void);
void make(void);
void sort(void);

int main(int argc, char *argv[]){
    srand((unsigned int)time(NULL));
    if (strcmp(argv[1], "make") == 0) {
        make();
    } else if (strcmp(argv[1], "sort") == 0) {
        sort();
    }

    return 0;
}

int randi(void){
    return rand() % 100;
}

void make(void){
    FILE *fp = fopen("04test.txt", "w+");
    if (fp == NULL) {
        puts("随机数生成失败");
        perror("open");
    } else {
        for (int i = 0; i < 10; ++i) {
            fprintf(fp, "%d ", randi());
        }

        fclose(fp);
    }
}

void sort(void) {
    int array[10] = {0};
    int index = 0;
    FILE *fp = fopen("04test.txt", "r");
    if (fp == NULL) {
        puts("读取随机数文件失败");
        perror("open");
    } else {
        while(!feof(fp)){
            fscanf(fp, "%d ", &array[index]);
            index++;
        }

        fclose(fp);
    }

    printf("排序前:");
    for (int i = 0; i < 10; ++i){
        printf("%d ", array[i]);
    }
    puts("");

    int tmp = 0;
    for(int i = 0; i < 10; ++i){
        for(int j = 0; j < 10-i-1; ++j){
            if(array[j] > array[j+1]){
                tmp = array[j];
                array[j] = array[j+1];
                array[j+1] = tmp;
            }
        }
    }
    
    printf("排序后:");
    for (int i = 0; i < 10; ++i){
        printf("%d ", array[i]);
    }
    puts("");
}

按照块读写文件

写文件fwrite

c
#include <stdio.h> 
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

功能:以数据块的⽅式给文件写⼊内容。

参数

  • ptr :准备写⼊文件数据的地址
  • sizesize_tunsigned int类型,此参数指定写⼊文件内容的块数据⼤⼩
  • nmemb :写⼊文件的块数,写⼊文件数据总⼤⼩为:size * nmemb
  • stream :已经打开的文件指针

返回值

  • 成功:实际成功写⼊文件数据的块数目,此值和 nmemb 相等
  • 失败: 0
c
typedef struct Stu{ 
    char name[50]; 
    int id; 
}Stu;

Stu s[3]; 
int i = 0;
for (i = 0; i < 3; i++){
    sprintf(s[i].name, "stu%d%d%d", i, i, i);
    s[i].id = i + 1;
}

int ret = fwrite(s, sizeof(Stu), 3, fp); 
printf("ret = %d\n", ret);

读文件fread

c
#include <stdio.h> 
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

功能:以数据块的⽅式从文件中读取内容。

参数

  • ptr :存放读取出来数据的内存空间
  • size : size_t 为 unsigned int类型,此参数指定读取文件内容的块数据⼤⼩
  • nmemb :读取文件的块数,读取文件数据总⼤⼩为:size * nmemb
  • stream :已经打开的文件指针

返回值

  • 成功:实际成功读取到内容的块数,如果此值比nmemb⼩,但⼤于0,说明读到文件的结尾
  • 失败: 0

0: 表⽰读到文件结尾。(feof())

c
typedef struct Stu{ 
    char name[50]; 
    int id; 
}Stu;

Stu s[3]; 
int ret = fread(s, sizeof(Stu), 3, fp); 
printf("ret = %d\n", ret);

int i = 0;
for (i = 0; i < 3; i++){
    printf("s = %s, %d\n", s[].id);
}

强化训练

大文件拷贝

实现
c

文件的随机读写

c
#include <stdio.h> 
int fseek(FILE *stream, long offset, int whence);

功能:移动文件流(文件光标)的读写位置。

参数

  • stream :已经打开的文件指针
  • offset :根据whence来移动的位移数(偏移量),可以是正数,也可以负数,如果正 数,则相对于whence往右移动,如果是负数,则相对于whence往左移动。如果向前移动 的字节数超过了文件开头则出错返回,如果向后移动的字节数超过了文件末尾,再次写 ⼊时将增⼤文件尺⼨。 whence :其取值如下:
    • SEEK_SET :从文件开头移动offset个字节
    • SEEK_CUR :从当前位置移动offset个字节
    • SEEK_END :从文件末尾移动offset个字节

返回值

  • 成功:0
  • 失败: -1
c
#include <stdio.h> 
long ftell(FILE *stream);

功能:获取文件流(文件光标)的读写位置。

参数

  • stream :已经打开的文件指针

返回值

  • 成功:当前文件流(文件光标)的读写位置
  • 失败: -1
c
#include <stdio.h> 
void rewind(FILE *stream);

功能:把文件流(文件光标)的读写位置移动到文件开头。

参数

  • stream :已经打开的文件指针

返回值:⽆返回值

c
typedef struct Stu{ 
    char name[50]; 
    int id; 
}Stu;

//假如已经往文件写⼊3个结构体 
//fwrite(s, sizeof(Stu), 3, fp);

Stu s[3]; 
Stu tmp; 
int ret = 0;

//文件光标读写位置从开头往右移动2个结构体的位置 
fseek(fp, 2 * sizeof(Stu), SEEK_SET);

//读第3个结构体 
ret = fread(&tmp, sizeof(Stu), 1, fp); 
if (ret == 1){ 
    printf("[tmp]%s, %d\n", tmp.name, tmp.id); 
}

//把文件光标移动到文件开头 
//fseek(fp, 0, SEEK_SET); 
rewind(fp);

ret = fread(s, sizeof(Stu), 3, fp); 
printf("ret = %d\n", ret);

int i = 0;
for (i = 0; i < 3; i++){
    printf("s === %s, %d\n", s[i].name, s[i].id);
}