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