文章目录

(十二) 文件1.流(1)流模型(2)程序员视角的文件(3)缓冲区类型(4)标准流(5)二进制文件 与 文本文件(6)文件流的接口(API)

2.打开/关闭文件(1)fopen(2)fclose(3)示例代码

3.读/写文件(1)fgetc / fputc:读写文本文件,一个字符一个字符地读写(2)fgets / fputs:一行一行地读写①fgets②fputs③代码

(3)fscanf / fprintf:格式化地读写①fscanf②fprintf③代码

(4)fread / fwrite:读写二进制文件,一块一块地读写①fread②fwrite③代码

4.文件定位、移动文件位置(1)fseek:移动文件位置(2)ftell:返回当前文件位置(3)rewind:将文件位置移回开头(4)代码实战

5.错误处理:perror

(十二) 文件

1.流

(1)流模型

data sink:数据接收端、数据汇

优点: ①程序员读写文件时,不需要关心文件的位置 ②数据源(data source) 和 数据汇(data sink) 是解耦的

(2)程序员视角的文件

存放的是一个一个字节。 EOF(end of file)指向文件末尾后一个位置,EOF是一个宏,值为-1

(3)缓冲区类型

①满缓冲:缓冲区空才从输入流读数据;缓冲区满向输出流中写入数据。 ②行缓冲:以行为单位进行读和写 ③无缓冲:没有缓冲区,立即输入输出,例如:标准错误流 stderr

刷新输出缓冲区 (fflush),将输出缓冲区的内容输出到屏幕上

(4)标准流

①stdin:标准输入 ②stdout:标准输出 ③stderr:标准错误

这三个标准流,不需要程序员手动声明、创建、关闭

(5)二进制文件 与 文本文件

1.区别 (1)二进制文件:byte。(二进制文件以字节为单位,人类不可读,但体积小)

(2)文本文件:字符 + 编码。(文本文件以字符为单位,一个字符占几个字节)

2.优缺点: 文本文件:人类可读,数据量大 二进制文件:人类不可读,数据量小

(6)文件流的接口(API)

1.打开文件流 :fopen

2.读/写文件: 统计、转换、加密解密

2.5 移动文件位置

3.关闭文件流:fclose

2.打开/关闭文件

(1)fopen

1.fopen的参数 ①filename是文件的路径 ②mode是打开模式

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

2.文件路径 (1)绝对路径 从根目录 (或者盘符) 开始,一直到文件所在的位置,比如:“c:/project/test.dat”。 (2)相对路径 另一种是相对路径:从当前工作目录开始,一直到文件所在的位置,比如:“in.dat”。

相对路径用的多,因为一个app各文件的相对路径一般不变,但是绝对路径,当安装到不同电脑上时一般不同。

3.打开模式(mode):文件的类型、对文件的操作(r,w) (1)以文本文件方式打开 ①“r”,读(read):要求文件存在。若不存在则返回NULL ②“w”,写(write):若文件存在,清空文件内容;若文件不存在,创建文件。 ③“a”,追加(append):若文件存在,不修改原内容,每次都在文件末尾追加写入;若文件不存在,创建文件。

文件存在文件不存在r只读返回NULLw清空文件内容创建文件a追加创建文件

(2)以二进制文件打开

(2)fclose

fclose 可以关闭程序不再使用的文件。

int fclose(FILE* stream);

如果成功关闭, fclose 返回零;否则返回 EOF

(3)示例代码

#define _CRT_SECURE_NO_WARNINGS

#include

int main(void) {

//1.打开文件

FILE* stream = fopen("a.txt", "w");

if (stream == NULL) {

fprintf(stderr, "file open failed.\n");

exit(1);

}

//2.读写文件 (统计,转换,加密,解密)

//3.关闭文件

fclose(stream);

return 0;

}

3.读/写文件

(1)fgetc / fputc:读写文本文件,一个字符一个字符地读写

1.函数参数

int fgetc(FILE* stream);

int fputc(int c, FILE* stream);

2.惯用法:一个字符一个字符地读取,直到文件末尾

int c;

while((c = fgetc(src)) != EOF){

//操作

}

3.完整代码:

#define _CRT_SECURE_NO_WARNINGS

#include

#include

