C语言 文件、文件操作、文件相关函数

目录

1、概念

 2、文件路径

2.1、绝对路径

2.2、相对路径

3、文件分类

3.1、文本文件(ASCII文件)

3.2、二进制文件

4、缓存

4.1、缓存

4.2、文件缓冲区

 5、文件操作函数

5.1、文件打开

5.2、文件数据写入

5.3、文件数据读取

5.4、文件关闭

6、拷贝文件小练习

6.1、拷贝文本文件

6.2、歌曲拷贝

7、文件相关函数

7.1、文件相关知识

7.2、文件光标移动函数 fseek(),文件字节数统计函数 ftell()       

7.3、getc(),putc()

        扩充:

        扩充:

 7.3、fgets(),fputs()

7.4、 fscanf()、fprintf() 

8、总结


1、概念

        ●文件:存储在外部介质数据的集合

        ●我们用 "" 表示数据从源端到目的端(终端)的流动。文件从外存被调入内存的过程就产生了输入流,从内存外存中保存的过程就产生了输出流

        ●文件光标:当我们打开一个文件时,鼠标在文件中点击一下,就会出现一个文件光标在跳动。这代表了我们可以在当前位置对文件进行编辑或者读取 。文件光标位置可以通过函数 fseek() 指定,在下面文件相关函数中介绍。

         ●文件结束标记EOF:在文件的末尾会有一个标记字符EOF来表示文件数据就那么多,后面没有了。EOF是一个宏,其值为-1。

 2、文件路径

        通过文件路径,我们可以知道文件在计算机外存中的存储位置,从而找到该文件,再进行目标操作。

2.1、绝对路径

        绝对路径就是从根目录开始的完整的文件路径。Windows上根目录有C盘、D盘等等,Linux的根目录只有一个:/。

   

        所以,在Windows电脑上一个绝对路径可以是 C:\Users\861\Pictures\画图\输入输出流.png

        但是,如果将这段路径保存为字符串时,就需要将单个反斜杠换成双反斜杠。因为单个反斜杠是转义字符的标志,双斜杠在字符串中才表示一个斜杠:"C:\\Users\\861\\Pictures\\画图\\输入输出流.png"

        也可以用正斜杠来代替反斜杠"C:/Users/861/Pictures/画图/输入输出流.png"

2.2、相对路径

        相对路径就是在当前所在的目录(文件夹)下,文件相对于本文件夹的路径。所以,相对路径的前提是目标文件在本文件夹(或本文件夹的子文件夹)中

        对于Pictures文件夹,输入输出流.png文件的绝对路径就是 "画图/输入输出流.png"。

3、文件分类

3.1、文本文件(ASCII文件)

        文本文件的后缀名为 .text、.cpp等,存储方式为字符串。也就是对于数字123,其在文本文件中以字符形式存储为'1','2',‘3’,大小为三字节。

3.2、二进制文件

        多媒体文件全都存储为二进制文件(0101二进制机器码形式),后缀名为:.ppt、.mp3、.jng等。对于数字123,其在二进制文件中的存储形式为0111 1011,大小为1字节。

4、缓存

4.1、缓存

        缓存是数据交换的缓冲区。

        当CPU要读取数据时,先在缓存中查找数据,如果找到了数据,就直接使用;如果没找到,就去内存中找;如果内存中还没有就去外存中找。如果同一数据被多次从外存调入内存使用,系统就将该数据保存在缓存中,提高效率。

4.2、文件缓冲区

        数据在存入文件,或我们通过scanf()对变量进行赋值时,不是一个数据一个数据进行操作的,而是先将数据保存在缓冲区,等数据数量达到上限,或输入结束,再将所有数据按顺序进行保存或赋值。

        ●ANSI C标准采用“缓冲文件系统”处理数据文件。

        ●文件系统:组织,管理文件的方法和数据结构。

        ●“缓冲文件系统”:系统自动地在内存中为每一个正在使用的文件开辟一个文件缓冲区。如果要将内存中的数据保存到外存的文件中,就将所有数据先输入到输出文件缓冲区中等缓冲区被装满,或操作完成时,再将所有缓冲区的数据按顺序存储到外存中;然后对缓冲区进行刷新,等待下次操作。将文件数据调入内存中时原理相同。

        缓冲区未装满之前,会进行等待,等到装满、操作完成或程序结束时,将数据进行向外存保存。目的是为了减少I/O调用,减少时间的浪费

 5、文件操作函数

