【C5】读写文件



2021年12月04日    Author:Guofei

文章归类: C    文章编号: 10005

版权声明:本文作者是郭飞。转载随意,但需要标明原文链接,并通知本人
原文链接:https://www.guofei.site/2021/12/04/c5.html


fopen和fclose

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

FILE *p = fopen("./a.txt", "r");
// 如果打开失败,p = NULL;

// 如果打开成功,需要关闭。(如果打开失败,关不关都行)
int fclose(*p);

mode

  • r 读,必须存在
  • r+ 读写,必须存在
  • rw+ 读写
  • w 写,不存在则建立,存在则清空
  • w+ 读写,不存在则建立,存在则清空
  • a 附加写入
  • a+ 读写。不存在会创建,存在会在末尾附加

b 在 windows 下,用于操作 \r\n

getc和putc读写char

// 不需要放到代码里面
#define EOF -1; // 文件结尾的标识,它不是文件的一部分内容,文本文件可以用,二进制文件不能,二进制用feof。

读内容

int main() {
    FILE *p = fopen("./a.txt", "r");
    if (p) {
        printf("open success\n");

        char c=getc(p);
        while (c!=EOF){
            printf("%c",c);
            c=getc(p);
        }

    } else {
        printf("open fail\n");
    }

    printf("\n");
    fclose(p);
}

实现一个读内容的函数

int my_read(int argc, char **args) {
    if (argc < 2)
        return 0;

    FILE *p = fopen(args[1], "r");
    if (p != NULL) {
        char c = getc(p);
        while (c != EOF) {
            printf("%c", c);
            c = getc(p);
        }
        printf("\n");

    } else {
        printf("fail!\n");
    }
    fclose(p);
    return 0;
}

int main(int argc, char **args) {
    return my_read(argc, args);
}

实现一个写内容的函数

int my_write(unsigned long len_text, char *text, char *filename) {
    FILE *p = fopen(filename, "w");

    if (p) {
        for (int i = 0; i < len_text; i++) {
            printf("%c\n", text[i]);
            putc(text[i], p);
        }
        fclose(p);
    } else {
        printf("read fail!\n");
    }

    return 0;
}

int main() {
    char *text = "abc";
    char *filename = "./a.txt";
    my_write(strlen(text), text, filename);
    return 0;
}

练习

做一个文件拷贝代码

int main() {

    FILE *p1 = fopen("./a.txt", "r");
    FILE *p2 = fopen("./b.txt", "w");

    if ((p1 != NULL) && (p2 != NULL)) {
        char c = getc(p1);
        while (c != EOF) {
            putc(c, p2);
            c = getc(p1);
        }
    }
}

练习:做一个加密代码:
思路:保存前c=c+1; 加密 c=c-1; 解密

fprint和fputs(最有用)

前面写过,打印字符串和获取用户输入可以这么做

fgets(a, sizeof(a), stdin);
fputs(a, stdout);

他们还能用来读写文本文件:

写入文件

// 写入文件
FILE *p = fopen("./a.txt", "w");

if (p) {
    fputs("hello world,你好!", p);
    fclose(p);
} else { printf("read fail\n"); }

读取文件(读一行)

FILE *p = fopen("./a.txt", "r");
char buf[1024] = {};
fgets(buf, sizeof(buf), p); // 这里一次读取一行
printf("%s", buf);

读取文件(读多行)

// feof(p) 当前遇到结尾返回 1,否则返回 0
int main() {

    FILE *p = fopen("./a.txt", "r");
    char buf[1024] = {};
    if (p == NULL) {
        printf("read fail!\n");
        return 0;
    }
    while (!feof(p)) { // 判断是否已到文件结尾
        fgets(buf, sizeof(buf), p); //读一行,buf的末尾会包含\n
        printf("%s", buf);

    }

    return 0;
}

fcanf和fprintf

我们知道,

  • scanf 是从字符串中读取,并按照指定格式提取某些值。
  • printf 是按照指定格式打印某些值

类似的

  • fscanf 是从文件中读取,并按照指定格式提取某些值。
  • fprintf 是按照指定格式把某些值写到文件中

例子:

// a.txt的样子:  12+35=
FILE *p1 = fopen("./a.txt", "r");
FILE *p2 = fopen("./b.txt", "w");


int a;
char b;
int c;
fscanf(p1, "%d%c%d=", &a, &b, &c);
fprintf(p2, "%d%c%d=%d", a, b, c, a + c);


printf("%d%c%d=", a, b, c);

stat:获取文件本身的属性

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main() {
    struct stat st;
    stat("./a.txt", &st);
    printf("size = %lld \n", st.st_size);
}

(还可以打印其它很多信息,有时间再研究)

fread和fwrite二进制的方式

size_t fread(&a, size_t size, size_t nitems, p);
size_t fwrite(&a, size_t sizeof(a), size_t nitems, p);
FILE *p = fopen("b.txt", "r");
int a = 0;
do {
    fread(&a, sizeof(int), 1, p);
    printf("%d\n", a);
} while (!feof(p));
fclose(p);

