C语言 day22
文件
- 文件的基本概念
- C语言对文件的处理
- 文件的基本练习
文件的基本概念
- 磁盘文件:指一组相关数据的有序集合,通常储存在外部介质(如磁盘),使用时才调入内存。
- 设备文件:
- 键盘:标准输入文件
- 屏幕:标准输出文件
- 其他设备:打印机、触摸屏、摄像头、音箱等
缓冲区的作用:
1、提高存取效率
2、提高磁盘的使用寿命
磁盘文件的分类
- 一个文件通常是磁盘上一段命名的存储区
- 计算机的存储在物理上是二进制的,所以物理上所有的磁盘文件本质上都是以字节
为单位进行顺序存储的 - 从用户或者操作系统的使用角度上(逻辑上)可以把文件分为:
- 文本文件:基于字符编码的文件
- 二进制文件:基于值编码的文件
文本文件
- 基于字符编码,常见编码有ASCII、UNICODE等,一般可以使用文本编辑器直接打开
例如:数5678以ASCII码的存储形式为00110101 00110110 00110111 00111000
歌词文件(lrc):文本文件
二进制文件
- 把内存中的数据按其在内存中的存储形式原样输出到磁盘上
- 一般需要自己判断或使用特定的软件分析数据格式
例如:5678的储存形式为 二进制码:00010110 00101110
音频文件(mp3):二进制文件
文本文件、二进制文件的对比(重要)
译码:
- 文本文件编码基于字符定长,译码容易些
- 二进制文件编码是变长的,译码难一些,不同的二进制文件,需要不同的译码方式,或即要使用不同的软件打开
空间利用率:
- 二进制文件用一个比特来代表一个意思(位操作)
- 文本文件任何一个符号至少需要一个字节
可读性:
- 文本文件用通用的记事本就几乎可以浏览所有文本文件
- 二进制文件需要一个具体的文件解码器,不如读图软件、音乐播放器等。
文件的打开与关闭
C语言中不能直接操作文件
只能采用库函数间接对文件进行操作C语言操作文件的基本流程
在使用文件前调用打开函数将文件打开打开文件会得到一个文件指针fp
- 调用各种有关函数,利用fp对文件进行具体操作(读或写)
- 在文件使用完后,及时电泳关闭函数来关闭文件
- C语言中所有的文件操作都围绕文件指针完成
任何文件使用之前必须打开,使用之后必须关闭
文件指针
- 文件指针的定义FILE是系统在头文件
1
FILE * 指针变量名
stdio.h
中使用typedef
定义出来的存储有关文件信息的一种结构体类型
注意:实际编程过程中,使用库函数操作文件,无需关心
FILE
结构体的细节三个特殊的文件指针(无需定义,直接可以使用)
stdio
:标准输入设备 默认为当前终端(键盘)stdout
:标准输出设备 默认为当前终端(屏幕)stderr
:标准出错 默认为当前终端(屏幕)
当我们程序出错或者使用perror函数时,信息打印在此终端
fopen打开文件
头文件:
#include <stdio.h>
函数定义:
FILE *fopen(const char *path, const char *mode);
- 第一个参数:文件的路径
- 第二个参数:打开方式
r w a + b t
返回值:成功返回相应的指针,失败返回
NULL
第二个参数的集中形式(打开文件的方式)
r
以只读的方式打开文件- 文件不存在返回
NULL
- 文件存在,返回文件指针,进行后续的读操作
- 文件不存在返回
w
以只写的方式打开- 文件不存在,以指定文件名创建此文件
- 文件存在,清空文件内容,进行写操作
- 文件打不开(权限为只读),返回
NULL
a
以追加的方式打开文件- 文件不存在,以指定文件名创建此文件(同
w
) - 文件存在,从文件的结尾处进行写操作
- 文件不存在,以指定文件名创建此文件(同
+
同时以读写方式打开指定文件- 注意与
rw
的区别
- 注意与
b
和t
b
以二进制的方式打开文件t
(可省略)以文本方式打开文件
1 | FILE *fp = NULL; |
打开方式的组合形式
fclose关闭文件
- 头文件:
#include <stdio.h>
- 函数定义:
int fclose(FILE *fp);
- 返回值:
- 关闭成功:返回0
- 关闭失败:返回非0
1 | FILE *fp=NULL; |
文件的顺序读写
- 字节读写函数:
fgetc
和fputc
- 字符串读写函数:
fgets
和fputs
- 数据块读写函数:
fread
和fwrite
- 格式化读写函数:
fscanf
和fprintf
字节的读写函数
字节的读操作fgetc
- 头文件:
#include <stdio.h>
- 函数定义:
int fgetc(FILE *stream);
- 返回值:
- 读取成功: 返回字符对应的ASCII码
- 到达文件末尾或发生读错误:返回 EOF。
1
ch=fgetc(fp);//读一个字节
- 文本文件:读到文件末尾返回EOF(EOF是个宏-1)
- 二进制文件:要使用feof判断是否到达文件末尾
字节的读操作fgetc
- 头文件:
#include <stdio.h>
- 函数定义:
int fputc(int c, FILE *stream);
- 返回值:
- 输出成功:返回输出的字节
- 输出失败:返回EOF
1
ch=fgetc(fp);//读一个字节
案例1:
1 | void test1(void) { |
运行结果:
案例2:
1 | void test2(void) { |
运行结果:
练习:从一个文件(文本文件)中读取所有信息,写入到另一个文件中
参考:r+ w+ fgetc fputc EOF
字符串的读写函数
字符串的写操作fputs
- 头文件:
#include <stdio.h>
- 函数定义:
int fputs(const char *s, FILE *stream);
- 返回值:
- 成功:返回一个非负值
- 失败:返回 EOF
- 注意:字符串的’\0’不会写入到文件中,且遇到’\0’结束
字符串的读操作
- 头文件:
#include <stdio.h>
- 函数定义:
char *fgets(char *s, int size, FILE *stream);
- 返回值:
- 成功:返回相同的 str 参数
- 到达文件末尾或者没有读取到任何字符:返回NULL
- 发生错误:返回NULL
- 注意:从
stream
文件中读取size-1
个字符,在读取过程中遇到换行符或EOF,读取结束,并读取换行符,在最后加一个’\0’。获取一行数据。
案例3:
1 | void test3(void) { |
运行结果:
案例4:
1 | void test4(void) { |
运行结果:
文件块的读写函数
文件块的写操作fwrite
- 头文件:
#include <stdio.h>
- 函数定义:
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
- 函数参数:
- 第一个参数:从哪里读取写入
- 第二个参数:一次读写的数据块大小
- 第三个参数:要读写的数据块数
- 第四个参数:要写入文件的指针
- 返回值:
- 实际读写的数据块数(不是总数据大小,重要)
- 注意:fwrite是将内存中的数据原样写入到文件中,记事本打开会出现乱码;但是数据本身是没问题的,不影响读取
文件块的读操作fread
- 头文件:
#include <stdio.h>
- 函数定义:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
- 函数参数:
- 第一个参数:读到哪里去
- 第二个参数:一次读写的数据块大小
- 第三个参数:要读写的数据块数
- 第四个参数:要写入文件的指针
- 返回值:
- 实际读写的数据块数(不是总数据大小,重要)
案例5:
1 | void test5(void) { |
运行结果:
案例6:
1 | void test6(void) { |
运行结果:
文件的格式化读写
格式化写操作fprintf
- 头文件:
#include <stdio.h>
- 函数定义:
int fprintf(FILE *stream, const char *format, ...);
- 函数参数:
- 第一个参数:要写入文件的指针
- 返回值:
- 成功:返回写入的字符总数
- 失败:返回一个负数。
格式化读操作fscanf
- 头文件:
#include <stdio.h>
- 函数定义:
int fscanf(FILE *stream, const char *format, ...);
- 函数参数:
- 第一个参数:要读取的文件的指针
- 返回值:
- 成功:返回成功匹配和赋值的个数
- 到达文件末尾或发生读错误:返回EOF。
案例7:
1 | void test7(void) { |
运行结果:
案例8:
1 | void test8(void) { |
运行结果:
文件的随机读写
rewind、ftell、fseek函数
引入
1 | void test9(void) { |
运行结果:
为什么会出现这种现象?
当写入hello file!完成后,流指针到达文件末尾!的位置;当再次读取的时候从!开始读取,由于系统不知道你要写入多少内容,他会给你分配足够的空间,所以后面是屯屯屯,而当读取屯屯屯后流指针停在了屯屯屯的末尾,即分配的足够空间的末尾,此时再关闭文件,屯屯屯也不会消失了。如果没有进行读取操作在写入hello file!后立即关闭文件,系统会自动删除流指针后面的屯屯屯。
解决办法:文件写完后需要关闭文件,然后重新打开文件。让文件流指针指向文件开始的位置。(顺序读取只能这样解决)
rewind
- 头文件:
#include <stdio.h>
- 函数定义:
void rewind(FILE *stream);
- 返回值:
void
- 功能:将文件流指针返回到文件首位置
ftell
- 头文件:
#include <stdio.h>
- 函数定义:
long ftell(FILE *stream);
- 返回值:返回流指针距离文件首的字节数
- 功能:测量当前流指针的位置
fseek
- 头文件:
#include <stdio.h>
- 函数定义:
int fseek(FILE *stream, long offset, int whence);
- 函数参数:
- offset:偏移量(正数向右,负数向左)
- whence:开始偏移的位置
- SEEK_SET 文件的开头(宏 0)
- SEEK_CUR 文件指针的当前位置(宏 1)
- SEEK_END 文件的末尾(宏 2)
- 返回值:
- 成功:函数返回零
- 失败:返回非零值
- 功能:流指针的移动
案例9:
根据文件大小动态申请空间,一次性读取文件所有内容
1 | void test10(void) { |
运行结果:
feof函数
头文件:
#include<stdio.h>
函数定义:
int feof(FILE *stream);
返回值:当设置了与流关联的文件结束标识符时,该函数返回一个非零值,否则返回零。即,
- 文件结束:返回非0
- 文件未结束:返回0
功能:判断文件是否到达末尾
注意:
- EOF宏 只能用于文本文件
- feof函数:文本文件、二进制文件都可以使用
案例10:
运行结果:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18void test11(void) {
char ch = '\0';
FILE* fp = NULL;
fp = fopen("C:/Users/11977/Desktop/g.txt", "r");
if (fp == NULL) {
perror("fopen");
return;
}
while (feof(fp) == 0)
{
ch = fgetc(fp);
printf("%c", ch);
}
fclose(fp);
return;
}
ferror函数
- 头文件:
#include<stdio.h>
- 函数定义:
int ferror(FILE *stream)
- 返回值:如果设置了与流关联的错误标识符,该函数返回一个非零值,否则返回一个零值。即,
- 遇到错误:返回非0
- 无错误:返回0