文章目录
文件
用文件的方式,将数据长期保存
文件有格式
文件有规范
C下面的文件就是一个结构,用指针来操作
对CPU而言,就是一种设备【用于读写】
- 在CPU控制下,由内存写到文件
- 在CPU控制下,由文件读到内存
文件必须按原产生的类型标准来操作读写,否则,就会“乱码”
1C语言中的文件
- 程序文件
- 源文件
- 目标文件
- 可执行文件
- 数据文件
- 文本文件,ASCII字符形式,可以对字节里的二进制进行编码,方便人的理解
- 二进制文件,机器直接用的文件
2文件缓冲区
- ANSIC 标准采用“缓冲文件系统”处理的数据文件的
- 所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在 使用的文件开辟一块“文件缓冲区”。
- 从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘 上。
- CPU处理和IO读写之间,有速度差异,不然,CPU就会等待【浪费】
- 从CPU到文件的两个方向:CPU读,CPU写
- 文件,本质上是磁盘上的数据,即外围设备中的数据
- 缓冲区思想 == 缓冲带 == 缓冲池
- 类似的应用:打印机缓冲,
- 目的:利用中间地带,缓解速度矛盾,提高资源利用率
- 如果从磁盘向计算机读入数据,则从磁盘文件中读取数据输入到内存缓冲区(充满缓冲区),然后再从缓冲区逐 个地将数据送到程序数据区(程序变量等)。
- 缓冲区的大小根据C编译系统决定的(看不懂再看下面)
- 这就相当于你嗑瓜子,垃圾桶离你很远【必须的,如办公室里不能放这个桶,你又在办公室里】的时候,你难道要吃一个跑过去扔一个吗?那么我们就是嗑一把瓜子后,把瓜子皮一次送往垃圾桶,而文件缓冲区就相当于你聚瓜子皮的地方;
- 【某个位置】,即聚皮的地方,即缓冲区
- CPU写数据时,把数据先组织好,放在内存【某个位置】中,等到满时或是完时,内存一次写到硬盘中。
- CPU读数据时,把数据从磁盘读入,先组织好存放在内存【某个位置】中,等到满时或是完时,CPU一次读入
- 从内存向磁盘输出数据会先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘 上。
- 所谓缓冲文件系统是指系统自动地在内存中为程序中每一个正在 使用的文件开辟一块“文件缓冲区”。
3基本过程
3.1常见操作
- 打开 fopen
- 成功:返回文件指针,即文件
- 不成功:返回空指针,即NULL
- 操作 write/read
- fscanf(fp,格式列表,内存地址列表);
- 成功:返回读出的数据个数
- 不成功:返回EOF
- fprintf(fp,格式列表,输出数据列表);
- 成功:返回写到文件的字符个数
- 不成功:返回一个负数
- fscanf(fp,格式列表,内存地址列表);
- 关闭 fclose
- 成功:返回0
- 不成功:返回EOF
// 0 文件指针准备
FILE* fp;
// 1 打开文件
FILE * fopen ( const char * filename, const char * mode );
FILE* fp=fopen("e:/text.txt","r");
// 2 处理
// n 关闭文件
int fclose ( FILE * stream );
fclose(fp);
3.2对fopen的补充说明
1.如果文件打开成功,fopen()函数将返回给文件的文件指针,以后就可以通过该文件指针来对文件进行各种操作,而不用再使用文件名。
2.如果文件打开失败,fopen()函数返回空指针NULL.
3.可以通过判断fopen()函数的返回值来确定文件是否正常打开
if(fp=fopen("filename","r")==NULL)
{
printf("Can not open this file!\n");
exit(0);
}
3.3文件的关闭
- 使用完文件后应及时地对文件进行关闭,及时关闭文件的理由至少有以下两点:
- 使用缓冲文件系统时,如果数据缓冲区未满而又不关闭文件就退出程序的运行,则会造成数据缓冲区内的数据丢失。
- 一个系统内能够同时打开的文件有一定的数量限制。如果不及时地关闭已经不使用的文件,就可能造成打开其他文件时出错。
函数原型:int fclose( FILE *stream );
调用形式:fclose(文件指针);
函数功能:将与指定文件指针相关联的文件关闭。
fclose函数正常关闭了文件,返回0,否则返回EOF(-1)。
建议:在fclose操作完成后,可以再做一下fp=NULL;的操作
说明:
1.该函数用于关闭使用fopen()函数打开的文件,一般程序在这之前打开了几个文件,就必须调用fclose()关闭几个文件。
2.标准设备文件stdin、stdout和stderr由系统自动打开,系统会自动关闭。
3.函数正常关闭文件后返回值为0,出错则返回符号常量EOF(值为-1)。
4.若文件关闭后又想再次对这个文件进行操作,需要再一次使用fopen()函数打开文件。
3.4文件尾检测
- 程序中需要判断文件是否处理完成,即文件内部记录指针是否已移动到了文件尾标志处。
- EOF(仅用于文本文件)
- 由于文本文件中任何字符的编码均不是-1,可以用-1表示文本文件的文件尾标志,系统中用符号常量EOF来表示。在输入流中表示为ctrl+z。
- feof(pf)
- ANSI C提供了一个测试文件状态的函数feof(pf),当文件未结束时feof函数的值为0,否则为非0值。
- 使用函数feof来判断文件是否结束既可用于文本文件,还可用于二进制文件。
- 这个方法,会多读一次文件。
4对单个文件的操作方法
4.1单字符输入函数fgetc
- 函数原型:int fgetc(FILE *stream );
- 函数调用:ch=fgetc(fpt); //注意,ftp和ch分别为已经定义的文件指针变量和字符变量
- 函数功能:从指针变量fpt相关联的文件中读取一个字符并将其赋给字符型变量ch;执行函数时遇文件结束符或在执行中出错时返回值为EOF(-1)。
示例:
-
功能:从键盘输入一个文本文件的名称,并将其内容显示在屏幕上。
-
解释:
- 1.文件名一般啊包含为多个字符的字符串,可以定义一个字符数组filename进行存放。
- 2.要从文件中读取内容,fopen()函数的打开方式可设置为“r".
- 3.文件打开后可以使用fgetc()函数一个一个的读取字符,然后使用putchar将读到的字符显示到屏幕上,知道文件读完为止。
- 4.由于fgetc()函数遇到文件尾时返回EOF,因此可构建一个while循环判断文件是否读完。
程序代码如下:
#include <stdio.h>
#include <stdlib.h>
void main()
{
FILE *fp;
char ch, filename[50];
printf("请输入文件名:");
gets(filename);
if ((fp=fopen(filename, "r")) == NULL) //打开文件
{
printf("打开文件失败!\n");
exit(0);
}
while ((ch = fgetc(fp)) != EOF) //检测文件读取是否达到尾部
{
putchar(ch); //输出到屏幕上
}
fclose(fp); //关闭文件
system("pause");
}
字符读取函数fgetc()可从文件数据流中一次读取一个字符,然后读取光标移动到下一个字符,并逐步将文件的内容读出。如果字符读取成功,则返回所读取的字符,否则返回EOF(end of file)。EOF是表示数据结尾的常量,真值为-1。另外,要判断文件是否读取完毕,可利用feof()进行检查。未完返回0,已完返回非零值。
5操作实例
P320
/*
读文件,完成运算
编写一程序P320.C实现以下功能
在文本文件Comp.txt里有需要计算结果的整数算式,每个算式占一行且文件中只有一个算式,运算类型只有“加法(+)”或者“减法(-)”且运算符前后至少有一个空格。
计算该算式的结果并在屏幕上显示。
单击此处下载程序运行时测试用的算式文件Comp.txt(加法示例,编程时还应考虑算式为减法的情况)并保存到程序P320.C所在的文件夹且文件名保持不变。
编程可用素材:
printf("%d + %d = %d\n");
printf("%d - %d = %d\n");
程序的运行效果应类似地如图1和图2所示。
123 + 556 = 679
图1 程序运行效果示例(测试用算式文件Comp.txt内容为整数加法算式)
123 - 556 = -433
*/
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int data1, data2;
int result;
char ch;
FILE *fp;
// open file
if ((fp = fopen("Comp.txt", "r")) == NULL)
{
printf("Open file failure.\n");
exit(0);
}
// read data
// 只有一行数据,直接格式化读取,注意,%c前有一个空格
fscanf(fp, "%d %c %d", &data1, &ch, &data2);
if ('+'==ch)
{
result = data1 + data2;
printf("%d + %d = %d\n", data1, data2, result);
}
else
{
result = data1 - data2;
printf("%d - %d = %d\n", data1, data2, result);
}
//close file
fclose(fp);
return 0;
}
P321
/*
编写一程序P321.C实现以下功能
在文本文件Comp.txt里有需要计算结果的整数算式,每个算式占一行且文件中有多个(数量不确定)算式,运算类型只有“加法(+)”或者“减法(-)”且运算符前后至少有一个空格。
计算这些算式的结果并在屏幕上显示。
单击此处下载程序运行时测试用的算式文件Comp.txt并保存到程序P321.C所在的文件夹且文件名保持不变。
编程可用素材:
printf("Line %03d: %5d + %-5d = %-6d\n");
printf("Line %03d: %5d - %-5d = %-6d\n");
程序的运行效果应类似地如图1所示。
Line 001: 123 + 556 = 679
Line 002: 300 - 215 = 85
Line 003: 1001 - 18976 = -17975
Line 004: 9123 + 5156 = 14279
图1 程序运行效果示例(使用系统提供的测试用算式文件Comp.txt)
*/
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int data1, data2;
int result;
char ch;
int i = 1;
FILE *fp;
// open file
if ((fp = fopen("Comp.txt", "r")) == NULL)
{
printf("Open file failure.\n");
exit(0);
}
// read data
while (!feof(fp))
{
fscanf(fp, "%d %c %d", &data1, &ch, &data2);
if (!('+' == ch || '-' == ch)) // 如果读出来的不是运算符号,就终止
{
break;
}
if ('+' == ch)
{
result = data1 + data2;
printf("Line %03d: %5d + %-5d = %-6d\n", i, data1, data2, result);
}
else
{
result = data1 - data2;
printf("Line %03d: %5d - %-5d = %-6d\n", i, data1, data2, result);
}
ch = '\0'; // 每一次用完之后,就将运算符清空
i++;
}
// close file
fclose(fp);
return 0;
}
/*
另一种思路,就是将fscanf的返回值拿来判断
程序中用到fscanf、fprintf,为了判断异常,需要知道两个函数的返回值含义
原型:int fscanf(FILE* stream, const char* format, [argument...]);
1.fscanf正常情况下返回从文件中读出的参数个数
fscanf返回的是实际读取的数据个数,出错或者到结尾返回EOF。
2.fprintf正常情况下返回写入文件的字节数
注意细节:
1. 在空白符这个意义上来讲,fscanf 对空格、制表符、换行符是一视同仁的,不加区分的;%s会跳过前面的空白符,但是不会跳过后面的空白符;%c不会跳过空白符。
2. *表示读取一个域,但是不赋值给变量。
3. []表示只读取中括号内的字符,[]表示不读取中括号内的字符,值得注意的是%[]s将不会跳过前面的空白符。
4. 如果还没有任何一个域匹配成功或者任何一个匹配失败发生之前,就达到了文件流末尾,就算出错;或者读取文件流出错。这两种情况下,fscanf 返回EOF。
*/
P324
/*
编写一程序P324.C实现以下功能
在文本文件Comp.txt、CompA.txt、CompB.txt里有需要计算结果的整数算式,文件Comp.txt提供参加运算的第一个数,文件CompA.txt提供进行运算的运算符(只有“加法(+)”或者“减法(-)”),文件CompB.txt提供参加运算的第二个数,每个数或运算符均占一行,组合起来成为一个算式,遇到无法组成一个完整算式时即结束运算。这样的算式有多个(数量不确定)。计算这些算式的结果并在屏幕上显示。
单击下载程序运行时测试用的算式文件Comp.txt、CompA.txt、CompB.txt并保存到程序P324.C所在的文件夹且文件名保持不变。
编程可用素材:
printf("Line %03d: %5d %c %-5d = %-6d\n");
程序的运行效果应类似地如图1所示。
Line 001: 123 + 556 = 679
Line 002: 300 - 215 = 85
Line 003: 1001 - 18976 = -17975
Line 004: 9123 + 5156 = 14279
图1 程序运行效果示例
*/
#include <stdio.h>
int main(void)
{
int a1, a2; // 两个操作数
char ch; // 操作符
int n = 1; // 计数器
// 文件操作指针
FILE *fp1, *fp2, *fp3;
fp1 = fopen("Comp.txt", "r");
fp2 = fopen("CompA.txt", "r");
fp3 = fopen("CompB.txt", "r");
// 赋值后再判断,语句更简单一些
if (fp1 == NULL || fp2 == NULL || fp3 == NULL)
{
printf("error\n");
return 0;
}
while (!feof(fp2)) // 运算完成,首先要有运算符
{
// 充分利用返回值判断读取是否成功,三个数据要同时都有,才可以完成运算
if ((fscanf(fp1, "%d", &a1) == 1) && (fscanf(fp2, " %c", &ch) == 1) && (fscanf(fp3, "%d", &a2) == 1))
{
if (ch == '+')
{
printf("Line %03d: %5d %c %-5d = %-6d\n", n, a1, ch, a2, a1 + a2);
}
else
{
printf("Line %03d: %5d %c %-5d = %-6d\n", n, a1, ch, a2, a1 - a2);
}
n++; // 计数器
}
else // 如果读取失败【即两个操作数和一个操作符,只要有一个未读到数据,就不再处理】
{
break;
}
}
// 要记得关闭文件
fclose(fp1);
fclose(fp2);
fclose(fp3);
return 0;
}
P325
/*
编写一程序P325.C实现以下功能
在文本文件Comp.txt、CompA.txt、CompB.txt里有需要计算结果的整数算式,
文件Comp.txt提供参加运算的第一个数,
文件CompA.txt提供进行运算的运算符(只有“加法(+)”或者“减法(-)”),
文件CompB.txt提供参加运算的第二个数,每个数或运算符均占一行,组合起来成为一个算式,遇到无法组成一个完整算式时即结束运算。
这样的算式有多个(数量不确定)。计算这些算式的结果并将结果以文本文件格式保存到程序P325.C所在的文件夹中且文件名命名为CompC.txt。
单击下载程序运行时测试用的算式文件Comp.txt、CompA.txt、CompB.txt并保存到程序P325.C所在的文件夹且文件名保持不变。
编程可用素材:
fprintf(…"Line %03d: %5d %c %-5d = %-6d\n");
程序运行后生成的文件CompC.txt的内容应类似地如图1所示。
图1 程序运行效果示例(生成的文件CompC.txt之内容)
*/
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
int data1, data2, result;
char ch;
int i = 1;
int r1, r2, r3; // 文件读取返回值
FILE *fp1, *fp2, *fp3, *fp4;
// 文件正常打开
if ((fp1 = fopen("Comp.txt", "r"))==NULL)
{
printf("open file Comp.txt failure.\n");
exit(0);
}
if ((fp2 = fopen("CompA.txt", "r"))==NULL)
{
printf("open file CompA.txt failure.\n");
exit(0);
}
if ((fp3 = fopen("CompB.txt", "r"))==NULL)
{
printf("open file CompB.txt failure.\n");
exit(0);
}
// 注意:这个文件是写数据
if ((fp4 = fopen("CompC.txt", "w"))==NULL)
{
printf("open file CompC.txt failure.\n");
exit(0);
}
while (!feof(fp2)) // 要有运算符号,才可以运算
{
r1 = fscanf(fp2, " %c", &ch);
r2 = fscanf(fp1, "%d", &data1);
r3 = fscanf(fp3, "%d", &data2);
if ((r1 == 1) && (r2 == 1) && (r3 == 1)) //是否都成功读取了数据
{
if ('+' == ch)
{
result = data1 + data2;
fprintf(fp4, "Line %03d: %5d %c %-5d = %-6d\n", i, data1, ch, data2, result);
}
else
{
result = data1 - data2;
fprintf(fp4, "Line %03d: %5d %c %-5d = %-6d\n", i, data1, ch, data2, result);
}
i++; // 计数器
}
else
{
break;
}
}
// 关闭文件
fclose(fp1);
fclose(fp2);
fclose(fp3);
fclose(fp4);
return 0;
}