int main(int argc, char* argv[]) {

//xxx.exe src dst

//0.参数校验

if (argc != 3) {

fprintf(stderr, "Usage: %s src dst\n", argv[0]);

exit(1);

}

//1.打开文件

FILE* src = fopen(argv[1], "r");

if (!src) {

fprintf(stderr, "Open %s failed.\n", argv[1]);

exit(1);

}

FILE* dst = fopen(argv[2], "w");

if (!dst) {

fprintf(stderr, "Open %s failed.\n", argv[2]);

fclose(src);

exit(1);

}

//2.读写文件 (统计,转换,加密,解密)

//(1)一个字符一个字符地读写:fgetc, fputc

//把大写字母转换为小写字母

int c;

while ((c = fgetc(src)) != EOF) {

fputc(tolower(c), dst);

}

printf("大写字母已全部转换为小写字母\n");

//3.关闭文件

fclose(src);

fclose(dst);

return 0;

}

(2)fgets / fputs:一行一行地读写

fgetc的c是character,fgets的s是string

①fgets

1.函数参数

char* fgets(char* str, int count, FILE* stream);

参数: ①str:指向第一个字符数组 ②count:能够写入的最大字符数量 (通常是str指向字符数组的长度) ③stream:输入流 ④返回值:成功返回str,失败返回NULL

2.fgets的特点 ①fgets会读\n ②gets(str) 等价于 fgets(str, ∞, stdin) ,即gets不会检查数组越界

②fputs

1.函数参数

int fputs(const char* str, FILE* stream);

参数: ①str:要写的字符串(以’\0’结尾的字符串) ②stream:输出流 ③返回值:成功返回一个非负值;失败返回EOF,并设置errno

2.fputs的特点 ①fputs原样输出字符串,puts在字符串后多输出一个换行符’\n’ ②puts(str) 等价于 fputs(str, stdout)

③代码

1.核心代码:

#define MAXLINE 128

//2.读写文件:

//(2)一行一行地读写

//每行加序号

char line[MAXLINE];

char buffer[MAXLINE];

//fgets(line, MAXLINE, src);

//fgets(line, MAXLINE, src);

int line_num = 1;

while ((fgets(buffer, MAXLINE, src)) != NULL) {

sprintf(line, "%d.%s", line_num, buffer);

fputs(line, dst);

line_num++;

}

2.完整代码:

#define _CRT_SECURE_NO_WARNINGS

#include

#include

#define MAXLINE 128

int main(int argc, char* argv[]) {

//xxx.exe src dst

//0.参数校验

if (argc != 3) {

fprintf(stderr, "Usage: %s src dst\n", argv[0]);

exit(1);

}

//1.打开文件

FILE* src = fopen(argv[1], "r");

if (!src) {

fprintf(stderr, "Open %s failed.\n", argv[1]);

exit(1);

}

FILE* dst = fopen(argv[2], "w");

if (!dst) {

fprintf(stderr, "Open %s failed.\n", argv[2]);

fclose(src);

exit(1);

}

//2.读写文件:

//(2)一行一行地读写

//每行加序号

char line[MAXLINE];

char buffer[MAXLINE];

//fgets(line, MAXLINE, src);

//fgets(line, MAXLINE, src);

int line_num = 1;

while ((fgets(buffer, MAXLINE, src)) != NULL) {

sprintf(line, "%d.%s", line_num, buffer);

fputs(line, dst);

line_num++;

}

printf("每行内容前面已加上序号。\n");

//3.关闭文件

fclose(src);

fclose(dst);

return 0;

}

(3)fscanf / fprintf:格式化地读写

①fscanf

int fscanf(FILE* stream, const char* format, ...);

②fprintf

int fprintf(FILE* stream, const char* format, ...);

③代码

#define _CRT_SECURE_NO_WARNINGS

#include

#include

#define MAXLINE 128

typedef struct Student {

int id;

char name[25];

char gender;

int chinese;

int math;

int english;

} Student;

int main(int argc, char* argv[]) {

//xxx.exe src dst

//0.参数校验

if (argc != 3) {

fprintf(stderr, "Usage: %s src dst\n", argv[0]);

exit(1);

}

//1.打开文件

FILE* src = fopen(argv[1], "r");

if (!src) {

fprintf(stderr, "Open %s failed.\n", argv[1]);

exit(1);

}

FILE* dst = fopen(argv[2], "w");

if (!dst) {

fprintf(stderr, "Open %s failed.\n", argv[2]);

fclose(src);

exit(1);

}

//2.读写文件 (统计,转换,加密,解密)

//(3)格式化地读写

Student students[5];

for (int i = 0; i < 5; i++) {

fscanf(src, "%d%s %c%d%d%d",

&students[i].id,

students[i].name,

&students[i].gender,

&students[i].chinese,

&students[i].math,

&students[i].english);

}

//修改成绩

for (int i = 0; i < 5; i++) {

students[i].chinese *= 10;

students[i].math *= 10;

students[i].english *= 10;

}

for (int i = 0; i < 5; i++) {

fprintf(dst, "%d %s %c %d %d %d\n",

students[i].id,

students[i].name,

students[i].gender,

students[i].chinese,

students[i].math,

students[i].english);

}

printf("成绩已修改。\n");

//3.关闭文件

fclose(src);

fclose(dst);

return 0;

}