5.1、文件打开

        文件打开函数是fopen(),使用形式为:FILE* fp = fopen(" 文件路径.后缀名","读写模式");

        ●fopen()的返回值为文件类型的结构体指针 FILE* ,结构体的成员变量是文件的各种信息,指针指向了该文件。

        ●文件路径可以是绝对路径,也可以是相对路径,但要注意使用 "\\"或"/"。

        ●文件的读写模式分两大类,一种是针对文本文件,一种是针对二进制文件。其原理都相同,只不过二进制文件的读写模式在文本文件的读写模式后加上字母b。

        以只读模式打开,就只能对文件数据进行读取,不能添加或修改;以只写模式打开,就只能对文件进行数据写入,不能读取。 所以,为了方便操作,一般选择r+、w+或a+模式打开文件。

5.2、文件数据写入

        文件数据写入函数为 fwrite(),使用形式为:fwrite(buff,sizeof(数据类型),count,fp); 意为将buff字符串或字符数组中的count个数据写入到fp指针指向的文件中去。

        ●fwrite()的返回值为count,是写入到文件中的数据个数;

        ●sizeof(数据类型):是我们要写入文件中的数据的单个数据字节大小。

        ●count:这一次操作要向文件写入的数据个数;如果buff中的数据少于count,则系统会硬从缓冲区中读取够count个数据,写入到文件中。所以,count的大小应为buff数组中有效数据个数

        ●fp:指向要写入数据的文件的指针。

5.3、文件数据读取

        文件数据读取函数是fread(),其使用形式为fread(buff,sizeof(char),count,fp); 意为将fp指向的文件中最多count个数据读取到buff字符数组中。数据的读取是从文件数据首部开始,若文件数据量大于count,文件光标移动到count位置,下一次的读取从count位置开始。

        ●fread()返回值是成功从文件中读取到的实际数据个数,若fp指向文件的数据数量小于count,就只读取实际的数量。

        ●sizeof(char):单个数据大小。

        ●count:一次性想从fp指向的文件中读取的数据个数,注意不能超出buff的容量。

        ●fp:指向要读取数据的文件的指针。

5.4、文件关闭

        文件关闭函数为fclose(),使用形式为fclose(fp);

        文件被打开使用完之后,就需要关闭。正如上述所言,文件被使用时,系统会给文件开辟一块文件缓冲区,并且还会占据其他资源;如果只打开文件,而不关闭,必然会导致系统资源被浪费。

6、拷贝文件小练习

6.1、拷贝文本文件

        对于一个文件"hello.text",其内容为"hello,world!",将其内容拷贝到一个新文件"newhello.text"中。

//以只读模式,打开hello.text文件,以fp1指向
//以r模式打开文件,只能对文件进行数据读取,
//若文件不存在,会返回NULL值
FILE* fp1 = fopen("C:\\Users\\86187\\Documents\\hello.txt","r");	
assert(fp != NULL);    //若打开失败,就中断程序

//以可读可写w+模式,打开newhello.text文件,以fp2指向
//系统中没有newhello.text,程序会自动创建文件
FILE* fp2 = fopen("C:\\Users\\86187\\Documents\\newhello.txt", "w+");
assert(fn != NULL);

//用来保存一次性读取到的数据,数组大小可以更大
//也可以用一个char型变量来保存,
//一次只读取一个字符,就比较慢
char buff[256]={0};    
int count = 0;

