文件输入输出

目录

1.认识文件

1.1什么是文件

1.2    IO:IN & OUT

1.3从标准IO到文件IO

2.文本文件IO

2.1文件打开与检查: fopen

2.2文件路径

2.3文件打开模式

2.4读写方式

2.4.1从文本文件中读: fscanf

2.4.2往文本文件中写: fprintf

2.4.3判断是否读取至文件末尾: feof

2.4.4往文本文件中追写

2.5例题

3.二进制文件IO

3.1二进制文件

3.2二进制文件读写: fread / fwrite

3.3例题


 

1.认识文件

1.1什么是文件

任何外部数据源都是文件(于操作系统而言)
和运行的程序(内存数据)不同,文件中的数据是持久化的,断电也存在
但本质仍是 数据 (二进制字节块/字节流)

1.2    IO:IN & OUT

程序 = 数据 + 指令
数据需要输入 input 和输出 output

1.3从标准IO到文件IO

我们熟悉使用 printf 和 scanf 等 库函数 用以控制台IO
控制台本质也是一个外部数据源,换言之文件IO和控制台IO是类似的
即学习文件IO就是学习一系列 C语言文件操作 的函数的用法/效果
#include <stdio.h>
scanf(“%d”, &a);
printf(“%d”, a);
#include <stdio.h>
FILE *fp = fopen("data.txt","w+");//文件打开
if ( NULL == fp ) { // 省略... }//文件检查
fscanf( fp,"%d", &a);//从文件读
fprintf( fp,"%d", a);//往文件写
fclose(fp)//文件关闭

2.文本文件IO

2.1文件打开与检查: fopen

文件是外存上的实体,在C程序中用一个结构体指针(FILE*)表示它
类比:同学们(文件)把学生卡(文件指针)都交给老师进行抽奖(而不是把每个学生复制一遍放进盒子里,意思是文件不可能加载到程序内存中,因为太大了,用一个指针进行访问文件,相当于用学生卡或者代表学生信息物件充当指针从而进行访问)
#include <stdio.h>
// ... FILE *fp = fopen("data.txt","w+");
if ( NULL == fp )
{
    return 0;
}
函数 fopen(文件路径, 打开模式) 返回该路径的文件指针
文件路径, 打开模式都是 字符串,所以要用双引号
由于目录权限、占用等原因, fopen 不一定成功
因此在通过文件指针读写文件之前要 检查 其不为空指针
NULL 是C语言关键字常量,本质是整数0,表示空指针

2.2文件路径

相对路径:以 程序执行路径 为基准的路径
绝对路径:以操作系统 根目录 为基准的路径
FILE *fp = fopen("..\\data.txt","w+");
C:
/Code
    data.txt
    /bin
        a.exe
c盘code下有两个文件,一个data.txt一个bin文件夹
./ 表示当前目录
../ 表示相对当前路径的上一级目录
\表示转义字符,\\在转义字符\前面再加一个\才表示\本身的含义
FILE *fp = fopen("..\\files\\data.txt","w+");
C:
/Code
    /files
        data.txt
    /bin
        a.exe
FILE *fp = fopen(".\\data.txt","w+");
FILE *fp = fopen("data.txt","w+");
C:
/Code
    data.txt
    a.exe
.\\ 表示当前目录
..\\ 表示相对当前路径的上一级目录
FILE *fp = fopen("C:\\Code\\files\\data.txt","w+");
C:
/Code
    /files
        data.txt
    /bin
        a.exe
绝对路径从系统根目录(盘符)开始

2.3文件打开模式

r : r ead 读
w : w rite 写
a : a ppend 追加
FILE *fp = fopen("data.txt" , "打开模式字符串" );
打开模式字符串= 包括:①权限 ②方式
1.读写 权限
FILE *fp = fopen("data.txt" , " r ");
只读打开, 文件必须存在
FILE *fp = fopen("data.txt" , " w ");
写入打开,文件不存在则新建,存在则 抹掉从头写,形成一个新文件,不能读
FILE *fp = fopen("data.txt" , " a ");
追加打开,文件不存在则新建,存在则 从最后开始追写,不能读
FILE *fp = fopen("data.txt" , " r+ ");
给r添加写权限,文件必须存在,可以修改(写/ )原有内容
FILE *fp = fopen("data.txt" , " w+ ");
给w添加读权限,文件不存在则新建,存在则抹掉, 可读
FILE *fp = fopen("data.txt" , " a+ ");
给a添加读权限,文件不存在则新建,存在则 从最后开始追写,可读

2.4读写方式

FILE *fp = fopen("data.txt" , "r");
FILE *fp = fopen("data.txt" , "r b ");
FILE *fp = fopen("data.txt" , "w b ");
FILE *fp = fopen("data.txt" , "r+ b ");
默认以文本(text)的方式
通过 b 指明以二进制( binary )的方式打开

2.4.1从文本文件中读: fscanf

从文本文件中读内容等效于从控制台中读取用户输入的相同内容
只不过,使用 f scanf 函数并 指定文件指针
items.txt :
10, 3.5, milk
3, 18.0, book
21, 1.5, tissue
如果从控制台读:
int  c;
float p;
char name[100];
for (int i = 0; i < 3; i += 1) 
{
    scanf(“%d, %f, %s”, &c, &p, name);
    printf(“%d, %.2f, %s\n”, c, p, name);
}
如果从 items.txt 读:
int c;
float p;
char name[100];
FILE *fp = fopen("items.txt","r");
for (int i = 0; i < 3; i += 1)
{
    fscanf(fp,"%d, %f, %s", &c, &p, name);
    printf("%d, %.2f, %s\n", c, p, name);
}

2.4.2往文本文件中写: fprintf

往文本文件中写内容等效于往控制台输出内容
只不过,使用 f printf 函数并 指定文件指针
往 items.txt 写:
int c[3] = {10, 3, 21};
float p[3] = {3.5, 18.0, 1.5};
char name[3][100] = {"milk","book","tissue"};
FILE *fp = fopen("items.txt","w");
for (int i = 0; i < 3; i += 1) 
{
    fprintf(fp,"%d, %.2f, %s\n", c[i], p[i], name[i]);
}
运行后 items.txt 内容:
10, 3.5, milk
3, 18.0, book
21, 1.5, tissue

2.4.3判断是否读取至文件末尾: feof

• 文件指针随着读写而在文件中不同位置前后移动
在读文件时,文件指针移动到 文件末尾 就意味读取完成了文件所有内容
使用专门的函数 feof 进行此判断
items.txt :
10, 3.5, milk
3, 18.0, book
21, 1.5, tissue
从 items.txt 读:
int c;
float p;
char name[100];
FILE *fp = fopen("items.txt","r");
while ( !feof(fp) ) 
{
    fscanf(fp,"%d, %f, %s", &c, &p, name);
    printf("%d, %.2f, %s\n", c, p, name);
}
//会移动文件指针 fp

2.4.4往文本文件中

往文本文件中追写内容需要在打开模式字符串中包含字母 ‘a’
会从文件的原有内容的 最末尾后 继续写入
追写模式实质是打开文件后,将文件指针置于文件末尾
items.txt :
10, 3.5, milk
3, 18.0, book
21, 1.5, tissue
往 items.txt 追写:
int c[3] = {10, 3, 21};
float p[3] = {3.5, 18.0, 1.5};
char name[3][100] = {"milk","book","tissue"};
FILE *fp = fopen("items.txt,"a");
for (int i = 0; i < 3; i += 1)
{
    fprintf(fp,"%d, %.2f, %s\n", c[i], p[i], name[i]);
}
运行后 items.txt 内容:
10, 3.5, milk
3, 18.0, book
21, 1.5, tissue
10, 3.5, milk
3, 18.0, book
21, 1.5, tissue

2.5例题

Key 1 :通过 fopen 函数用指定模式打开指定路径的文件,获得一个 FILE* 指针
Key 2 :文件打开模式包括:①权限 ②方式,以字符串的形式传递给 fopen 函数
①权限 ②方式=先研究能不能进这栋大厦,进去了再思考是走楼梯还是坐电梯吧
1. 程序路径为 D:\prj\bin\test.exe,要打开日志 D:\prj\log\20221011.log 进行内容的追加应当使用:
     ___FILE*___ fp = fopen__("..//..//log//20221011.log","a" )_________________
分析:相对路径中 ../ 表示上一级目录,../../ 表示上两级目录,以此类推,也可以写绝对路径 “D:\\prj\\log\\20221011.log
2. 如果需要打开一个已经存在的非空文本文件 "FILE" 并进行修改,正确的语句是( D)
A. FILE *fp = fopen("FILE" , "r");
B. FILE *fp = fopen("FILE" , "a+");
C. FILE *fp = fopen("FILE" , "w+");
D. FILE *fp = fopen("FILE" , "r+");
分析:文件打开权限中,最难理解的就是”r+” :其中 r 表明这个文件是存在的,现在要打开读取其内容,因此文件指针一开始会置于文件最开头; + 表示同时需要写权限,随着读写操作导致的文件指针移动,可以在任意一处进行内容的写入( 覆盖原有文本 ),起到修改的效果。

3. 关于文件打开模式,说法正确的是(C )
A. “r”必须打开已有文件,想新建文件需要使用”r+”
B. “w”用于已有文件表示在其后追写内容
C. “a+”模式的+表示”a”基础上增加上读取权限
D. “w+”模式适合用于修改已有文件
分析:想要新建文件要有w,修改已有文件的是r+

Key 3 :通过 fscanf 和 fprintf 读写文本文件,其效果等同于控制台IO
1. 文件 f.txt 内首行是一个整数,表明接下来由空格分隔的小数的数量,正确加载这些数据的是( B)
A. 
FILE *fp = fopen("f.txt","r");
if (!fp) return 0;
int n;
scanf(fp,"%d", &n);
float *p = malloc(4 * n);
for (int i = 0; i < n; i += 1)
fscanf("%f", p + i);
free(p);
fclose(fp);
FILE *fp = fopen("f.txt","r");
if (!fp) return 0;
int n;
fscanf(fp,"%d", &n);
float *p = malloc(4 * n);
for (int i = 0; i < n; i += 1)
fscanf("%f", &p[i]);
free(p);
fclose(fp);
FILE *fp = fopen("f.txt","r");
if (fp) return 0;
int n;
fscanf(fp,"%d", &n);
float *p = malloc(4 * n);
for (int i = 0; i < n; i += 1)
fscanf("%f", p + i);
free(p);
fclose(fp);
FILE *fp = fopen("f.txt","w");
if (!fp) return 0;
int n;
fscanf(fp,"%d", &n);
float *p = malloc(4 * n);
for (int i = 0; i < n; i += 1)
fscanf("%f", p + i);
free(p);
free(fp);
分析:条件语句为!fp,因为当fp为0时返回0,为假时想进入语句,用取反,内存开辟就要释放,文件指针不需要释放,但要关闭文件。
2. 文本文件DATA.txt的原始内容如下所示,执行该段代码后,文件的内容变为( D)

DATA.txt:
hello, world
FILE *fp = fopen("DATA.txt","r+");
if (!fp) return 0;
char str[100] = "HELLO";
fprintf(fp,"%s", str);
A. hello, world
B. hello, worldHELLO
C. HELLO
D. HELLO, world
分析:以”r+”模式打开文件,若文件存在则打开后文件指针放置于文件开头,能读能写。因此打开DATA.txt后直接写入一个字符串将从头开始覆写原有内容。

3.二进制文件IO

3.1二进制文件

思考:通过文本文件存储整数 123456789 需要多少空间?(需要九字节,文本文件存储原则是一个字节一个字符,9个数字分别占一个字节)
在文本文件中,将被存放成字符串“123456789” ,每字符1字节,共占9个字节
但它本来是个int,只占4字节呀!为什么不 直接把这4字节写入文件 就好了
文本文件:对数据进行ASCII编码后,按字节 逐字符 写入/读取
二进制文件:直接将内存二进制数据 整体搬入 文件,要解读需要知道其 特定格式
可以尝试用记事本打开一个.mp3文件,或 者.jpg文件,.ppt文件,.mp4文件...等,会发现出现一对乱码,因为每个文件有特殊的读取方式
FILE *fp = fopen("data.bin","wb");
if (!fp) { return 0; }
int a = 999999; char b =‘a’; float c = 12.34f;
fwrite(&a, sizeof(int), 1, fp);
fwrite(&b, sizeof(char), 1, fp);
fwrite(&c, sizeof(float), 1, fp);
FILE *fp = fopen("data.bin","rb");
if (!fp) { return 0; }
int a; char b; float c;
fread(&a, sizeof(int), 1, fp);
fread(&b, sizeof(char), 1, fp);
fread(&c, sizeof(float), 1, fp);
printf("%d, %c, %.2f", a, b, c);

3.2二进制文件读写: fread / fwrite

直接把二进制文件读写理解成 字节块搬家 即可, fwrite 搬进去, fread 搬出来
•fwrite( 待写入数据指针,数据单元大小,数据个数,fp )
fread( 存放数据的指针,数据单元大小,数据个数,fp )
数据单元大小与数据个数 乘积为总读(写)字节数
int a[3] = {1, 2, 3};
float b[2] = {1.7f, 3.14f};
FILE *fp = fopen("data.bin","wb");
if (!fp) { return 0; }
fwrite(a, sizeof(int), 3, fp);
fwrite(b, sizeof(b[0]), sizeof(b) / sizeof(b[0]), fp);
int a[3];
float b[2];
FILE *fp = fopen("data.bin","rb");
if (!fp) { return 0; }
fread(a, sizeof(int), 3, fp);
fread(b, sizeof(float), 2, fp);

3.3例题

Key 1 :二进制文件的本质是数据的原生字节块,需要知道对应的编码方式才能读取
Key 2 :将数据存入二进制中使用fwrite,读取使用fread,指明类型和数量即可
1. 下面是通过二进制模式读写结构体数据的两段代码,代码行及其描述正确的是( C)
A. 行 [b] 的作用是检测文件是否已经存在
B. 行 [d] 中的 1 指的是待写入数据所占内存的字节数
C. 行 [2] 中的 “rb” 指的是以只读权限和二进制方式打开文件
D. 行 [4] 中的 sizeof(s) 是获得待读取数据的字节总数
typedef struct Student
{
    int age;
    float score;
    char name[20];
}S;
[a] S s;
[b] FILE *fp = fopen("student.dat","wb");
[c] if (!fp) { return 0; }
[d] fwrite(&s, sizeof(s), 1, fp);
[e] fclose(fp);
[1] S s;
[2] FILE *fp = fopen("student.dat","rb");
[3] if (!fp) { return 0; }
[4] fread(&s, sizeof(s), 1, fp);
[5] fclose(fp);
3. 执行完左侧的代码段后,执行右侧代码段的输出是( B)
注:1094795585 (10) = 41414141 (16) = 01000001,01000001,01000001,01000001 (2)
FILE *fp = fopen("data.bin","wb");
unsigned n = 0x41414141;
fwrite(&n, sizeof(n), 1, fp);
fclose(fp);
FILE *fp = fopen("data.bin","r");
char ch[20];
fscanf(fp,"%s", ch);
printf("%s", ch);
fclose(fp);
A. 1094795585
B. AAAA
C. 01000001010000010100000101000001
D. 程序会报错,因为文件的写入和读取方式不匹配
分析:变量n实际字节内容就是01000001010000010100000101000001,二进制方式会将这4个字节直接写入文件,即文件实际存放的就是 这4字节的内容。而右侧代码是通过文本方式读文件,会把这四个字节理解成一个一个的字符(文本),即按ASCII码进行解码,得到的结果为字 符串“AAAA” ,因为字符'A'的ASCII码为65(10) = 01000001 (2)
文件读写字节数目必须匹配,实质就是字节搬家,文本文件是一个字节一个字符
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值