//a.txt

1 Edward M 100 100 100

2 Amber F 99 99 99

3 Sam M 98 98 98

4 Windy F 97 97 97

5 Chole F 96 96 96

(4)fread / fwrite:读写二进制文件,一块一块地读写

①fread

②fwrite

③代码

1.核心代码:

//读写二进制文件 (复制)

char buffer[BUFSIZE];

int bytes;

while ((bytes = fread(buffer, 1, BUFSIZE, src)) > 0) {

fwrite(buffer, 1, BUFSIZE, dst);

}

2.完整代码:

#define _CRT_SECURE_NO_WARNINGS

#include

#define BUFSIZE 4096

int main(int argc, char* argv[]) {

//xxx.exe src

//0.参数校验

if (argc != 3) {

fprintf(stderr, "Usage: %s src dst\n", argv[0]);

exit(1);

}

//1.打开文件

FILE* src = fopen(argv[1], "rb");

if (!src) {

fprintf(stderr, "Open %s failed.\n", argv[1]);

exit(1);

}

FILE* dst = fopen(argv[2], "wb");

if (!dst) {

fprintf(stderr, "Open %s failed.\n", argv[2]);

fclose(src);

exit(1);

}

//2.读写文件

//(4)读写二进制文件 (复制)

char buffer[BUFSIZE];

int bytes;

while ((bytes = fread(buffer, 1, BUFSIZE, src)) > 0) {

fwrite(buffer, 1, BUFSIZE, dst);

}

//3.关闭文件

fclose(src);

fclose(dst);

return 0;

}

4.文件定位、移动文件位置

(1)fseek:移动文件位置

int fseek(FILE* stream, long int offset, int whence);

whence的三个宏: ①SEEK_SET:文件的起始位置,0 ②SEEK_CUR:文件的当前位置,pos ③SEEK_END:文件的末尾位置,EOF

(2)ftell:返回当前文件位置

long int ftell(FILE* stream);

(3)rewind:将文件位置移回开头

void rewind(FILE* stream);

(4)代码实战

难点: ①如何确定文件的大小

#define _CRT_SECURE_NO_WARNINGS

#include

#include

char* readFile(const char* path) {

//1.打开文件

FILE* stream = fopen(path, "rb");

if (stream == NULL) { //!stream

fprintf(stderr, "Open %s failed\n", path);

exit(1);

}

//2.确定文件大小

fseek(stream, 0, SEEK_END);

long n = ftell(stream);

char* content = malloc((n + 1) * sizeof(char)); // 1 for '\0'

//3.读取文件

rewind(stream); //回到文件开头

int bytes = fread(content, 1, n, stream);

content[bytes] = '\0';

//4.关闭文件

fclose(stream);

return content;

}

int main(int argc, char* argv[]) {

//0.参数校验

if (argc != 2) {

fprintf(stderr, "Usage: %s filename\n", argv[0]);

exit(1);

}

char* content = readFile(argv[1]);

//操作

printf("%s\n", content);

free(content);

return 0;

}

5.错误处理:perror

1.如何检测错误:①② 2.如何打印错误信息:③④

①返回值

②errno,定义在 头文件中 (每个线程都有自己的errno,线程特定变量) errno为0表示没有错误,非0表示有错误

③strerror(errno),定义在 头文件中

④perror("前缀信息"),错误的前缀信息,后面自带了 相当于调用了fprintf(stderr,"%s: ,%s", prefix, strerror(errno));

#define _CRT_SECURE_NO_WARNINGS

#include

#include

#include

int main(void) {

printf("%d\n", errno); // 0:没有错误

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

printf("%d\n", errno);

printf("%s\n", strerror(errno)); //将errno对应的错误信息,转化为人类可读的字符串

perror("prefix"); //添加错误的前缀信息

return 0;

}

精彩链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。