当前位置:   article > 正文

【c语言】文件操作,解开你的疑惑

【c语言】文件操作,解开你的疑惑

为什么使用文件

我们程序运行的数据是运行在内存中的,当成程序结束的时候,内存被回收,数据也就销毁了,等再次运行程序的时候,是看不到上次的数据的,所以我们要使用文件将数据保存。

什么是文件

保存在硬盘(磁盘)上的文件就是文件

文件的分类

我们一般把文件分为:

  1. 数据文件
  2. 程序文件

程序文件:源程序文件(后缀为.c),目标文件(windows环境下后缀为.obj),可执行程序(windows环境下后缀为.exe)
数据文件:文件内容不一定是程序,而是程序运行时读取的数据。

在这,我们只讨论数据文件

文件名

文件都有一个唯一的文件标识,以便用户识别和引用。为了方便称呼文件标识又成为文件名
文件名分为以下3个部分:
文件路径,文件名主干,文件后缀
如:c:\csdn\text.txt

  • c:\csdn\是文件路径
  • text是文件名主干
  • .txt是文件后缀

二进制文件和文本文件

根据数据的组织形式,将文件分为二进制文件文本文件
二进制文件:数据在内存中是以二进制的补码的形式存储的,如果我们不加转换的将数据存储到文件中,那这个文件就是二进制文件
文本文件:如果我们将数据转换成ascll码的形式存储到文件中,那这个以ascll码字符形式存储的文件就是文本文件。

一个数在文件中是怎么存储的?

  • 字符一律按照ascll码形式存储
  • 数值型数据可以按照ascll码的形式存储也可以按照二进制的形式存储

举个例子:整数10000,它的二进制是:00000000000000000010011100010000,那么将他以二进制的形式存储时,就如下图,占4字节。
在这里插入图片描述

将10000以ascll码的形式存储:
1的ascll码为:49,转换成二进制就是:00110001
0的ascll码为:48,转换成二进制就是:00110000
共占5个字节
在这里插入图片描述

在这里插入图片描述
测试代码:

