C语言进阶篇 六.文件的操作

目录

1. 为什么使用文件

2. 什么是文件

3. 文件的打开和关闭

4. 文件的顺序读写

5. 文件的随机读写

6. 文本文件和二进制文件

7. 文件读取结束的判定


1. 为什么使用文件

我们写的代码运行时,数据在内存中,程序操作着那部分内存,如果关闭程序,内存还给了操作系统,数据不但不能保存,更不能访问了,如果意外断电,则内存中的数据就丢失了。
若要访问和保存数据,则可以用文件(或数据库)帮我们保存数据,这时数据会被写入磁盘中,即使断电也能长期保存数据。

2. 什么是文件

文件即指磁盘上的文件,在程序设计中,通常指两类文件:

1.程序文件

包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj,Linux下后缀为.o),可执行程序(windows环境后缀为.exe,Linux下为.out)

2.数据文件

文件的内容一般是程序运行时读写的数据,比如程序运行需要从中读取数据的文件,
或者输出内容的文件。 本节主要介绍数据文件。
为了用户使用和识别,文件要有唯一的文件标识,就是常说的文件名,包含三个部分,文件路径,文件名主干,文件后缀,比如c:\CPP\text.txt

3. 文件的打开和关闭

在C语言中,提供了两个函数分别用于文件的打开和关闭。

FILE *fopen( const char *filename, const char *mode );

参数一filename是文件名,可以用像tt.txt这样的相对路径(相对于当前工程文件的目录下),也可以用像W:\a\tt.txt这样的绝对路径(注意\前加\才能表示\字符),因为类型是字符指针,故文件名要加“ ”,表示字符串首地址。

FILE* pf = fopen("W:\\a\\tt.txt", "w");//绝对路径
FILE* pf = fopen("tt.txt", "w");//相对路径

参数二mode是指文件的打开方式,有多种打开方式,如:

 该函数的返回值是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;
不同编译器的FILE类型包含的内容不完全相同。

当打开一个文件的时候,系统会根据文件的情况自动创建一个FILE结构体的变量,并初始化。这样一来,通过文件指针就能找到对应的文件。

当使用fopen函数打开文件失败时,函数会返回空指针。

于是可以这么使用:

FILE* pf = fopen("tt.txt", "w");
	if (pf == NULL) {
		perror("fopen->");
	    return 1;
    }
在进行文件操作后用fclose函数来关闭文件。
int fclose( FILE * stream );
该函数参数是文件指针,关闭成功时返回0,发生错误时返回EOF.
对应上述打开操作,关闭文件时:
fclose(pf);
pf = NULL;

4. 文件的顺序读写

常见读写的函数 

fgetc函数可以从所有输入流(包括标准输入流(键盘))中返回字符的ASCII码值,读取后该文件指针会指向文件中下一个字符。
int fgetc ( FILE * stream );

如果读到文件末尾或者读取错误就会返回EOF,这个函数和getc等效,但是有些库getc是用宏实现的。

例如工程目录下在tt.txt,写字母a,然后用fgetc可以读取文件内容

 fputc函数与这个函数相对,可以将字符输出到所有输出流中(包括标准输出流(屏幕)),输出后该文件指针会指向文件中下一个字符。

int fputc ( int character, FILE * stream );

如果写入成功,返回字符的ASCII码值,写入错误时返回EOF。

与fgetc,fputc对应的一组函数是fgets,fputs,不同的是输出和输入的是字符串,

char * fgets ( char * str, int num, FILE * stream );

从stream中读取num个字符(num包含了\0,实际就num-1个字符)拷贝到str指向的空间,在字符串结尾还会拷贝一个\0,直到遇到读到换行符或者文件结尾(EOF),如果读到换行符\n,会停止,把换行符也拷贝到str指向的空间。

fgets函数和gets不太一样,首先是参数个数,然后是gets不会拷贝\0到str中,gets不能指定拷贝的字符个数(可能会导致缓冲区溢出),而fgets可以指定拷贝个数。