要点

  • 或者 char,每次读 1 个 Byte。
  • 对比之下,如果是 int,就读 4 个 Byte,而且是小端对齐。
  • fread 返回什么?
    • 实际读取的字节数/size,这里还是整除
    • 因此这个数字最多是 nitems
    • 小于 nitems 意味着读到了文件末尾
    • 最少是0
    • 注意是整除,因此返回0也有可能读入信息
  • fread 还可以读写结构体、结构体数组。

文件指针相关

上面的读写函数可以改变文件指针的位置,下面的函数也与文件指针有关。

fseek

file 内部是有个指针的

  • fseek,用来改变指针的位置
  • 如果执行失败,不改变指向的位置,返回0
  • ???这个似乎不同系统不一样。如果向后超过末尾,或者向前超过首位置,也返回0
int fseek(p, long, int);
// 第2个参数是offset偏移量(单位字节)
// 第3个参数:SEEK_SET 文件开头,SEEK_CUR 文件当前位置,SEEK_END 文件结尾

用 fseek 可以很方便的一个超大文件

FILE *p = fopen("a.mp4", "w");
fseek(p,10000,SEEK_SET);
char a=0;
fwrite(&a,sizeof(a),1,p);
fclose(p);

ftell

返回当前指针的位置

long ftell(p);

应用:得到文件的大小

fseek(p, 0, SEEK_END);
int size = ftell(p);

fflush

把内容写入到磁盘中

fflush(p);

尽量少用,影响磁盘寿命,还影响性能

remove和rename

remove("a.txt");
rename("old.txt", "new.text");

练习题

题目1

  1. 生成一个文件,里面有100万个0到255的随机数,
  2. 研发一个排序算法,对这个文件排序,然后写回去
    • 只准用栈,不准用堆

题目2:实现一个简单的文件加密代码

int main(int argc, char **args) {
    if (argc < 3) {
        return 0;
    }

    FILE *p1 = fopen(args[1], "r");
    FILE *p2 = fopen(args[2], "w");

    char c = getc(p1);
    while (c != EOF) {
        putc(c^35, p2);
        c = getc(p1);
    }

    fclose(p1);
    fclose(p2);
}

题目3:有一个文件,里面有多行加减乘除算式。写一个程序,把算式和结果写到另一个文件中。每行算式长度不超过100

int cal(a, b, c) {
    switch (b) {
        case '+':
            return a + c;
        case '-':
            return a - c;
        case '*':
            return a * c;
        case '/':
            return a / c;
        default:
            return -1;
    }
}

int main(int argc, char **args) {
    if (argc < 3) {
        return 0;
    }

    FILE *p1 = fopen(args[1], "r");
    FILE *p2 = fopen(args[2], "w");

    int a;
    char b;
    int c;
    while (!feof(p1)) {
        fscanf(p1, "%d%c%d=\n", &a, &b, &c);
        fprintf(p2, "%d%c%d=%d\n", a, b, c, cal(a, b, c));
    }
    fclose(p1);
    fclose(p2);
}

题目4:print和scan相关的函数很多,整理成一个表,方便使用

  • 解决这个问题:一个文件是格式化的 “姓名=张三,年龄=20” 这种,提取其中的姓名和年龄
  • 打印年龄第2大的人的姓名
#include<stdio.h>
#include <stdlib.h>
#include <string.h>

int cal(a, b, c) {
    switch (b) {
        case '+':
            return a + c;
        case '-':
            return a - c;
        case '*':
            return a * c;
        case '/':
            return a / c;
        default:
            return -1;
    }
}

struct student {
    char name[20];
    int age;
};
typedef struct student std;

int main(int argc, char **args) {
    if (argc < 3) {
        return 0;
    }

    FILE *p1 = fopen(args[1], "r");
    FILE *p2 = fopen(args[2], "w");

    char a[20];
    char *name;
    char *age;
    int age_;
    char *stopstring;
    std *st = calloc(1, sizeof(std));

    int cnt_num = 0;
    std *pointer = st;
    while (!feof(p1)) {
        char buf[1024] = {};
        fgets(buf, sizeof(buf), p1);

        strtok(buf, "=");
        name = strtok(NULL, ",");
        if (name == NULL) {
            break;
        }
        strtok(NULL, "=");
        age = strtok(NULL, "=");
        printf("%s\n", name);
        age_ = strtol(age, &stopstring, 10);
        printf("%d\n", age_);

        strcpy(pointer->name, name);
        pointer->age = age_;
        pointer++;
        cnt_num++;
    }
    fclose(p1);
    fclose(p2);


//    求第二大

    int first;
    int second;
    if ((st->age) > (st + 1)->age) {
        first = 0;
        second = 1;
    } else {
        first = 1;
        second = 0;
    }

    for (int i = 2; i < cnt_num; i++) {
        printf("max name=%s,age=%d\n", (st + i)->name, (st + i)->age);

        if (((st + i)->age) > (st + first)->age) {
            second = first;
            first = i;
        } else if (((st + i)->age) > (st + second)->age) {
            second = i;
        }
    }
    printf("%d\n", second);
    printf("max name=%s,age=%d\n", (st + second)->name, (st + second)->age);
}

end


您的支持将鼓励我继续创作!