//用count记录每次成功读取到的数据量
//若count==0,意味着文件数据被读取完了
while (count = fread(buff, sizeof(char), 255, fp)) {
    //将buff中读取到的数据写入新文件
    //读取到count个,就写入count个
    //否则系统会强转从缓存区读取够你指定的数量
    //就会读取到其他未知数据
	fwrite(buff, sizeof(char), count, fn);
    //将buff中数据清空,防止影响下次写入
	memset(buff, 0, 256);
}

fclose(fp);    //关闭文件
fclose(fn);

        若执行成功,就会在指定路径下找到新拷贝的文件。 

6.2、歌曲拷贝

        在音乐软件中,下载一首普通歌曲。确认路径,然后以二进制文件形式打开。虽然二进制文件与文本文件对于数据的存储方式有所不同。如123:以文本文件存储为'1','2','3';以二进制文件存储为 123的二进制形式:0111 1011。但是,其最根本的形式还是二进制0101...机器码,所以我们直接按字节大小来读取。

        一个char型数据容量为1字节,一个256个格子的字符数组能保存256个字节的数据。我们使用数组来保存读取到的字节数据,设置一次性读取的最大数据量为255,也可以是256、254......。count为实际读取到的数据字节数,根据实际字节数,将数据写入新文件。

//以二进制读模式打开歌曲
FILE* fp1 = fopen("D:/KuGou/EXO - 咆哮 (Chinese ver.).mp3", "rb");
//以二进制读写模式打开新歌曲文件
//没有文件,自动创建
FILE* fp2 = fopen("D:/KuGou/NewSong.mp3", "wb+");

char buff[256] = { 0 };
int count = 0;

while (count = fread(buff, sizeof(char), 255, fp1)) {
	fwrite(buff, sizeof(char), count, fp2);
    //清空buff数组,防止影响下次写入数据
	memset(buff, 0, count);
}

fclose(fp1);
fclose(fp2);

         若歌曲拷贝成功,就可以进行播放。新歌名为NewSong,歌手未知,但其他数据全部拷贝过来了。

7、文件相关函数

7.1、文件相关知识

1、我们先了解一下关于文件头部,光标当前位置和文件尾部的宏定义:

        SEEK_SET:文件头部。数值:0

        SEEK_CUR:文件光标当前位置。数值:1

        SEEK_END:文件尾部。数值:2

2、标准输入stdin、标准输出stdout、标准错误stderr

        在程序运行的时候,系统会默认开启三个输入输出流,stdin默认指向键盘,stdout和stderr默认指向屏幕,也可以重定向输出到文件中。这仨个流分布完成数据的传输:stdin将输入数据传输到指定位置,stdout将要输出的数据传输到指定位置,stderr将程序运行的错误信息传输到指定位置。在下方会配合文件相关函数,理解这三个流和文件函数。

7.2、文件光标移动函数 fseek(),文件字节数统计函数 ftell()       

        1、fseek(FILE* fp,int count,SEEK_SET):此时count需要 >= 0,因为当count为正时,代表将光标从指定的位置向后移动count位;当count为负时,代表将文件光标从指定位置向前移动count位;count为0时,代表就让鼠标待在指定的三个位置之一。

//指定文件光标,
//待在从文件开头向后移一位的位置
fseek(fp, 1, SEEK_SET);
//待在当前位置
fseek(fp, 0, SEEK_SCUR);
//待在从文件尾部向前移一位的位置
fseek(fp, -1, SEEK_END);

         此时,还暂时没有体现出文件光标移动的意义,fseek()配合着统计光标之前的字节数函数ftell(),就体现出其一部分的作用了。

        2、ftell(FILE* fp):统计该文件文件光标之前的数据字节数。如果我们想要通过代码知道一个文件的大小,就可以通过将文件光标移动到文件结尾,再统计光标之前的字节数,就可以知道文件的字节大小了。

        将之前写入的内容清除,此时文件中只有12个字符,所以大小为12字节。