int main()
{
    int a = 10000;
    FILE* pf = fopen("text.txt","wb");//以二进制的形式写入数据
    assert(pf);//判断是否打开成功
    fputc(a,pf);
    fclose(pf);//打开文件后要记得关闭文件
    pf = NULL;
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

我们看到文件里面的数据是一个看不懂的符号
在这里插入图片描述
当我们在vs下用二进制编辑器打开这个文本的时候会看到:00000000 10 27 00 00
前面的00000000我们不看,就来看10 27 00 00
将它转换成二进制会得到:0000 0000 0000 0000 0010 0111 0001 0000 对应:0 0 0 0 2 7 1 0,因为在vs下是小端存储,所以我们看到的是10 27 00 00

文件的打开与关闭

流与标准流

我们的程序要输出到各种外部设备,也要从各种外部设备中获取数据,但是不同的设备输入输出的操作各不相同,所以为了方便程序员对各种设备的操作,我们抽象出了流的概念。可以把流理解为:流淌着字符的河流

一般情况下,我们想要向流里写数据或者读取数据都是要打开流后再进行操作。

标准流

我们从键盘输入数据再输出到屏幕上就是打开了标准流。
你可能疑惑:我咋不记得我打开了标准流啊?
那是因为标准流都是默认打开的,在c语言执行的时候默认打开了三个流:

  • stdin,标准输入流
  • stdout,标准输出流
  • stderr,标准错误流

这三个流是默认打开的,所以我们使用printf、scanf函数时能输入输出数据。
stdin、stdout、stderr三个流的类型为:FILE*,文件指针

文件指针

缓冲⽂件系统中,关键的概念是“⽂件类型指针”,简称“⽂件指针”。
每当打开一个文件的时都会在内存中开辟一个文件信息区,用来存放文件的相关信息的(文件名,文件状态,文件的当前位置)
这些信息是保存在⼀个结构体变量中的。该结构体类型是由系统声明的,取名FILE

VS2013编译环境提供的stdio.h 头⽂件中有以下的⽂件类型申明:

struct _iobuf {
 char *_ptr;
 int   _cnt;
 char *_base;
 int   _flag;
 int   _file;
 int   _charbuf;
 int   _bufsiz;
 char *_tmpfname;
 };
 typedef struct _iobuf FILE;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

不同的C编译器的FILE类型包含的内容不完全相同,但是⼤同⼩异

我们可以创建一个文件指针:

FILE*pf;
  • 1

这里定义了一个指向文件信息区的文件指针变量pf,通过这个指针变量可以间接的访问相关文件。
在这里插入图片描述

文件的打开与关闭

在使用的文件时,需要先打开文件,最后再关闭文件。
再编写程序时,打开文件会返回一个FILE*类型文件指针,该指针指向该文件
ANSI C规定,使用fopen函数来打开文件,fclose函数来关闭文件

//打开⽂件
FILE * fopen ( const char * filename, const char * mode );//filename为文件指针,mode为文件的打开方式
 //关闭⽂件 
int fclose ( FILE * stream );
  • 1
  • 2
  • 3
  • 4
#include<stdio.h>
int main()
{
	FILE*pf=fopen("text.txt","r");
	//....程序
	fclose(pf);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

文件的打开方式有下面这些:
在这里插入图片描述

文件的顺序读写

顺序读写函数介绍
在这里插入图片描述
上面所讲“适用于所有流指标准流和其他流(如文件流)”。
fgetc函数

int fgetc ( FILE * stream );
  • 1

字符输入,对于键盘到程序来说,是从键盘输入到程序,对于文件到程序来说,是从程序输入到文件(也可称为读文件)

#include<stdio.h>
int main()
{
	FILE*pf=fopen("text.txt","r");//以读的形式打开文件,如果文件存在,则打开成功,否在打开失败
	if(pf=NULL)//判断文件是否打开成功
	{
	perror("fopen");//打印错误信息
	return 1;//打开失败就提前结束程序
	}
	int ch=0;
	ch=fgetc(pf);//fgetc函数的返回类型为int型
	printf("%c",ch);//输出到屏幕
	fclose(pf);
	pf=NULL;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

假设这个文件中存放了一个字符a,那么这个字符a将被读取然后赋值到ch中在输出到屏幕上。

fputc函数

int fputc ( int character, FILE * stream );
  • 1

输出字符,从程序输出到文件中。

int main()
{
	FILE*pf=fopen("text.txt","w");//以写的形式打开文件,如果文件存在,则删除原来的内容后写入数据,如果不存在,则创建新文件
	if(pf=NULL)
	{
		perror("fopen");
		return 1;
	}
	fputc(c,pf);//向文件写入一个字符c
	fclose(pf);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

对于顺序读写,是根据光标来的
在这里插入图片描述
当写入\读取一个数据后,光标会后移一位,对于读取文件,当读取到文件末尾时,会返回EOF,这表示文件读取结束。
我们可以通过EOF来循环读取文件:

#include<stdio.h>
int main()
{
	FILE*pf=fopen("text.txt","r");//以读的形式打开文件,如果文件存在,则打开成功,否在打开失败
	if(pf=NULL)//判断文件是否打开成功
	{
	perror("fopen");//打印错误信息
	return 1;//打开失败就提前结束程序
	}
	int ch=0;
	while((ch=fgetc(pf))!=EOF)//fgetc函数的返回类型为int型
	{
	printf("%c",ch);//输出到屏幕
	}
	fclose(pf);
	pf=NULL;
	return 0;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

剩下的函数读者可自行去了解,用法于上面两个函数相差不大,这边我推荐一个网站来了解学习c语言相关函数
cplusplus

文件的随机读写

函数声明
fseekint fseek ( FILE * stream, long int offset, int origin );
ftelllong int ftell ( FILE * stream );
导管void rewind ( FILE * stream );

fseek函数
根据文件指针的位置和偏移量来定位文件指针

int fseek ( FILE * stream, long int offset, int origin );
offset为便宜量
origin为文件指针位置

 - SEEK_SET,文件起始位置
 - SEEK_CUR,文件当前位置
 - SEEK_END,文件末尾

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
int main()
{
    FILE* tmp = fopen("text.txt","r");
    int ch = fgetc(tmp);
    printf("%c\n",ch);
    fseek(tmp,2,SEEK_CUR);
    ch = fgetc(tmp);
    printf("%c\n",ch);
    fseek(tmp,-3,SEEK_END);
    ch = fgetc(tmp);
    printf("%c",ch);
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

假设文件中的内容有:abcdefg,那么读到的内容为“a” “d"以及"b”。

ftell函数
返回⽂件指针相对于起始位置的偏移量

 long int ftell ( FILE * stream );
  • 1
int main()
{
   
    FILE* tmp = fopen("text.txt","r");
    int ch = fgetc(tmp);
    printf("%c\n",ch);
    fseek(tmp,2,SEEK_CUR);
    ch = fgetc(tmp);
    printf("%c\n",ch);
    fseek(tmp,-3,SEEK_END);
    ch = fgetc(tmp);
    printf("%c\n",ch);

    printf("%ld",ftell(tmp));//输出4

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

rewind函数
让文件指针回到文件的起始位置

 void rewind ( FILE * stream );
  • 1

文件读取结束的判定

feof函数
用来判断文件结束的原因是不是因为读取到文件末尾
记住,feof函数不是用来判断文件是否读取结束的

文件缓冲区

ANSIC标准采用“缓冲文件系统”处理数据文件。简单来讲就是:打开文件时内存会开辟一块相应的文件缓冲区,当读取或写入数据时会先将数据放到缓冲区,等缓冲区空间满后在刷新缓冲区将数据读取到程序或者写入到文件中。缓冲区的大小由编译器决定。
在这里插入图片描述
因为有缓冲区的存在,C语⾔在操作⽂件的时候,需要做刷新缓冲区或者在⽂件操作结束的时候关闭⽂件,如果不做,可能导致读写文件失败的问题。(关闭文件时会刷新缓冲区)
在这里插入图片描述

干兴趣的可以通过这段代码观察一下文件 的读写

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/758491
推荐阅读
相关标签
  

闽ICP备14008679号