目录
4.7 数据块读写函数——fread()函数和fwrite()函数
前提:
由scanf 键盘输入的数据与printf程序运行产生的结果,会随着运行程序的结束而丢失。即输入数据和产生的结果并没有被保存,被保存的仅仅只是程序执行代码。文件是解决不仅保留执行代码,保留输入数据或运行结果。
一、文件概述
文件指一组相关数据的有序集合,即文件名;通常存储在外部介质上(存储在磁盘上),在使用时才调入到内存中。
文件用来存放程序、文档、音频、视频数据、图片等数据的。
文件就是存放在磁盘上的,一些数据的集合。
在windows下可以通过写字板或记事本打开文本文件对文件进行编辑保存。写字板和记事本是微软程序员写的程序,对文件进行打开、显示、读写、关闭。
作为一个程序员,必须掌握编程实现创建、写入、读取文件等操作。
对文件的操作是经常要用到的知识,比如:写飞秋软件传送文件等
如常把磁盘作为信息载体,保存中间结果或最终数据。当有大量数据输入,通过编辑器可以事先建立输入数据文件,程序运行时从指定文件中读取数据;当有大量数据输出时,可将其输出到指定文件,可随时查看相应文件。
如文件系统输入输出,在使用一些字处理工具时,通过打开一个文件来将磁盘中的信息输入到内存,通过关闭文件来实现将内存数据输出到磁盘
所有文件都是通过流进行输入、输出操作。流是一个数据字节的序列,流入程序的流称为“输入流”, 流出程序的流“输出流”。
1.1 文件的定义
磁盘文件:(我们通常认识的文件)
指一组相关数据的有序集合,通常存储在外部介质(如磁盘)上,使用时才调入内存。
设备文件:
在操作系统中把每一个与主机相连的输入、输出设备看作是一个文件,把它们的输入、输出等同于对磁盘文件的读和写。
键盘:标准输入文件
屏幕:标准输出文件
其它设备:打印机、触摸屏、摄像头、音箱等
在Linux操作系统中,每一个外部设备都在/dev目录下对应着一个设备文件,咱们在程序中要想操作设备,就必须对与其对应的/dev下的设备文件进行操作。
标准io库函数对磁盘文件的读取特点
文件缓冲区是库函数中请的一段内存,由库函数对其进行操作,程序员没有必要知道存放在哪里,只需要知道对文件操作的时候的一些缓冲特点即可。
1.2缓冲区的分类
行缓存
相对于中断而言,标准io库函数,往标准输出(屏幕)输出东西的时候是行缓冲的;
所谓的行缓存就是缓冲区碰到换行符时才刷新缓冲区
如果不刷新缓冲区,无法对文件进行读写操作。
行缓冲的刷新条件:
void main()
{
//printf("Hello world!");
//刷新缓冲区方法1:使用\n
// printf("Hello world!\n");
//刷新缓冲区方法2: 程序正常结束
//printf("Hello world!\n");
//return ;
//刷新缓冲区方法3:使用fflush函数刷新缓冲区
//printf("Hello world!");
//fflush:刷新函数,可以刷新指定的缓冲区
//stdout:标准输出,就是对终端进行写操作
//fflush(stdout);
//刷新缓冲区方法4:当缓冲区满时自动刷新
//默认行缓冲的大小1024字节
int i;
for(i = 0; i < 300; i++)
{
printf("%03d ", i);
}
while(1)
;
}
全缓冲
相对于普通文件而言, 标准io库函数,往普通文件读写数据的,是全缓冲的,碰到换行符也不刷新缓冲区,即缓冲区满了,才刷新缓冲区
刷新缓冲区的情况
1.缓冲区满了,刷新缓冲区
2.人为刷新缓冲区fflush(文件指针)
3.程序正常结束会刷新缓冲区
无缓冲
在读写文件的时候通过系统调用io(read write),对文件进行读写数据这个时候是无缓冲的,即写数据会立马进入文件,读数据会立马进入内存
1.3 写文件的流程
应用程序空间 -> 内核空间 -> 驱动空间 -> 硬盘上
应用程序和内核程序运行在不同的空间里,目的是为了保护内核。
1.4 设置缓冲区目的
通过缓冲区可以减少进出内核的次数,以提高效率。
二、磁盘文件的分类
一个文件通常是磁盘上一段命名的存储区
计算机的存储在物理上是二进制的,
所以物理上所有的磁盘文件本质上都是一样的:以字节为单位进行顺序存储从用户或者操作系统使用的角度(逻辑上)把文件分为:
文本文件:基于字符编码的文件
二进制文件:基于值编码的文件
2.1 文本文件
文本文件
基于字符编码,常见编码有ASCII、UNICODE等
一般可以使用文本编辑器直接打开
例如:数5678的以ASCII存储形式为:
ASCII 码: 00110101 00110110 0011011100111000歌词文件(1rc):文本文件
2.2 二进制码文件
二进制码文件:
基于值编码,自己根据具体应用,指定某个值是什么意思把内存中的数据按其在内存中的存储形式原样输出到磁盘上
一般需要自己判断或使用特定软件分析数据格式
例如:数5678的存储形式为:
二进制码: 00010110 00101110
音颊文件(mp3):二进制文件
图片文件(bmp)文件,一个像素点由两个字节来描述*****#####&&&&&*代表红色的值
#代表绿色的值&代表蓝色的值
二进制文件以位来表示一个意思。
2.3 文本文件、二进制文件对比:
译码:
文本文件编码基于字符定长,译码容易些;
二进制文件编码是变长的,译码难一些(不同的二进制文件格式,有不同的译码方式)。
空间利用率:
二进制文件用一个比特来代表一个意思(位操作);
而文本文件任何一个意思至少是一个字符。
二进制文件,空间利用率高。
可读性:
文本文件用通用的记事本工具就几乎可以浏览所有文本文件
二进制文件需要一个具体的文件解码器,比如读BMP文件,必须用读图软件。
总结:
文件在硬盘上存储的时候,物理上都是用二进制来存储的。
标准io库函数,对文件操作的时候,不管文件的编码格式(字符编码、或二进制),而是按字节对文件进行读写,所以管文件又叫流式文件,即把文件看成一个字节流。
三、文件基本操作
基本操作:文件打开和关闭。除标准输入、输出文件外,其他所有文件须先打开,再使用,而使用后必须关闭该文件。
C使用换成缓冲文件系统来处理文件。
缓冲文件系统指系统自动在内存区为每一个正在使用的文件开辟一个缓冲区,
- 当需要把数据写入磁盘时,首先需要把数据写入缓冲区,系统会自动完成把数据从缓冲区写入磁盘文件
- 从磁盘读取数据同样也经过缓冲区。
3.1 文件指针
3.1.1 文件指针的概念
文件指针:是一个指向文件有关信息的指针,保存在结构体变量中。C系统为每个打开的文件在内存中开辟一个缓冲区,用于存放文件相关信息,即就是用于标识一个文件的,所有对文件操作的都是用对文件指针进行操作
文件指针在程序中用来标示(代表)一个文件的,在打开文件的时候得到文件指针,文件指针就用来代表咱们打开的文件。
对文件进行读、写、关闭等操作的时候,对文件指针进行操作即可,即将文件指针,传给读、写、关闭等函数,那些函数就知道要对哪个文件进行操作。
3.1.2 文件指针的一般形式
定义文件指针的一般形式为:
FILE* 指针变量标识符
FILE 为大写,需要包含<stdio.h>
FILE 是系统使用typedef 定义的有关文件信息的一种结构体类型,结构中含有文件名、文件状态和文件当前位置等信息
一般情况下,操作文件前必须定义一个文件指针标示将要操作的文件
实际编程中使用库函数操作文件,无需关心 FILE 结构体的细节,
只需要将指针传给 IO 库函数, 库函数再通过 FILE 结构体里的信息对文件进行操作
本质上文件指针是一个结构体指针,结构体中包含了当前文件的许多信息,但在实际编程时,不需要关系结构体中的成员,只需要使用文件指针即可。
FILE 结构体
typedef struct
{
unsigned flags; //标志文件状态
char fd; //文件号
unsigned char hold; //无缓冲区取消字符输入
short bsize; //缓冲区的大小
unsigned char *buffer; //缓冲区指针
unsigned ar *curp; //无活动指针
unsigned istemp; //草稿文件标识
short token; //做正确性检测
}FILE;
在缓冲文件系统中,每个被使用的文件都要在内存中开辟一块 FILE 类型的区域,存放与操作文件相关的信息:
FILE *fp;
3.1.3 对文件操作步骤
- 对文件进行读写等操作之前要打开文件得到文件指针
- 可以通过文件指针对文件进行读写操作
- 读写等操作完毕后,要关闭文件,关闭文件后,就不能再通过此文件指针操作文件了。
3.1.4 特殊文件指针
c语言中有三个特殊的文件指针无需定义,在程序中可以直接使用
- stdin:标准输入默认为当前终端(键盘)
使用的scanf、getchar函数默认从此终端获得数据
- stdout:标准输出默认为当前终端(屏幕)
使用的 printf、puts 函数默认输出信息到此终端
- stderr:标准错误输出设备文件,默认为当前终端(屏幕)
当我们程序出错使用:perror函数时信息打印在此终端
3.2 文件打开
3.2.1 函数的定义:
FILE *fopen(const char *path, const char*mode);
函数说明:
fopen函数的功能是打开一个已经存在的文件,并返回这个文件的文件指针(文件的标示)或者创建一个文件,并打开此文件,然后返回文件的标示。
函数的参数:
- 参数1:打开的文件的路径
1.绝对路径,从根目录开始的路径名称
“/home/edu/test/test.txt”
2.相对路径
“./test/test.txt”-../test/test.txt”
- 参数2:文件打开的方式,即以什么样的方式(只读、只写、可读可写等等)打开文件
第二个参数的几种形式(打开文件的方式)
3.2.2 文件的打开方式
返回值:
成功:打开的文件对应的指针
失败:返回NULL
注意:一定要判断,打开是否成功
3.2.3 文件打开的使用
FILE *fp;
fp = fopen("文件名", "使用文件方式");
只读方式打开文件名为“123”的文本文档文件
FILE *fp;
fp = fopen("123.txt", "r")
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
//以只读的方式打开文件,如果文件不存在则报错
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "r");
if(fp == NULL)
{
printf("fail to fopen\n");
}
}
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
//以只写的方式打开文件,如果文件不存在则创建,如果文件存在清空
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "w");
if(fp == NULL)
{
printf("fail to fopen\n");
}
}
“w”
- 如果文件不存在,则会自行创建文件
- 如果文件存在,且文件中有值,则会清空文件的内容。
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
//以只写的方式打开文件,如果文件不存在则创建,如果文件存在则追加
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "a");
if(fp == NULL)
{
printf("fail to fopen\n");
}
}
“a”
- 如果文件不存在,则会自行创建文件
- 如果文件存在,且文件中有值,则不会清空文件的内容,其是在现有的文本下追加新文本。
文件打开失败存在原因:
- 指定盘符或路径不存在
- 文件名中含有无效字符
- 以r模式打开一个不存在的文件
3.3 文件关闭
函数的定义:
fclose(文件指针名);
//正常关闭文件,返回值为0, 否则返回EOF(即 -1)
int fclose(FILE *fp);
函数的说明:
关闭fp所代表的文件
注意:
一个文件只能关闭一次,不能多次关闭,关闭文件之后就不能再文件指针对文件进行读写等操作了。
返回值:
- 成功返回0
- 失败返回非0
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
//fp = fopen("C:/Users/LWZ/Desktop/file.txt", "r");
//fp = fopen("C:/Users/LWZ/Desktop/file.txt", "w");
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "a");
if(fp == NULL)
{
printf("fail to fopen\n");
}
//使用fclose关闭函数
fclose(fp);
}
四、 读写文件
4.1 写字符函数——fputc()函数
int fputc(int c, FILE *stream)
功能:向指定的文件stream写入一个字符c
返回值:
如果输出成功,则返回输出的字节值,
如果输出失败,则返回一个EOF。
fputc(ch, fp);
:把一个字符写到fp所指向的文件中去,其中ch是要输出的字符,看是一个字符常量/字符变量,fp文件指针变量。
例子1:
#include<stdio.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
main()
{
FILE *fp;
char ch;
if((fp = fopen("E:\\cyuyan1.txt", "w")) == NULL )
{
printf("不能打开的文件\n");
}
ch = getchar();
while(ch != '*')
{
fputc(ch, fp);
ch = getchar();
}
fclose(fp);
printf("\n");
}
例子2:
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "w");
if(fp == NULL)
{
printf("fail to fopen\n");
}
//使用fputc向文件写入一个字符
fputc('a', fp);
fputc('a', fp);
fputc('a', fp);
fputc('\n', fp);
fputc('o', fp);
}
注意:
打开文件时,默认读写位置在文件开始,如果以a的方式打开读写位置在文件的末尾,向文件中读取字节或写入字节时,读写位置会往文件的末尾方向偏移,读写多少个字节,读写位置就往文件末尾方向偏移多少个字节。
4.2 读字符函数——fgetc()函数(一次读一个字符)
int fgets(FILE *stream);
功能:从文件指针stream指向的磁盘文件中读取一个字符,并且将读取的字符存储到字符变量中
返回值:
以 t 的方式: 读取文件结尾返回EOF
以 b 的方式,读到文件结尾,使用feof 判断结尾
ch = fgetc(fp);
从fp指向文件读入一个字符赋值给ch。
在文件打开时,该指针总是指向文件的第一个字节,
使用fgetc()函数后,该位置指针会发生变化,向后移动一个字节。
例子1:
#include<stdio.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
main()
{
FILE *fp;
char ch;
fp = fopen("E:\\cyuyan1.txt", "r");
ch = fgetc(fp);
while(ch != EOF)
{
putchar(ch);
ch = fgetc(fp);
}
printf("\n");
fclose(fp);
}
例子2 :
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "r");
if(fp == NULL)
{
printf("fail to fopen\n");
}
//使用fgetc从文件读取一个字符
int c;
while((c = fgetc(fp)) != EOF)
{
printf("c = %c - %d\n", c, c);
}
}
例子3:文本加入一行
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "r");
if(fp == NULL)
{
printf("fail to fopen\n");
}
//使用fgetc从文件读取一个字符
int c;
//文件的每一行结束的位置都有一个标识符,是一个换行符,称为行结束符
// fgetc可以读取到行结束符
while((c = fgetc(fp)) != EOF)
{
printf("c = %c - %d\n", c, c);
}
}
4.3 写字符串函数——fputs()函数
int fputs(const char *s, FILE *stream);
返回值:
成功返回写入的字节数
失败返回 -1
功能和说明:
向指定的文件stream写入一个字符串,其中字符串可以是字符串常量/字符数组名、指针或变量。
在stream所指的文件中读取字符,在读取时碰到换行符或文件的末尾停止读取,或 读取了size - 1 个字节停止读取,在读取内容后后面会加‘\0’,作为字符串的结尾返回值
返回值:
如果输出成功,则返回输出的字节值,
如果输出失败,则返回一个EOF。
例子1:
#include<stdio.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
main()
{
FILE *fp;
char filename[30], str[30];
printf("请输入文件路径:\n");
scanf("%s",filename);
if((fp = fopen(filename, "w")) == NULL )
{
printf("不能打开的文件\n");
getchar();
return 0;
}
printf("输入字符串:\n");
getchar();
gets(str);
fputs(str, fp);
fclose(fp);
printf("\n");
}
例子2:
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "w");
if(fp == NULL)
{
printf("fail to fopen\n");
}
//使用fputs向文件写入一个字符串
fputs("76876567\n", fp);
fputs("hello \n", fp);
}
4.4 读字符串函数——fgets()函数
char *fgets(char *s, int size, FILE *stream);
返回值:
成功返回目的数组的首地址,即s
失败返回NULL
功能与说明:从指定文件stream 中读取一个字符串s, 在读取的时候碰到换行符或者碰到文件的末尾停止读取,或读取size - 1 个字节停止读取,在读取内容后面会加 “\0” ,作为字符串的结尾。
例子1:
#include<stdio.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
main()
{
FILE *fp;
char filename[30], str[30];
printf("请输入文件路径:\n");
scanf("%s",filename);
if((fp = fopen(filename, "r")) == NULL )
{
printf("不能打开的文件\n");
getchar();
return 0;
}
fgets(str, sizeof(str), fp);
printf("%s\n", str);
printf("\n");
fclose(fp);
}
例子2:
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "r");
if(fp == NULL)
{
printf("fail to fopen\n");
}
//使用fgets向文件读入一个字符串
char buf[32];
//如果 fgets每次读取时最多读取文件一行内容,只要遇到 行结束符就立即返回
//如果想要读取的字节数小于一行内容,则只会读取第二个参数 - 1 个字节,
//最后位置 补\0
// fgets(buf, 8, fp);
fgets(buf, 32, fp);
printf("buf = %s\n", buf);
}
4.5 格式化写入函数——fprintf()函数
格式化:把想要输出的数据,按照一定格式输出。
功能: 将“输出列表” 数据按照指定的格式输出到 “ 文件类型指针”所指向的磁盘文件中。
函数调用:
fprintf(文件指针,格式字符串,输出表列)﹔
函数功能:
从磁盘文件中输出字符
fprintf和 printf_函数类似:
- printf是将数据输出到屏幕上(标准输出),
- fprintf函数是将数据输出到文件指针所指定的文件中。
fprintf(fp, "%d", i);
将整型变量i的值按%d的格式输出到fp指向的文件中。
#include<stdio.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
main()
{
FILE *fp;
int i =107;
char filename[30];
printf("请输入文件路径:\n");
scanf("%s",filename);
if((fp = fopen(filename, "w")) == NULL )
{
printf("不能打开的文件\n");
getchar();
return 0;
}
fprintf(fp, "%c", i);
printf("\n");
fclose(fp);
}
4.6 格式化读取函数——fscanf()函数
功能:按照“格式字符串”中指定的格式,从“文件类型指针”指向的文件中,将“输入列表”中的数据输入数据缓冲区。
函数调用:
fscanf(文件指针,格式字符串,输入表列);
函数功能:
从磁盘文件中读入字符
fscanf和scanf函数类似:
- scanf是从键盘(标准输入)获取输入,
- fscanf是从文件指针所标示的文件中获取输入。
fscanf(fp, "%d", &i);
读取fp所指向的文件中的i值
#include<stdio.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
main()
{
FILE *fp;
int i, j;
char filename[30];
printf("请输入文件路径:\n");
scanf("%s",filename);
if((fp = fopen(filename, "r")) == NULL )
{
printf("不能打开的文件\n");
getchar();
return 0;
}
for(i = 0; i < 5; i++)
{
fscanf(fp, "%c", &j);
printf("%d is: %5d\n", i + 1, j);
}
printf("\n");
fclose(fp);
}
例子:fscanf和fprintf
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
char ch1 = 'a', ch2;
int num1 = 50, num2;
char str1[20] = "hello", str2[20];
float score1 = 85.5, score2;
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "w+");
if(fp == NULL)
{
printf("fail to fopen\n");
}
//使用fprintf 向文件写入字符串
fprintf(fp, "%c %d %s %f\n", ch1, num1, str1, score1);
//将文件偏移量设置为文件的起始位置
rewind(fp);
//使用fscanf 获取文件内容
fscanf(fp, "%c %d %s %f\n", &ch2, &num2, &str2, &score2);
printf("%c %d %s %f\n", ch2, num2, str2, score2);
fclose(fp);
}
4.7 数据块读写函数——fread()函数和fwrite()函数
fprintf 和 fscanf 函数使用时,要将文本文件转换为二进制文件,在输出时又要将二进制文件转换为字符,用read()和fwrite()函数对磁盘文件读写
4.7.1 fread()函数
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
函数的说明:
fread函数从stream所标示的文件中读取数据,一块是size个字节,共nmemb块,存放到ptr指向的内存里
返回值:
实际读到的块数
功能:从fp所指向的文件中写入count次,每次读出大小为size个字节,且将读入的信息保存在buffer地址中。
例子1:
int num;
num = fread(str, 100, 3, fp);
从fp所代表的坟件中读取内容存放到str指向的内存中,读取的字节数为,每块100个字节,3块。返回值num,
如果读到300个字节返回值num为3
如果读到了大于等于200个字节小于300个字节返回值为2读到的字节数,
大于等于100个字节小于200个字节返回1
不到100个字节返回О
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "r");
if(fp == NULL)
{
printf("fail to fopen\n");
}
//使用fread函数读取文件内容
int num;
char buf[128];
num = fread(buf, 5, 4, fp);
printf("buf = %s\n", buf);
printf("num = %d\n", num);
}
4.7.2 fwrite()函数
size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream)
返回值:
实际写入的块数
函数说明:
fwrite函数将ptr 指向的内存量的数据(因为是void*型,也可以存放像结构体,int类型的值都是可以的),向stream所标示的文件中写入数据,
一块是size个字节,共stream
功能:将buffer数据,写入fp文件中写入count次,每次写大小为size个字节。
例子1: 使用fwrite向文件写入一个结构体
实现将输入的通讯录信息保存到磁盘文件中
#include<stdio.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
struct address_list
{
char name[10];
char adr[20];
char tel[15];
}info[100];
void save(char* name, int n)
{
FILE *fp;
int i;
if((fp = fopen(name, "wb")) == NULL )
{
printf("不能打开的文件\n");
exit(0);
}
for(i = 0; i < n; i++)
{
if(fwrite(&info[i], sizeof(struct address_list), 1, fp) != 1)
{
printf("file write error\n");
}
}
fclose(fp);
}
void show(char *name, int n)
{
int i;
FILE *fp;
if((fp = fopen(name, "rb")) == NULL)
{
printf("不能打开的文件");
exit(0);
}
for(i = 0; i < n; i++)
{
fread(&info[i], sizeof(struct address_list), 1, fp);
printf("%15s%20s%20s\n",info[i].name, info[i].adr, info[i].tel);
}
fclose(fp);
}
main()
{
int i, n;
char filename[50];
printf("有多少个学生?\n");
scanf("%d", &n);
printf("请输入文件路径及名称\n");
scanf("%s", filename);
printf("请输入学生的姓名、地址、电话:\n");
for(i = 0; i < n; i++)
{
printf("NO:%d\n",i + 1);
scanf("%s%s%s", info[i].name, info[i].adr, info[i].tel );
save(filename, n);
}
show(filename, n);
}
例子2:使用fwrite向文件写入一个结构体
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int a;
int b;
char c;
}MSG;
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "w+");
if(fp == NULL)
{
printf("fail to fopen\n");
}
//使用fwrite向文件写入一个结构体
MSG msg[4] = {1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8, 'd'} ;
fwrite(msg, sizeof(MSG), 4, fp);
}
读取:
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "w+");
if(fp == NULL)
{
printf("fail to fopen\n");
}
//使用fwrite向文件写入一个结构体
MSG msg[4] = {1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8, 'd'} ;
fwrite(msg, sizeof(MSG), 4, fp);
//就是想看存储进去的内容
MSG rcv[4];
fread(rcv, sizeof(MSG), 4, fp);
int i;
for(i = 0; i < 4; i++)
{
printf("%d - %d - %c\n", rcv[i].a, rcv[i].b, rcv[i].c );
}
}
原因:
因为在对文件进行操作的时候,有个文件属性称文件偏移量,当第一次读取文件字节数时候程序没有结束,再次读取文件字节的时候,会在之前读取的末尾的后一个字节继续再次 读取,比如:文件中文本有1, 2, 3, 4, 5,如果第一次读取的是1,2那下一次读取是从3开始的,所以之前,printf输出的无数值,说明其与之前用的是同一个偏移量,当我们写完数据之后,文件偏移量是在文件后面位置,因此,读取不到,使用rewind将文件偏移量设置为文件的起始位置
修改:
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "w+");
if(fp == NULL)
{
printf("fail to fopen\n");
}
//使用fwrite向文件写入一个结构体
MSG msg[4] = {1, 2, 'a', 3, 4, 'b', 5, 6, 'c', 7, 8, 'd'} ;
fwrite(msg, sizeof(MSG), 4, fp);
//将文件偏移量设置为文件的起始位置
rewind(fp);
//就是想看存储进去的内容
MSG rcv[4];
fread(rcv, sizeof(MSG), 4, fp);
int i;
for(i = 0; i < 4; i++)
{
printf("%d - %d - %c\n", rcv[i].a, rcv[i].b, rcv[i].c );
}
}
五、定位文件
前面介绍的对文件的读写方式都是顺序读写,即读写文件只能从头开始,顺序读写各个数据;但在实际问题中常要求只读写文件中某一指定的部分,例如:读取文件第200--300个字节
为了解决这个问题可以移动文件内部的位置指针到需要读写的位置,再进行读写,这种读写称为随机读写实现随机读写的关键是要按要求移动位置指针,这称为文件的定位.
完成文件定位的函数有:
rewind、fseek函数
文件定位:根据读写要求移动文件内部读写位置指针。
5.1 随机读写操作——fseek()函数
随机读写操作:指读写完上一个字符(字节)后,不一定要读写其后续的字符(字节),而是可以在任意位置上读写文件中所需要的字节(字符)。
freek():来移动文件内部位置指针。
“文件类型指针”:指向被移动的文件;
“位移量”:表示移动的字节数,要求位移量是long型数据,以便文件长度大于64KB时不会出错.
"起始点“:表示从何处开始计算位移量。如图:
freek(fp, 20L, 1);
总结:fseek定位位置指针(读写位置)
fseek函数(一般用于二进制文件即打开文件方式都需要带b )
函数定义:
int fseek(FILE *stream, long offer, int whence);
函数功能:
移动文件流的读写位置
参数:
whence起始位置
文件开头 SEEK_SET 0
文件当前位置 SEEK_CUR 1
文件末尾 SEEK_END 2
位移量 offer:
以起始点为基点,向前、后移动的字节数,正数往文件末尾方向偏移,负数往文件开头方向偏移。
返回值:
成功:0
失败:1
rewind(fp) <==> fseek(fp, 0, SEEK_SET);
#include<stdio.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
main()
{
FILE *fp;
char filename[30], str[50];
printf("请输入文件路径:\n");
scanf("%s", filename);
if((fp = fopen(filename, "rb")) == NULL)
{
printf("不能打开\n");
getchar();
exit(0);
}
printf("请输入一个字符串\n");
getchar();
gets(str);
fputs(str, fp);
fclose(fp);
if((fp = fopen(filename, "rb")) == NULL)
{
printf("不能打开\n");
getchar();
exit(0);
}
fseek(fp, 6L, 0); //将文件指针指向距文件首6个字节的位置
fgets(str, sizeof(str), fp);
putchar('\n');
puts(str);
fclose(fp);
printf("\n");
}
5.2 返回位置指针——rewind()函数
rewind函数
void rewind(文件指针)
函数功能:
把文件内部的位置指针移到文件首部
调用形式:
rewind(文件指针);
#include<stdio.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
main()
{
FILE *fp;
char filename[50], ch;
printf("请输入文件路径:\n");
scanf("%s", filename);
if((fp = fopen(filename, "r")) == NULL)
{
printf("不能打开\n");
getchar();
exit(0);
}
ch = fgetc(fp);
while(ch != EOF)
{
putchar(ch);
ch = fgetc(fp);
}
printf("\n");
rewind(fp);
ch = fgetc(fp);
while(ch != EOF)
{
putchar(ch);
ch = fgetc(fp);
}
fclose(fp);
printf("\n");
}
5.3得到文件位置——ftell()函数
功能:得到流式文件中的当前位置。
总结:ftell测文件读写位置距文件开始有多少个字节
函数定义:
long ftell(文件指针)
函数功能:
取得文件流目前的读写位置
返回值:
返回当前读写位置(距离文件起始的字节数),出错时返回-1
#include<stdio.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
main()
{
FILE *fp;
int n;
char filename[50], ch;
printf("请输入文件路径:\n");
scanf("%s", filename);
if((fp = fopen(filename, "r")) == NULL)
{
printf("不能打开\n");
getchar();
exit(0);
}
ch = fgetc(fp);
while(ch != EOF)
{
putchar(ch);
ch = fgetc(fp);
}
printf("\n");
n = ftell(fp);
printf("这个字符串的长度为:%d\n", n);
fclose(fp);
printf("\n");
}
将文件2内容复制到文件1
#include<stdio.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
main()
{
FILE *fp1, *fp2;
int n;
char filename1[50], ch, filename2[50];
printf("请输入文件1路径:\n");
scanf("%s", filename1);
printf("请输入文件2路径:\n");
scanf("%s", filename2);
if((fp1 = fopen(filename1, "ab+")) == NULL)
{
printf("不能打开\n");
getchar();
exit(0);
}
if((fp2 = fopen(filename2, "rb")) == NULL)
{
printf("不能打开\n");
getchar();
exit(0);
}
fseek(fp1, 0L, 2);
while((ch = fgetc(fp2)) != EOF )
{
fputc(ch, fp1);
}
fclose(fp1);
fclose(fp2);
printf("\n");
}
定位文件的整体的例子:
①、未添加定位函数
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "w+");
if(fp == NULL)
{
printf("fail to fopen\n");
}
fputs("123456789\n", fp);
fputs("aabcdefghijklmn\n", fp);
char buf[32] ="";
while(fgets(buf, 32, fp) != NULL)
{
printf("[%s]\n", buf);
}
}
②、获取当前文件指针的读写位置
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "w+");
if(fp == NULL)
{
printf("fail to fopen\n");
}
fputs("123456789\n", fp);
fputs("aabcdefghijklmn\n", fp);
//获取当前文件指针的读写位置
printf("offset = %ld\n", ftell(fp));
char buf[32] ="";
while(fgets(buf, 32, fp) != NULL)
{
printf("[%s]\n", buf);
}
}
③将当前文件的读写位置设置到 文件的起始位置2种方法
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "w+");
if(fp == NULL)
{
printf("fail to fopen\n");
}
fputs("123456789\n", fp);
fputs("aabcdefghijklmn\n", fp);
//获取当前文件指针的读写位置
printf("offset = %ld\n", ftell(fp));
//将当前文件的读写位置设置到 文件的起始位置
//rewind(fp); //方法一
fseek(fp, 0 ,SEEK_SET); //方法二
char buf[32] ="";
while(fgets(buf, 32, fp) != NULL)
{
printf("[%s]\n", buf);
}
}
④将当前的文件的读写位置设置为倒数 第5个位置
void main()
{
//使用fopen函数打开或创建文件,返回文件指针
FILE *fp;
fp = fopen("C:/Users/LWZ/Desktop/file.txt", "w+");
if(fp == NULL)
{
printf("fail to fopen\n");
}
fputs("123456789\n", fp);
fputs("aabcdefghijklmn\n", fp);
//获取当前文件指针的读写位置
printf("offset = %ld\n", ftell(fp));
//将当前文件的读写位置设置到 文件的起始位置
//rewind(fp); //方法一
//fseek(fp, 0 ,SEEK_SET); //方法二
//将当前的文件的读写位置设置为倒数 第5个位置
fseek(fp, -5, SEEK_END) ;
char buf[32] ="";
while(fgets(buf, 32, fp) != NULL)
{
printf("[%s]\n", buf);
}
}