7.3、getc(),putc()

        1、getc(FILE* fp)函数:从指定文件的文件光标处获取一个数据当作返回值返回。当文件刚被打开时,默认文件光标在文件开头。当getc() 获取到一个数据时,光标自动向后移动一位

        ●使用方式:

FILE* fp = fopen("C:\\Users\\86187\\Documents\\hello.txt","a+");	
assert(fp != NULL);

char a = getc(fp);
printf("%c", a);

//等价于
printf("%c", getc(fp));

fclose(fp);

          连续使用,就可以按顺序获取到文件的数据。

        ●注意:对于二进制文件,其以二进制方式保存数据,如果该文件中刚好有 -1,其值与文件结束标记EOF的值相同,所以用getc()函数获取二进制文件的数据时可能会被误导。

char a = 0;
while ((a = getc(fp)) != EOF) {
	printf("%c", a);
}

        扩充:

        若我们将getc获取数据的来源从文件改为输入流stdin,我们就实现的getchar()的功能:getc(stdin); <=> getchar();  //所以getchar()是getc()的特殊实现。//这也是一个专业术语--重定向,的实现。

        2、putc(int a,FILE* fp)函数:将数据a输入到fp指向的文件中。如果文件是以写的w+或r+方式打开,a会从文件头开始保存,覆盖原数据;如果文件以a+方式打开,数据就从文件结尾开始保存。当a值的大小在ASCII码范围内,a被保存后,可以被识别为我们常见的字符;如果超出范围,我们打开文件就可能会显示乱码。

        扩充:

         若我们将putc输出数据的终端从文件改为输出流stdout,我们就实现的putchar()的功能:putc('a',stdout); <=> putchar();  //所以putchar()是putc()的特殊实现。 

 7.3、fgets(),fputs()

        1、fgets(char* buff,int count,FILE* fp):将fp指向文件中的count个数据获取到字符串buff中。

         当我们将其数据来源重定向为stdin时,我们就实现了gets()函数。读者可自行验证。

        2、fputs(const char* buff,FILE* fp):将字符串buff中的数据输出到fp指向文件中。

         当我们将其数据输出终端重定向为stdout时,我们就实现了puts()函数。读者可自行验证。

7.4、 fscanf()、fprintf()

        1、fscanf(FILE*fp,Format 数据格式,数据去向):从文件中获取数据,以指定格式输入到指定去向。如:

//将fp指向的文件中光标位置的后两个数据
//以字符形式分别保存到a、b中
char a = 0, b = 0;
fscanf(fp, "%c%c", &a,&b);
printf("%c,%c",a,b);

        将数据来源从文件修改为输入流stdin时,就可以实现scanf()函数:

         2、fprintf(FILE* fp,Format 数据格式, 数据来源):将数据来源的数据以指定数据格式,写入文件fp。如:

         将数据去向从文件修改为输出流stdout,就实现了printf()函数。读者可自行验证。

8、总结

        1、在输入文件路径时,要注意斜杠问题,要使用双反斜杠"\\"或正斜杠'/'代替。

        2、文件类型分为文本文件和二进制文件两种,但最根本的数据形式还是二进制机器码。我们在文本文件中编辑的数据都是各类字符,是我们认识的,所以文本文件以文本解析工具打开后我们还能认识其中内容。但二进制文件中全是机器码,其排列组合就不一定是我们能认识的字符或ASCII码表中的字符了,所以二进制文件以文本解析工具打开的话,其中有些字符我们能认识,但大部分都是乱码形式。

        3、文本文件与二进制文件的打开模式相同,只不过多加了个字符b。

        4、如果以一个字节一个字节的形式从二进制文件中读取数据,有可能读取到 -1的二进制(与文件结束标志相同)。让程序误以为读取到文件末尾了,导致出错。

        5、文件数据读取与写入有缓冲区帮助,目的是为了减少I/O调用,避免浪费资源与时间。

        6、各类从键盘输入和屏幕输出的函数,其实都是各类文件数据读取和写入函数的特殊实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值