使用实例:

 

 以下实例证明确实拷贝了\0

以下两个函数,fscanf可以从所有输入流中(格式化)读取数据到文件中,二fprintf则可以从所有输出流格式化输出到文件中,使用过scanf和printf这组函数就明白了,只不过把屏幕换成了文件。
对比下函数参数易于学习。

int fscanf ( FILE * stream, const char * format, ... );
int scanf ( const char * format, ... );
int sscanf ( const char * s, const char * format, ...);

int fprintf ( FILE * stream, const char * format, ... );
int printf ( const char * format, ... );
int sprintf ( char * str, const char * format, ... );

sscanf函数可以从字符串中读取格式化数据,sprintf是把格式化数据写入到字符串中。

例如:

/* sscanf example */
#include <stdio.h>

int main ()
{
  char sentence []="Rudolph is 12 years old";
  char str [20];
  int i;

  sscanf (sentence,"%s %*s %d",str,&i);
  printf ("%s -> %d\n",str,i);
  
  return 0;
}

注:scanf类函数中添加了*的部分会被忽略(跳过),不会被读取,也就是说上面函数从sentence字符串中读取了Rudolph字符串到str中,然后跳过了字符串is,然后读取了12到i中。(在printf中使用,*表示用后面的形参替代*的位置,实现动态格式输出,如printf("%.*s\n", 1, "abc");从字符串“abc”输出宽度为1的字符串,结果是a)

输出如下:

注意:如果把%*s去掉则读不到12了,如下

如果调整参数顺序,结果如下:

5. 文件的随机读写

上述函数只能在文件上顺序读写数据,要实现在文件各个位置读写可以借助下列函数。

int fseek ( FILE * stream, long int offset, int origin );

这个函数可以复位文件指针,调用函数便可以让它文件指针stream指向指定的位置。

参数origin是起始位置,可以是这些参数:

offset是相对起始位置的偏移量(字节数)。

使用实例:

要得到文件指针的当前位置可以借助ftell函数

long int ftell ( FILE * stream );

该函数返回当前文件指针相对文件起始位置的偏移量(字节数)。

使用实例:

rewind函数,可以将文件指针重新指向文件的起始位置
void rewind ( FILE * stream );

例如:(文件指针指向开头,所以ftell返回0 )

6. 文本文件和二进制文件

数据文件被分成了文本文件和二进制文件两种类型。

数据在内存中以二进制的形式存储,如果不加转换的输出到外存中,就是 二进制文件
在外存上以ASCII 码的形式存储,则需要在存储前转换成字符。以 ASCII 字符的形式存储的文件就是文本文件
前面提到的是文件文件,用fwrite函数可以将数据以二进制的形式输出到文本中,
size_t fwrite ( const void * ptr, size_t size, size_t count, FILE * stream );

通常,ptr是数组名,size是数组元素的大小(所占字节数),count是输出的元素个数,于是可以这么用,例如:

以二进制的形式打开,会发现文件中是这样的数据:

7. 文件读取结束的判定

需要注意不能用feof的返回值判断文件是否读取结束和直接判断文件是否为空。

int feof ( FILE * stream );

如果文件指针指向的位置已经没有数据了返回非0,表文件结束,否则返回0,表示还有数据。

该函数应用于当文件读取结束的时候,判断是读取失败结束,还是遇到文件尾结束
1. 文本文件读取是否结束用函数fgetc 判断是否为 EOF , 或fgets 判断返回值是否为 NULL .
2. 二进制文件的读取结束判断,用fread判断返回值是否小于实际要读的个数。
例如: 从文件中读取5个字符(包含\0),这时feof还返回0,
但是:这时如果再往下读,feof函数返回值就会被置为1,表示文件读取结束了(这时是文件读到尾的正常结束)
这还不是feof正确用法,feof常搭配ferror函数判断文件读取结束的原因,因为在使用文件读写的函数时,函数读写失败或结束feof和ferror的返回值就会被改变,这时就可以用两函数判断原因了。
使用例子:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值