【C5】读写文件
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
- 生成一个文件,里面有100万个0到255的随机数,
 - 研发一个排序算法,对这个文件排序,然后写回去
    
- 只准用栈,不准用堆
 
 
题目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
您的支持将鼓励我继续创作!