Error[8]: Undefined offset: 594, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

1.引言

 最近朋友在负责收集班上同学的青年大学习截图,每次都要统计那些人没交,很是困扰,于是找到我有没有办法可以一次性的统计。

我拿到这个问题的时候,已知是那些截图都是已经命名好了,比如张三.png,李四.jpg,我的思路是将这些名字统计到一个文件里,这个可以用脚本语言一句话实现,如下所示,其中的LIST_all是自己重定向输出的文件,这里不做过多介绍。

DIR *.* /B> LIST_all.txt

 再利用C语言文件 *** 作对这些统计好名字和班级所有人的名字库进行一一匹配,便可得出没有交青年大学习截图的同学名字,这其中我在文件 *** 作方面花费了大量时间,因此这里我对文件 *** 作表达一些个人的看法。

2.打开文件和关闭文件

 对文件的 *** 作,文件的打开和关闭 *** 作极其重要,必须成对出现,也就是说文件在打开之后,如果不再使用文件,必须关闭关闭文件。

实际上,文件的打开也就是在内存中为其建立相应的信息区和文件缓冲区,在打开文件的时候通常会有一个FILE类型的指针变量指向该文件,以后才能对文件进行 *** 作;而文件的关闭就是插销之前建立的信息区和文件缓冲区释放内存。

如果文件使用结束后不进行关闭 *** 作,就可能导致内存的泄露或者文件内容的损坏。

2.1 打开文件:fopen()函数

 首先看一下官方的函数定义:

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

 这里给出一个方法,在VS中选中函数后按F1就可以查看这个函数的帮助文档,当然了,这个文档是在线的。

首先看到定义了两个参数,第一个就是文件名,这里文件名可以使用相对路径也可以使用绝对路径:

	FILE* fp1;
	fp1 = fopen("D:\MyFiles\test.txt","w");  //绝对路径
	FILE* fp2;
	FILE* fp2 = fopen("test.txt", "w");  //相对路径

需要注意的是,在使用绝对路径时,直接复制过来的路径通常时一个反斜杠,这会被转义,所以需要手动添加一个反斜杠。

再来看第二个参数,这是指打开的模式,用于指定针对文件请求的访问类型,常见的有如下几种模式,也可以去官网docs.microsoft.com查看具体的介绍:

modeAccess
“r”打开以便读取。

如果文件不存在或找不到, fopen 调用将失败。

“w”打开用于写入的空文件。

如果给定文件存在,则其内容会被销毁。

“a”在文件末尾打开以进行写入(追加),在新数据写入到文件之前不移除文件末尾 (EOF) 标记。

创建文件(如果文件不存在)。

“r+”打开以便读取和写入。

文件必须存在。

“w+”打开用于读取和写入的空文件。

如果文件存在,则其内容会被销毁。

“a+"打开以进行读取和追加。

追加 *** 作包括在新数据写入文件之前移除 EOF 标记。

写入完成后,EOF 标记不会还原。

创建文件(如果文件不存在)。

 那么为什么要用一个FILE类型的指针来接收他呢,这就是在引言中提到的文件指针变量,以后对文件的 *** 作就是利用这个指针变量的。

2.2 关闭文件:fclose()函数

关闭文件直接利用这个函数就可以了,通常在结束对文件的 *** 作后立即调用这个函数:

fclose(fp);
3. 对文件的读 *** 作 3.1 读取一个字符:fgetc()

 从这里开始就要利用的文件指针了,用于记录当前已经 *** 作的具体位置,当然也可以通过fseek()函数来重置位置。

具体的 *** 作如下所示:

	char ch;
	ch = fgetc(fp);

此时fgetc()函数会将文件中(这里不是指目录)的第一个字符返回,例如我有如下文件:

那么显示的应该是“先”字,如下所示:


但是可能会出现乱码的问题,我的是在windows10下的VS2019版本,通过查阅后发现是编码问题,默认是gbk编码,而文本是utf8编码,因此可以将刚刚那个test.txt的编码格式转变为ANSI后,再次尝试,转换编码的方式(文件>另存为)如下:

但是我出现的问题是,即使转变了编码方式,也不能正常的显示,需要通过两个字符来接收,才能成功显示这个汉字(但是对于字母和数字不会出现这种问题,这个我已经试过了),如下所示:

当然了,为什么fgec()是读取的第一个字符,那么两个getc读取到的内容应该都是一样的,这个在后文会做介绍。

其实出现这个问题的原因是在C语言中char是一个字节,而在windows中按照utf-8的编码方式占两个字节,其他编码方式甚至更多,因此在这里采用一个字符串数组来接收fgetc函数得到的汉字(函数返回本身是汉字的ASCII值),如下所示:

好了,详细的问题就不再介绍了,言归正传,为了方便介绍,不再用汉字作为例子,下边采用如下的例子,之后的函数讲解都基于此文件:test.txt:

前文说到,fgetc是读取文本中的第一个字符,并返回其对应值,那么两个连续的fgetc是不是读取到的是一样的内容呢?答案是否定的,这就涉及到文件指针了,在利用fopen函数打开文件的时候,利用了一个文件指针fp,也正就是这个指针使得顺序的两个fgetc读取的是文件中顺序的两个字符,因为fp会记录当前 *** 作的位置,比如第一个fgetc后,fp的位置就来到了第二个字符上,因此第二个fgetc读取到的就是第二个字符,因为刚刚是汉字,占用的字节数不同,下边用字母来演示一下:

既然可以做到读两个连续的字符,那么推广至三个,四个甚至多个呢,当然也是可以的。

好,现在来一个需求,如何读取文件的第一行呢,我们是不知道一行有多少的字符的,显然是不能写n个fgetc函数来读取一行。

对的,换行的标志是什么呢,’\n’,那么这就简单了,这就是循环的终止条件。

如下所示:

	char ch;
	while ((ch = fgetc(fp) )!= '\n')
	{
		printf("%c",ch);
	}

再来看一下结果:

大家可能会有一个疑问,这不是第一段吗?这里说明一下,对的,这个是我在网上复制的一篇英语作文,虽然在上边看到的是第一段占了几行,但是这是为了方便看我把文本文档调成了自动换行模式哈,也就是说,这个第一段是写在一行上边的。

好,这下再提一个需求,那么可以读取一行的内容了,那两行,三行呢,这当然也是可以的,比如说我要读取这个文本的前两行内容,应该是到,to be parents.那里结束,那就是第二次出现‘\n’的时候结束循环嘛,可以设计一个循环终止标记,当达到第二个’\n’时终止循环,如下:

	char ch;
	int index = 2;
	while (index) 
	{
		ch = fgetc(fp);
		printf("%c",ch);
		if (ch == '\n') index--;
	}

代码中的index就是设置的读取行数,当index为0的时候就跳出循环了,结果如下:

可能有人会想,利用这个方法,那么是不是可以读取整个文本文档呢,这里会出现两个问题,第一我们事先是不知道这个文本有多少行的,那么无法确定index的值;第二个问题就是,很多时候在文本的最后一个段落的最后一个字符输入结束后,用户是不会再去按一个回车的,而是直接保存退出了,那么根据’\n’来判断段落的方法就失效了。

是不是就没有办法了呢,当然是有的,仔细看官方文档就会发现,文档中有那么一句:

在打开文件时,已经进行了文件安全性的检查,那么返回EOF就只有一个可能了,那就是到达文件尾了,那么这就比之前的读取两三行还要简单,如下所示:

	char ch;
	while ((ch = fgetc(fp)) != EOF)
	{
		printf("%c", ch);
	}

不出意外的话,结果如下所示:

这里对EOF做一个说明,它是一个C语言预定义的宏,是end of file 的缩写,可以参考文章《EOF宏,C语言EOF宏详解》。

3.2 读取一行:fgets()

 其实对一行的读取,有一个单独的函数,也就是接下来要介绍的这个函数fgets(),和fetc()函数一样,在读取一行之后,文件指针也会移动的下一个字符,这里是以一行为 *** 作对象,那么下一个字符的位置是哪里呢?答对了,就是下一行的首字符所在的位置,值得注意的是,它会将文本中的换行符读取出来,不会保留在流中;另外,既然是读取一行字符,肯定不能单单用一个char来存,这里采用字符串也就是一个char数组来存储读取到的内容。

同样的,我们先来看一下官方的函数定义:

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

这里需要三个参数,下边分别说明这三个参数,第一个参数str就是数据的存储位置,就是你要把读取到的内容存在哪里;第二参数numChars,要读取的最大字符数,上边说到用的一个字符数组来存储,我可以定义一个200的数组,而给numChars赋予100的值,只要numChars的值小于数组长度就可以了,但是一般我用的时候都是两者相等。

好,来看一下实际的效果:

	char str[300];
	fgets(str, 300, fp);
	printf("%s", str);

看一下运行结果,是不是到第一段结尾it is a good idea.

再次说明一下哈,可能读着读着忘了,因为我的记事本设置了自动换行,原本我的第一段就是放在一行里边写的,只是为了好看,让你看着像是几行,实际就是一行哈,这里三段就是三行。

看下运行结果,是不是中间多了一个空行,但是我并没有在printf中添加’\n’,那是fgets函数本身的原因了,具体我也不太清楚,还在摸索中,我这里就不乱说了哈,大家知道请给我留言嘿嘿。


 好的我们再来用这个函数,完成对整个文档的读取,也只需要一个循环就好,那么怎么判断循环终止条件呢,也是查看文档也可以很容易的知道:

因此我们用是否为空来判断:

	char str[300];
	while (fgets(str, 300, fp)) {
		printf("%s", str);
	}

不出意外的话,结果如下所示:

3.3 想读多少读多少:fread()

 看标题就知道,为什么说是想读多少读多少呢,因为它不以行为单位,以字符为单位,但又能同时读取多个字符,因此相对于前边两个函数,可以说是很强大了。

第一步,看看函数的定义:

size_t fread(
   void *buffer,
   size_t size,
   size_t count,
   FILE *stream
);

功能更强大了,需要的参数也就多了,第一个参数,仍然是存储位置,因为这个函数可以读取多个字符,所以也是用一个字符数组来接收读取到的内容。

再来看一下第二个参数和第三个参数,第二参数是指每个单位字符的存储大小,以字节为单位,不能任意指定,比如说用的char数组,那么每个数组元素的大小是固定的一个字节,那么size的值就只能是1,大于1会出错,这个我已经试过了,小于1还没试过;为什么将第三个参数和第二个参数合着一起说呢,因为这两个参数就决定了字符的个数,count就是要读取的项的最大数量,实际读取的数量可以小于这个值,那么用count/size就是得到的字符数了。

最后一个参数是所有函数都有的,不然这个函数怎么知道是对谁 *** 作呢是吧。


 好我们下来看一看具体的 *** 作,假如我要读取前13个字符,那么就是到students结束,空格也算一个字符的哈,那么size的值取1,count的值取13:如下:

	char str[300];
	fread(str, 1, 13, fp);
	printf("%s",str);

得到的结果如下:

哇哦,在students后边为什么出现这么多烫烫烫,其实啊,这是因为在创建字符串的时候没有初始化,那么里边的内容是未知的,只用把创建str那一行初始化就好了,如下:

char str[300] = {0};

那么就可以得到正确结果了:

当然了,这种方法比慢,它需要在一开始给300个初始化,那么这个数字一旦变大了过后就很慢了,因此这里给出另外一种方法:fread函数的官方文档中,得知它的返回值是实际读取的项的个数,那么我们就可以去确定数组有有效元素的个数,也就是它的返回值,利用printf函数的特性,遇到’\0’时结束输出,那么我们就手动在有效元素后边加一个‘\0’嘛,这样速度就大大的提高了,如下:

	char str[300];
	int num = fread(str, 1, 13, fp);
	str[num] = ';'printf
	("%s",)str;char

好,我们现在来个需求,要求读取整个文档,仍然是通过循环来解决,同样的也是通过它的返回值,当fread读取不到内容的时候,返回0,这下就好办了:

	[ str300];while
	( int= num fread (,str1 ,200 ,) fp)[
	{
		str]num= ';' printf(
		"%s",); str}int
	fputc

不出意外的话,结果如下所示:

记住哦记住哦,使用之前一定要初始化!!!

4. 对文件的写 *** 作

 上边关于对文件读 *** 作的三个函数,其实也有三个与之对应的写 *** 作,分别是fputc(),fputs(),fwrite()。

说明一下,现在要对文件进行写 *** 作了,记得要把之前fopen中的那个模式参数改为写的,同时也建议新建一个txt。

好,下边我们来看一下这三个函数。

4.1 写入一个字符:fputc()

 一样滴一样滴,在 *** 作之前都来看一下这个函数的定义,查看一下官方文档:

( int,
   * c)
   FILE ;stream
*;

第一个参数就是自己要写入的字符,那么这里为什么是整型呢,因为每个字符都有一个自己对应的ASCII码值,这些信息在计算机底层都是按照二进制来存储的;第二个参数就是要 *** 作的文件,也就是指向文件结构的指针。


我们来看一下实际效果:

	FILEif fp(
	!(=fopenfp ( "D:\test.txt","r+") ))printf(
	{
		"Error!");exit(
		-1);}fputc
	(
	'A',); fpchar; 
		while ch(
	( =fgetcch ( ))fp!=EOF ) printf(
	{
		"%c",); ch}*
	;

这里要注意几个问题,第一注意我fopen的打开模式变为了,“r+”,是既可以对文件进行读 *** 作,又可以进行写 *** 作。

这里我先进行了写 *** 作,输入了一个字符’A’,然后进行输出看一下有没有成功。

好现在我们来看一下运行结果:

哎,这就奇了个怪了,怎么回事呢,一行空行。

第一,我原本文件里的内容呢?第二,我新写入的字符’A’呢?哈哈哈要看帮助文档呀孩子,第一个就是fputc()函数是清空写,也就是会清空原本文件里的内容,然后再写入;再看第二个问题,其实在fopen的打开模式中,有这么一句话:

要对文件进行读写 *** 作,在写 *** 作和读 *** 作之间切换时,都要进行一定的 *** 作来修改文件指针此时的位置,也不难理解,之前说到会有一个文件指针记录当前 *** 作的位置,那么再输入字符’A‘过后,指针就来到了它的后边,也就是说再次调用fgetc()函数的时候,已经读取不到内容了,会直接结束输出,但是我这里遇到了一个新的问题,就是多产生了一行空行,具体是什么我也不清楚,希望有知道原因的小伙伴告诉我哈哈哈,我猜想会不会是缓冲流中的内容。

好,言归正传,已经知道原因了,怎么解决呢,来看一下解决办法,这里是使用的第二种方法,文件定位函数,因为这个在最后还会再做介绍:

我们把文件指针重新指向开头,那么再次读取的时候就不会出现问题了,关于fseek这个函数不用担心,文末会再次介绍。

好,我们下来再看另外一种情况,可不可以利用fputc()来连续写入多个字符呢,我们先来看最简单的一种:

	FILEif fp(
	!(=fopenfp ( "D:\test.txt","r+") ))printf(
	{
		"Error!");exit(
		-1);}fputc
	(
	'A',); fpfputc(
	'B',); fpfputc(
	'C',); fpfputc(
	'D',); fpfseek(

	,0fp, SEEK_SET) ;char;
	while ch(
	( =fgetcch ( ))fp!=EOF ) printf(
	{
		"%c",); ch}fclose
	(

	);fpintfputs

大家不妨来猜测一下结果是什么呢,是只有一个D,还是ABCD都有呢?答案是后者:

为什么呢,前边不是说fputc是清空写吗?那文件的内容应该是最后一次写入的内容呀,那么这就是可能忘了文件指针,还是那句话,在文件没有关闭之前,它都会记录当前 *** 作的位置,因此再第一个fput后,文件指针偏移,到第一个字符的末尾。

当然这个和清空写也不冲突,因为文件只打开了一次呀,文件打开后,再第一次调用fputc函数的地方会对文件进行清空,第二次调用fputc函数的时候,文件并没有再次打开,因此不会再清空文件了。

4.2 写入字符串:fputs()

 看一下官方的函数定义:

( constchar
   * , *str)
   FILE ;stream
char[

看了这么多,这下应该简单了哈,第一个参数就是自己要写入的字符串,第二个参数就是指定的文件。

直接看一下效果:

	] str="I am invincible!  --- 灭霸" ; fputs(
	,)str; fpchar[

注意,这个函数也是清空写,这里利用之前讲到的fread函数来看一下输出结果:

细心一点不难发现,这里是没有换行符的,但是在实际运用中,这一次输入后,为避免下一次输入紧贴在上一次的文本后边,是要换行的,这个可以自己手动在字符串结尾添加一个换行符:

] str="I am invincible!  --- 灭霸\r\n" ; size_tfwrite
4.3 想写多少写多少:fwrite()

 看一下官方的函数定义:

( constvoid
   * , size_tbuffer,
   size_t size,
   * count)
   FILE ;stream
char[

这个函数的结构和fwrite结构大致相同,因此这里就不做介绍了,具体可到官方文档中去查看。

首先我们来看一个最简单的例子,向文件中写入一段话:

	] str="我是天才哈哈哈哈哈哈哈哈哈哈哈" ; fwrite(
	,1str, strlen( ),str); fptypedefstruct

具体效果就不显示了,不过注意这个也是清空写。

但是一般我们不是用来写一段话的哈,为什么说这个函数这么强大呢,因为他不仅可以写一段话进去,我们大多时候会写一个数组,或者甚至是一堆结构体数组元素进去,这个也能做到。

比如:

Person char [ {
	10 name];char[
	4 sex];int;
	} age;
voidPersonmain
( )*=
{
	
	FILEfopen fp ( "D://test.txt","w+") ;if(
	== NULLfp ) exit( -1);="张三"

	Person p1 , { "男",12}; fwrite(
	&,sizeofp1(),Person1,);fpfclose(

	);fp}int
fseek
4. fseek()函数

 在很多时候,我们读取或者写入文件并不是从文件头开始的,我们会指定一个位置开始写,因此这里简单介绍一下文件指针定位函数:

( *,
   FILE longstream,
   int offset)
   ; origin
原始值含义

介绍一下这三个参数:第一个就是指明要对哪个文件进行 *** 作,也就是要 *** 作的文件流指针;第二个参数offfset,看名字就知道这个是偏移量,那么有偏移量肯定要指明是相对于哪个位置的偏移量,这就是第三个参数的含义,指明偏移相对的位置,通常有如下三种:

fseek(
SEEK_CUR文件指针的当前位置
SEEK_END文件尾
SEEK_SET文件头

比如,我想从当前位置往后第十个位置开始写,对应就是这样的:

,10fp,SEEK_CUR);fseek(

又如,从当前位置往前数是个位置开始写,猜对啦,就是写-10:

,-fp10,SEEK_CUR);[+++][+++]
5. 结尾语

 这篇文章是心血来潮突然想写的,平时我用的也不多,文中难免有许多错误的地方,如有发现错误的伙伴请积极留言,帮助我解决哈哈哈。

本文适用于那些第一次接触文件 *** 作的同学们,例子不多,仅供参考。

其实最好的办法还是官方的帮助文档和自己的多练习!!!

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
Error[8]: Undefined offset: 595, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

目录

1.引言

 最近朋友在负责收集班上同学的青年大学习截图,每次都要统计那些人没交,很是困扰,于是找到我有没有办法可以一次性的统计。

我拿到这个问题的时候,已知是那些截图都是已经命名好了,比如张三.png,李四.jpg,我的思路是将这些名字统计到一个文件里,这个可以用脚本语言一句话实现,如下所示,其中的LIST_all是自己重定向输出的文件,这里不做过多介绍。

DIR *.* /B> LIST_all.txt

 再利用C语言文件 *** 作对这些统计好名字和班级所有人的名字库进行一一匹配,便可得出没有交青年大学习截图的同学名字,这其中我在文件 *** 作方面花费了大量时间,因此这里我对文件 *** 作表达一些个人的看法。

2.打开文件和关闭文件

 对文件的 *** 作,文件的打开和关闭 *** 作极其重要,必须成对出现,也就是说文件在打开之后,如果不再使用文件,必须关闭关闭文件。

实际上,文件的打开也就是在内存中为其建立相应的信息区和文件缓冲区,在打开文件的时候通常会有一个FILE类型的指针变量指向该文件,以后才能对文件进行 *** 作;而文件的关闭就是插销之前建立的信息区和文件缓冲区释放内存。

如果文件使用结束后不进行关闭 *** 作,就可能导致内存的泄露或者文件内容的损坏。

2.1 打开文件:fopen()函数

 首先看一下官方的函数定义:

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

 这里给出一个方法,在VS中选中函数后按F1就可以查看这个函数的帮助文档,当然了,这个文档是在线的。

首先看到定义了两个参数,第一个就是文件名,这里文件名可以使用相对路径也可以使用绝对路径:

	FILE* fp1;
	fp1 = fopen("D:\MyFiles\test.txt","w");  //绝对路径
	FILE* fp2;
	FILE* fp2 = fopen("test.txt", "w");  //相对路径

需要注意的是,在使用绝对路径时,直接复制过来的路径通常时一个反斜杠,这会被转义,所以需要手动添加一个反斜杠。

再来看第二个参数,这是指打开的模式,用于指定针对文件请求的访问类型,常见的有如下几种模式,也可以去官网docs.microsoft.com查看具体的介绍:

modeAccess
“r”打开以便读取。

如果文件不存在或找不到, fopen 调用将失败。

“w”打开用于写入的空文件。

如果给定文件存在,则其内容会被销毁。

“a”在文件末尾打开以进行写入(追加),在新数据写入到文件之前不移除文件末尾 (EOF) 标记。

创建文件(如果文件不存在)。

“r+”打开以便读取和写入。

文件必须存在。

“w+”打开用于读取和写入的空文件。

如果文件存在,则其内容会被销毁。

“a+"打开以进行读取和追加。

追加 *** 作包括在新数据写入文件之前移除 EOF 标记。

写入完成后,EOF 标记不会还原。

创建文件(如果文件不存在)。

 那么为什么要用一个FILE类型的指针来接收他呢,这就是在引言中提到的文件指针变量,以后对文件的 *** 作就是利用这个指针变量的。

2.2 关闭文件:fclose()函数

关闭文件直接利用这个函数就可以了,通常在结束对文件的 *** 作后立即调用这个函数:

fclose(fp);
3. 对文件的读 *** 作 3.1 读取一个字符:fgetc()

 从这里开始就要利用的文件指针了,用于记录当前已经 *** 作的具体位置,当然也可以通过fseek()函数来重置位置。

具体的 *** 作如下所示:

	char ch;
	ch = fgetc(fp);

此时fgetc()函数会将文件中(这里不是指目录)的第一个字符返回,例如我有如下文件:

那么显示的应该是“先”字,如下所示:


但是可能会出现乱码的问题,我的是在windows10下的VS2019版本,通过查阅后发现是编码问题,默认是gbk编码,而文本是utf8编码,因此可以将刚刚那个test.txt的编码格式转变为ANSI后,再次尝试,转换编码的方式(文件>另存为)如下:

但是我出现的问题是,即使转变了编码方式,也不能正常的显示,需要通过两个字符来接收,才能成功显示这个汉字(但是对于字母和数字不会出现这种问题,这个我已经试过了),如下所示:

当然了,为什么fgec()是读取的第一个字符,那么两个getc读取到的内容应该都是一样的,这个在后文会做介绍。

其实出现这个问题的原因是在C语言中char是一个字节,而在windows中按照utf-8的编码方式占两个字节,其他编码方式甚至更多,因此在这里采用一个字符串数组来接收fgetc函数得到的汉字(函数返回本身是汉字的ASCII值),如下所示:

好了,详细的问题就不再介绍了,言归正传,为了方便介绍,不再用汉字作为例子,下边采用如下的例子,之后的函数讲解都基于此文件:test.txt:

前文说到,fgetc是读取文本中的第一个字符,并返回其对应值,那么两个连续的fgetc是不是读取到的是一样的内容呢?答案是否定的,这就涉及到文件指针了,在利用fopen函数打开文件的时候,利用了一个文件指针fp,也正就是这个指针使得顺序的两个fgetc读取的是文件中顺序的两个字符,因为fp会记录当前 *** 作的位置,比如第一个fgetc后,fp的位置就来到了第二个字符上,因此第二个fgetc读取到的就是第二个字符,因为刚刚是汉字,占用的字节数不同,下边用字母来演示一下:

既然可以做到读两个连续的字符,那么推广至三个,四个甚至多个呢,当然也是可以的。

好,现在来一个需求,如何读取文件的第一行呢,我们是不知道一行有多少的字符的,显然是不能写n个fgetc函数来读取一行。

对的,换行的标志是什么呢,’\n’,那么这就简单了,这就是循环的终止条件。

如下所示:

	char ch;
	while ((ch = fgetc(fp) )!= '\n')
	{
		printf("%c",ch);
	}

再来看一下结果:

大家可能会有一个疑问,这不是第一段吗?这里说明一下,对的,这个是我在网上复制的一篇英语作文,虽然在上边看到的是第一段占了几行,但是这是为了方便看我把文本文档调成了自动换行模式哈,也就是说,这个第一段是写在一行上边的。

好,这下再提一个需求,那么可以读取一行的内容了,那两行,三行呢,这当然也是可以的,比如说我要读取这个文本的前两行内容,应该是到,to be parents.那里结束,那就是第二次出现‘\n’的时候结束循环嘛,可以设计一个循环终止标记,当达到第二个’\n’时终止循环,如下:

	char ch;
	int index = 2;
	while (index) 
	{
		ch = fgetc(fp);
		printf("%c",ch);
		if (ch == '\n') index--;
	}

代码中的index就是设置的读取行数,当index为0的时候就跳出循环了,结果如下:

可能有人会想,利用这个方法,那么是不是可以读取整个文本文档呢,这里会出现两个问题,第一我们事先是不知道这个文本有多少行的,那么无法确定index的值;第二个问题就是,很多时候在文本的最后一个段落的最后一个字符输入结束后,用户是不会再去按一个回车的,而是直接保存退出了,那么根据’\n’来判断段落的方法就失效了。

是不是就没有办法了呢,当然是有的,仔细看官方文档就会发现,文档中有那么一句:

在打开文件时,已经进行了文件安全性的检查,那么返回EOF就只有一个可能了,那就是到达文件尾了,那么这就比之前的读取两三行还要简单,如下所示:

	char ch;
	while ((ch = fgetc(fp)) != EOF)
	{
		printf("%c", ch);
	}

不出意外的话,结果如下所示:

这里对EOF做一个说明,它是一个C语言预定义的宏,是end of file 的缩写,可以参考文章《EOF宏,C语言EOF宏详解》。

3.2 读取一行:fgets()

 其实对一行的读取,有一个单独的函数,也就是接下来要介绍的这个函数fgets(),和fetc()函数一样,在读取一行之后,文件指针也会移动的下一个字符,这里是以一行为 *** 作对象,那么下一个字符的位置是哪里呢?答对了,就是下一行的首字符所在的位置,值得注意的是,它会将文本中的换行符读取出来,不会保留在流中;另外,既然是读取一行字符,肯定不能单单用一个char来存,这里采用字符串也就是一个char数组来存储读取到的内容。

同样的,我们先来看一下官方的函数定义:

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

这里需要三个参数,下边分别说明这三个参数,第一个参数str就是数据的存储位置,就是你要把读取到的内容存在哪里;第二参数numChars,要读取的最大字符数,上边说到用的一个字符数组来存储,我可以定义一个200的数组,而给numChars赋予100的值,只要numChars的值小于数组长度就可以了,但是一般我用的时候都是两者相等。

好,来看一下实际的效果:

	char str[300];
	fgets(str, 300, fp);
	printf("%s", str);

看一下运行结果,是不是到第一段结尾it is a good idea.

再次说明一下哈,可能读着读着忘了,因为我的记事本设置了自动换行,原本我的第一段就是放在一行里边写的,只是为了好看,让你看着像是几行,实际就是一行哈,这里三段就是三行。

看下运行结果,是不是中间多了一个空行,但是我并没有在printf中添加’\n’,那是fgets函数本身的原因了,具体我也不太清楚,还在摸索中,我这里就不乱说了哈,大家知道请给我留言嘿嘿。


 好的我们再来用这个函数,完成对整个文档的读取,也只需要一个循环就好,那么怎么判断循环终止条件呢,也是查看文档也可以很容易的知道:

因此我们用是否为空来判断:

	char str[300];
	while (fgets(str, 300, fp)) {
		printf("%s", str);
	}

不出意外的话,结果如下所示:

3.3 想读多少读多少:fread()

 看标题就知道,为什么说是想读多少读多少呢,因为它不以行为单位,以字符为单位,但又能同时读取多个字符,因此相对于前边两个函数,可以说是很强大了。

第一步,看看函数的定义:

size_t fread(
   void *buffer,
   size_t size,
   size_t count,
   FILE *stream
);

功能更强大了,需要的参数也就多了,第一个参数,仍然是存储位置,因为这个函数可以读取多个字符,所以也是用一个字符数组来接收读取到的内容。

再来看一下第二个参数和第三个参数,第二参数是指每个单位字符的存储大小,以字节为单位,不能任意指定,比如说用的char数组,那么每个数组元素的大小是固定的一个字节,那么size的值就只能是1,大于1会出错,这个我已经试过了,小于1还没试过;为什么将第三个参数和第二个参数合着一起说呢,因为这两个参数就决定了字符的个数,count就是要读取的项的最大数量,实际读取的数量可以小于这个值,那么用count/size就是得到的字符数了。

最后一个参数是所有函数都有的,不然这个函数怎么知道是对谁 *** 作呢是吧。


 好我们下来看一看具体的 *** 作,假如我要读取前13个字符,那么就是到students结束,空格也算一个字符的哈,那么size的值取1,count的值取13:如下:

	char str[300];
	fread(str, 1, 13, fp);
	printf("%s",str);

得到的结果如下:

哇哦,在students后边为什么出现这么多烫烫烫,其实啊,这是因为在创建字符串的时候没有初始化,那么里边的内容是未知的,只用把创建str那一行初始化就好了,如下:

char str[300] = {0};

那么就可以得到正确结果了:

当然了,这种方法比慢,它需要在一开始给300个初始化,那么这个数字一旦变大了过后就很慢了,因此这里给出另外一种方法:fread函数的官方文档中,得知它的返回值是实际读取的项的个数,那么我们就可以去确定数组有有效元素的个数,也就是它的返回值,利用printf函数的特性,遇到’\0’时结束输出,那么我们就手动在有效元素后边加一个‘\0’嘛,这样速度就大大的提高了,如下:

	char str[300];
	int num = fread(str, 1, 13, fp);
	str[num] = ';'printf
	("%s",)str;char

好,我们现在来个需求,要求读取整个文档,仍然是通过循环来解决,同样的也是通过它的返回值,当fread读取不到内容的时候,返回0,这下就好办了:

	[ str300];while
	( int= num fread (,str1 ,200 ,) fp)[
	{
		str]num= ';' printf(
		"%s",); str}int
	fputc

不出意外的话,结果如下所示:

记住哦记住哦,使用之前一定要初始化!!!

4. 对文件的写 *** 作

 上边关于对文件读 *** 作的三个函数,其实也有三个与之对应的写 *** 作,分别是fputc(),fputs(),fwrite()。

说明一下,现在要对文件进行写 *** 作了,记得要把之前fopen中的那个模式参数改为写的,同时也建议新建一个txt。

好,下边我们来看一下这三个函数。

4.1 写入一个字符:fputc()

 一样滴一样滴,在 *** 作之前都来看一下这个函数的定义,查看一下官方文档:

( int,
   * c)
   FILE ;stream
*;

第一个参数就是自己要写入的字符,那么这里为什么是整型呢,因为每个字符都有一个自己对应的ASCII码值,这些信息在计算机底层都是按照二进制来存储的;第二个参数就是要 *** 作的文件,也就是指向文件结构的指针。


我们来看一下实际效果:

	FILEif fp(
	!(=fopenfp ( "D:\test.txt","r+") ))printf(
	{
		"Error!");exit(
		-1);}fputc
	(
	'A',); fpchar; 
		while ch(
	( =fgetcch ( ))fp!=EOF ) printf(
	{
		"%c",); ch}*
	;

这里要注意几个问题,第一注意我fopen的打开模式变为了,“r+”,是既可以对文件进行读 *** 作,又可以进行写 *** 作。

这里我先进行了写 *** 作,输入了一个字符’A’,然后进行输出看一下有没有成功。

好现在我们来看一下运行结果:

哎,这就奇了个怪了,怎么回事呢,一行空行。

第一,我原本文件里的内容呢?第二,我新写入的字符’A’呢?哈哈哈要看帮助文档呀孩子,第一个就是fputc()函数是清空写,也就是会清空原本文件里的内容,然后再写入;再看第二个问题,其实在fopen的打开模式中,有这么一句话:

要对文件进行读写 *** 作,在写 *** 作和读 *** 作之间切换时,都要进行一定的 *** 作来修改文件指针此时的位置,也不难理解,之前说到会有一个文件指针记录当前 *** 作的位置,那么再输入字符’A‘过后,指针就来到了它的后边,也就是说再次调用fgetc()函数的时候,已经读取不到内容了,会直接结束输出,但是我这里遇到了一个新的问题,就是多产生了一行空行,具体是什么我也不清楚,希望有知道原因的小伙伴告诉我哈哈哈,我猜想会不会是缓冲流中的内容。

好,言归正传,已经知道原因了,怎么解决呢,来看一下解决办法,这里是使用的第二种方法,文件定位函数,因为这个在最后还会再做介绍:

我们把文件指针重新指向开头,那么再次读取的时候就不会出现问题了,关于fseek这个函数不用担心,文末会再次介绍。

好,我们下来再看另外一种情况,可不可以利用fputc()来连续写入多个字符呢,我们先来看最简单的一种:

	FILEif fp(
	!(=fopenfp ( "D:\test.txt","r+") ))printf(
	{
		"Error!");exit(
		-1);}fputc
	(
	'A',); fpfputc(
	'B',); fpfputc(
	'C',); fpfputc(
	'D',); fpfseek(

	,0fp, SEEK_SET) ;char;
	while ch(
	( =fgetcch ( ))fp!=EOF ) printf(
	{
		"%c",); ch}fclose
	(

	);fpintfputs

大家不妨来猜测一下结果是什么呢,是只有一个D,还是ABCD都有呢?答案是后者:

为什么呢,前边不是说fputc是清空写吗?那文件的内容应该是最后一次写入的内容呀,那么这就是可能忘了文件指针,还是那句话,在文件没有关闭之前,它都会记录当前 *** 作的位置,因此再第一个fput后,文件指针偏移,到第一个字符的末尾。

当然这个和清空写也不冲突,因为文件只打开了一次呀,文件打开后,再第一次调用fputc函数的地方会对文件进行清空,第二次调用fputc函数的时候,文件并没有再次打开,因此不会再清空文件了。

4.2 写入字符串:fputs()

 看一下官方的函数定义:

( constchar
   * , *str)
   FILE ;stream
char[

看了这么多,这下应该简单了哈,第一个参数就是自己要写入的字符串,第二个参数就是指定的文件。

直接看一下效果:

	] str="I am invincible!  --- 灭霸" ; fputs(
	,)str; fpchar[

注意,这个函数也是清空写,这里利用之前讲到的fread函数来看一下输出结果:

细心一点不难发现,这里是没有换行符的,但是在实际运用中,这一次输入后,为避免下一次输入紧贴在上一次的文本后边,是要换行的,这个可以自己手动在字符串结尾添加一个换行符:

] str="I am invincible!  --- 灭霸\r\n" ; size_tfwrite
4.3 想写多少写多少:fwrite()

 看一下官方的函数定义:

( constvoid
   * , size_tbuffer,
   size_t size,
   * count)
   FILE ;stream
char[

这个函数的结构和fwrite结构大致相同,因此这里就不做介绍了,具体可到官方文档中去查看。

首先我们来看一个最简单的例子,向文件中写入一段话:

	] str="我是天才哈哈哈哈哈哈哈哈哈哈哈" ; fwrite(
	,1str, strlen( ),str); fptypedefstruct

具体效果就不显示了,不过注意这个也是清空写。

但是一般我们不是用来写一段话的哈,为什么说这个函数这么强大呢,因为他不仅可以写一段话进去,我们大多时候会写一个数组,或者甚至是一堆结构体数组元素进去,这个也能做到。

比如:

Person char [ {
	10 name];char[
	4 sex];int;
	} age;
voidPersonmain
( )*=
{
	
	FILEfopen fp ( "D://test.txt","w+") ;if(
	== NULLfp ) exit( -1);="张三"

	Person p1 , { "男",12}; fwrite(
	&,sizeofp1(),Person1,);fpfclose(

	);fp}int
fseek
4. fseek()函数

 在很多时候,我们读取或者写入文件并不是从文件头开始的,我们会指定一个位置开始写,因此这里简单介绍一下文件指针定位函数:

( *,
   FILE longstream,
   int offset)
   ; origin
原始值含义

介绍一下这三个参数:第一个就是指明要对哪个文件进行 *** 作,也就是要 *** 作的文件流指针;第二个参数offfset,看名字就知道这个是偏移量,那么有偏移量肯定要指明是相对于哪个位置的偏移量,这就是第三个参数的含义,指明偏移相对的位置,通常有如下三种:

fseek(
SEEK_CUR文件指针的当前位置
SEEK_END文件尾
SEEK_SET文件头

比如,我想从当前位置往后第十个位置开始写,对应就是这样的:

,10fp,SEEK_CUR);fseek(

又如,从当前位置往前数是个位置开始写,猜对啦,就是写-10:

,-fp10,SEEK_CUR);[+++]
5. 结尾语

 这篇文章是心血来潮突然想写的,平时我用的也不多,文中难免有许多错误的地方,如有发现错误的伙伴请积极留言,帮助我解决哈哈哈。

本文适用于那些第一次接触文件 *** 作的同学们,例子不多,仅供参考。

其实最好的办法还是官方的帮助文档和自己的多练习!!!

)
File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
C语言文件 *** 作_C_内存溢出

C语言文件 *** 作

C语言文件 *** 作,第1张

目录
    • 1.引言
    • 2.打开文件和关闭文件
      • 2.1 打开文件:fopen()函数
      • 2.2 关闭文件:fclose()函数
    • 3. 对文件的读 *** 作
      • 3.1 读取一个字符:fgetc()
      • 3.2 读取一行:fgets()
      • 3.3 想读多少读多少:fread()
    • 4. 对文件的写 *** 作
      • 4.1 写入一个字符:fputc()
      • 4.2 写入字符串:fputs()
      • 4.3 想写多少写多少:fwrite()
    • 4. fseek()函数
    • 5. 结尾语

1.引言

 最近朋友在负责收集班上同学的青年大学习截图,每次都要统计那些人没交,很是困扰,于是找到我有没有办法可以一次性的统计。

我拿到这个问题的时候,已知是那些截图都是已经命名好了,比如张三.png,李四.jpg,我的思路是将这些名字统计到一个文件里,这个可以用脚本语言一句话实现,如下所示,其中的LIST_all是自己重定向输出的文件,这里不做过多介绍。

DIR *.* /B> LIST_all.txt

 再利用C语言文件 *** 作对这些统计好名字和班级所有人的名字库进行一一匹配,便可得出没有交青年大学习截图的同学名字,这其中我在文件 *** 作方面花费了大量时间,因此这里我对文件 *** 作表达一些个人的看法。

2.打开文件和关闭文件

 对文件的 *** 作,文件的打开和关闭 *** 作极其重要,必须成对出现,也就是说文件在打开之后,如果不再使用文件,必须关闭关闭文件。

实际上,文件的打开也就是在内存中为其建立相应的信息区和文件缓冲区,在打开文件的时候通常会有一个FILE类型的指针变量指向该文件,以后才能对文件进行 *** 作;而文件的关闭就是插销之前建立的信息区和文件缓冲区释放内存。

如果文件使用结束后不进行关闭 *** 作,就可能导致内存的泄露或者文件内容的损坏。

2.1 打开文件:fopen()函数

 首先看一下官方的函数定义:

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

 这里给出一个方法,在VS中选中函数后按F1就可以查看这个函数的帮助文档,当然了,这个文档是在线的。

首先看到定义了两个参数,第一个就是文件名,这里文件名可以使用相对路径也可以使用绝对路径:

	FILE* fp1;
	fp1 = fopen("D:\MyFiles\test.txt","w");  //绝对路径
	FILE* fp2;
	FILE* fp2 = fopen("test.txt", "w");  //相对路径

需要注意的是,在使用绝对路径时,直接复制过来的路径通常时一个反斜杠,这会被转义,所以需要手动添加一个反斜杠。

再来看第二个参数,这是指打开的模式,用于指定针对文件请求的访问类型,常见的有如下几种模式,也可以去官网docs.microsoft.com查看具体的介绍:

modeAccess
“r”打开以便读取。

如果文件不存在或找不到, fopen 调用将失败。

“w”打开用于写入的空文件。

如果给定文件存在,则其内容会被销毁。

“a”在文件末尾打开以进行写入(追加),在新数据写入到文件之前不移除文件末尾 (EOF) 标记。

创建文件(如果文件不存在)。

“r+”打开以便读取和写入。

文件必须存在。

“w+”打开用于读取和写入的空文件。

如果文件存在,则其内容会被销毁。

“a+"打开以进行读取和追加。

追加 *** 作包括在新数据写入文件之前移除 EOF 标记。

写入完成后,EOF 标记不会还原。

创建文件(如果文件不存在)。

 那么为什么要用一个FILE类型的指针来接收他呢,这就是在引言中提到的文件指针变量,以后对文件的 *** 作就是利用这个指针变量的。

2.2 关闭文件:fclose()函数

关闭文件直接利用这个函数就可以了,通常在结束对文件的 *** 作后立即调用这个函数:

fclose(fp);
3. 对文件的读 *** 作 3.1 读取一个字符:fgetc()

 从这里开始就要利用的文件指针了,用于记录当前已经 *** 作的具体位置,当然也可以通过fseek()函数来重置位置。

具体的 *** 作如下所示:

	char ch;
	ch = fgetc(fp);

此时fgetc()函数会将文件中(这里不是指目录)的第一个字符返回,例如我有如下文件:

那么显示的应该是“先”字,如下所示:


但是可能会出现乱码的问题,我的是在windows10下的VS2019版本,通过查阅后发现是编码问题,默认是gbk编码,而文本是utf8编码,因此可以将刚刚那个test.txt的编码格式转变为ANSI后,再次尝试,转换编码的方式(文件>另存为)如下:

但是我出现的问题是,即使转变了编码方式,也不能正常的显示,需要通过两个字符来接收,才能成功显示这个汉字(但是对于字母和数字不会出现这种问题,这个我已经试过了),如下所示:

当然了,为什么fgec()是读取的第一个字符,那么两个getc读取到的内容应该都是一样的,这个在后文会做介绍。

其实出现这个问题的原因是在C语言中char是一个字节,而在windows中按照utf-8的编码方式占两个字节,其他编码方式甚至更多,因此在这里采用一个字符串数组来接收fgetc函数得到的汉字(函数返回本身是汉字的ASCII值),如下所示:

好了,详细的问题就不再介绍了,言归正传,为了方便介绍,不再用汉字作为例子,下边采用如下的例子,之后的函数讲解都基于此文件:test.txt:

前文说到,fgetc是读取文本中的第一个字符,并返回其对应值,那么两个连续的fgetc是不是读取到的是一样的内容呢?答案是否定的,这就涉及到文件指针了,在利用fopen函数打开文件的时候,利用了一个文件指针fp,也正就是这个指针使得顺序的两个fgetc读取的是文件中顺序的两个字符,因为fp会记录当前 *** 作的位置,比如第一个fgetc后,fp的位置就来到了第二个字符上,因此第二个fgetc读取到的就是第二个字符,因为刚刚是汉字,占用的字节数不同,下边用字母来演示一下:

既然可以做到读两个连续的字符,那么推广至三个,四个甚至多个呢,当然也是可以的。

好,现在来一个需求,如何读取文件的第一行呢,我们是不知道一行有多少的字符的,显然是不能写n个fgetc函数来读取一行。

对的,换行的标志是什么呢,’\n’,那么这就简单了,这就是循环的终止条件。

如下所示:

	char ch;
	while ((ch = fgetc(fp) )!= '\n')
	{
		printf("%c",ch);
	}

再来看一下结果:

大家可能会有一个疑问,这不是第一段吗?这里说明一下,对的,这个是我在网上复制的一篇英语作文,虽然在上边看到的是第一段占了几行,但是这是为了方便看我把文本文档调成了自动换行模式哈,也就是说,这个第一段是写在一行上边的。

好,这下再提一个需求,那么可以读取一行的内容了,那两行,三行呢,这当然也是可以的,比如说我要读取这个文本的前两行内容,应该是到,to be parents.那里结束,那就是第二次出现‘\n’的时候结束循环嘛,可以设计一个循环终止标记,当达到第二个’\n’时终止循环,如下:

	char ch;
	int index = 2;
	while (index) 
	{
		ch = fgetc(fp);
		printf("%c",ch);
		if (ch == '\n') index--;
	}

代码中的index就是设置的读取行数,当index为0的时候就跳出循环了,结果如下:

可能有人会想,利用这个方法,那么是不是可以读取整个文本文档呢,这里会出现两个问题,第一我们事先是不知道这个文本有多少行的,那么无法确定index的值;第二个问题就是,很多时候在文本的最后一个段落的最后一个字符输入结束后,用户是不会再去按一个回车的,而是直接保存退出了,那么根据’\n’来判断段落的方法就失效了。

是不是就没有办法了呢,当然是有的,仔细看官方文档就会发现,文档中有那么一句:

在打开文件时,已经进行了文件安全性的检查,那么返回EOF就只有一个可能了,那就是到达文件尾了,那么这就比之前的读取两三行还要简单,如下所示:

	char ch;
	while ((ch = fgetc(fp)) != EOF)
	{
		printf("%c", ch);
	}

不出意外的话,结果如下所示:

这里对EOF做一个说明,它是一个C语言预定义的宏,是end of file 的缩写,可以参考文章《EOF宏,C语言EOF宏详解》。

3.2 读取一行:fgets()

 其实对一行的读取,有一个单独的函数,也就是接下来要介绍的这个函数fgets(),和fetc()函数一样,在读取一行之后,文件指针也会移动的下一个字符,这里是以一行为 *** 作对象,那么下一个字符的位置是哪里呢?答对了,就是下一行的首字符所在的位置,值得注意的是,它会将文本中的换行符读取出来,不会保留在流中;另外,既然是读取一行字符,肯定不能单单用一个char来存,这里采用字符串也就是一个char数组来存储读取到的内容。

同样的,我们先来看一下官方的函数定义:

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

这里需要三个参数,下边分别说明这三个参数,第一个参数str就是数据的存储位置,就是你要把读取到的内容存在哪里;第二参数numChars,要读取的最大字符数,上边说到用的一个字符数组来存储,我可以定义一个200的数组,而给numChars赋予100的值,只要numChars的值小于数组长度就可以了,但是一般我用的时候都是两者相等。

好,来看一下实际的效果:

	char str[300];
	fgets(str, 300, fp);
	printf("%s", str);

看一下运行结果,是不是到第一段结尾it is a good idea.

再次说明一下哈,可能读着读着忘了,因为我的记事本设置了自动换行,原本我的第一段就是放在一行里边写的,只是为了好看,让你看着像是几行,实际就是一行哈,这里三段就是三行。

看下运行结果,是不是中间多了一个空行,但是我并没有在printf中添加’\n’,那是fgets函数本身的原因了,具体我也不太清楚,还在摸索中,我这里就不乱说了哈,大家知道请给我留言嘿嘿。


 好的我们再来用这个函数,完成对整个文档的读取,也只需要一个循环就好,那么怎么判断循环终止条件呢,也是查看文档也可以很容易的知道:

因此我们用是否为空来判断:

	char str[300];
	while (fgets(str, 300, fp)) {
		printf("%s", str);
	}

不出意外的话,结果如下所示:

3.3 想读多少读多少:fread()

 看标题就知道,为什么说是想读多少读多少呢,因为它不以行为单位,以字符为单位,但又能同时读取多个字符,因此相对于前边两个函数,可以说是很强大了。

第一步,看看函数的定义:

size_t fread(
   void *buffer,
   size_t size,
   size_t count,
   FILE *stream
);

功能更强大了,需要的参数也就多了,第一个参数,仍然是存储位置,因为这个函数可以读取多个字符,所以也是用一个字符数组来接收读取到的内容。

再来看一下第二个参数和第三个参数,第二参数是指每个单位字符的存储大小,以字节为单位,不能任意指定,比如说用的char数组,那么每个数组元素的大小是固定的一个字节,那么size的值就只能是1,大于1会出错,这个我已经试过了,小于1还没试过;为什么将第三个参数和第二个参数合着一起说呢,因为这两个参数就决定了字符的个数,count就是要读取的项的最大数量,实际读取的数量可以小于这个值,那么用count/size就是得到的字符数了。

最后一个参数是所有函数都有的,不然这个函数怎么知道是对谁 *** 作呢是吧。


 好我们下来看一看具体的 *** 作,假如我要读取前13个字符,那么就是到students结束,空格也算一个字符的哈,那么size的值取1,count的值取13:如下:

	char str[300];
	fread(str, 1, 13, fp);
	printf("%s",str);

得到的结果如下:

哇哦,在students后边为什么出现这么多烫烫烫,其实啊,这是因为在创建字符串的时候没有初始化,那么里边的内容是未知的,只用把创建str那一行初始化就好了,如下:

char str[300] = {0};

那么就可以得到正确结果了:

当然了,这种方法比慢,它需要在一开始给300个初始化,那么这个数字一旦变大了过后就很慢了,因此这里给出另外一种方法:fread函数的官方文档中,得知它的返回值是实际读取的项的个数,那么我们就可以去确定数组有有效元素的个数,也就是它的返回值,利用printf函数的特性,遇到’\0’时结束输出,那么我们就手动在有效元素后边加一个‘\0’嘛,这样速度就大大的提高了,如下:

	char str[300];
	int num = fread(str, 1, 13, fp);
	str[num] = ';'printf
	("%s",)str;char

好,我们现在来个需求,要求读取整个文档,仍然是通过循环来解决,同样的也是通过它的返回值,当fread读取不到内容的时候,返回0,这下就好办了:

	[ str300];while
	( int= num fread (,str1 ,200 ,) fp)[
	{
		str]num= ';' printf(
		"%s",); str}int
	fputc

不出意外的话,结果如下所示:

记住哦记住哦,使用之前一定要初始化!!!

4. 对文件的写 *** 作

 上边关于对文件读 *** 作的三个函数,其实也有三个与之对应的写 *** 作,分别是fputc(),fputs(),fwrite()。

说明一下,现在要对文件进行写 *** 作了,记得要把之前fopen中的那个模式参数改为写的,同时也建议新建一个txt。

好,下边我们来看一下这三个函数。

4.1 写入一个字符:fputc()

 一样滴一样滴,在 *** 作之前都来看一下这个函数的定义,查看一下官方文档:

( int,
   * c)
   FILE ;stream
*;

第一个参数就是自己要写入的字符,那么这里为什么是整型呢,因为每个字符都有一个自己对应的ASCII码值,这些信息在计算机底层都是按照二进制来存储的;第二个参数就是要 *** 作的文件,也就是指向文件结构的指针。


我们来看一下实际效果:

	FILEif fp(
	!(=fopenfp ( "D:\test.txt","r+") ))printf(
	{
		"Error!");exit(
		-1);}fputc
	(
	'A',); fpchar; 
		while ch(
	( =fgetcch ( ))fp!=EOF ) printf(
	{
		"%c",); ch}*
	;

这里要注意几个问题,第一注意我fopen的打开模式变为了,“r+”,是既可以对文件进行读 *** 作,又可以进行写 *** 作。

这里我先进行了写 *** 作,输入了一个字符’A’,然后进行输出看一下有没有成功。

好现在我们来看一下运行结果:

哎,这就奇了个怪了,怎么回事呢,一行空行。

第一,我原本文件里的内容呢?第二,我新写入的字符’A’呢?哈哈哈要看帮助文档呀孩子,第一个就是fputc()函数是清空写,也就是会清空原本文件里的内容,然后再写入;再看第二个问题,其实在fopen的打开模式中,有这么一句话:

要对文件进行读写 *** 作,在写 *** 作和读 *** 作之间切换时,都要进行一定的 *** 作来修改文件指针此时的位置,也不难理解,之前说到会有一个文件指针记录当前 *** 作的位置,那么再输入字符’A‘过后,指针就来到了它的后边,也就是说再次调用fgetc()函数的时候,已经读取不到内容了,会直接结束输出,但是我这里遇到了一个新的问题,就是多产生了一行空行,具体是什么我也不清楚,希望有知道原因的小伙伴告诉我哈哈哈,我猜想会不会是缓冲流中的内容。

好,言归正传,已经知道原因了,怎么解决呢,来看一下解决办法,这里是使用的第二种方法,文件定位函数,因为这个在最后还会再做介绍:

我们把文件指针重新指向开头,那么再次读取的时候就不会出现问题了,关于fseek这个函数不用担心,文末会再次介绍。

好,我们下来再看另外一种情况,可不可以利用fputc()来连续写入多个字符呢,我们先来看最简单的一种:

	FILEif fp(
	!(=fopenfp ( "D:\test.txt","r+") ))printf(
	{
		"Error!");exit(
		-1);}fputc
	(
	'A',); fpfputc(
	'B',); fpfputc(
	'C',); fpfputc(
	'D',); fpfseek(

	,0fp, SEEK_SET) ;char;
	while ch(
	( =fgetcch ( ))fp!=EOF ) printf(
	{
		"%c",); ch}fclose
	(

	);fpintfputs

大家不妨来猜测一下结果是什么呢,是只有一个D,还是ABCD都有呢?答案是后者:

为什么呢,前边不是说fputc是清空写吗?那文件的内容应该是最后一次写入的内容呀,那么这就是可能忘了文件指针,还是那句话,在文件没有关闭之前,它都会记录当前 *** 作的位置,因此再第一个fput后,文件指针偏移,到第一个字符的末尾。

当然这个和清空写也不冲突,因为文件只打开了一次呀,文件打开后,再第一次调用fputc函数的地方会对文件进行清空,第二次调用fputc函数的时候,文件并没有再次打开,因此不会再清空文件了。

4.2 写入字符串:fputs()

 看一下官方的函数定义:

( constchar
   * , *str)
   FILE ;stream
char[

看了这么多,这下应该简单了哈,第一个参数就是自己要写入的字符串,第二个参数就是指定的文件。

直接看一下效果:

	] str="I am invincible!  --- 灭霸" ; fputs(
	,)str; fpchar[

注意,这个函数也是清空写,这里利用之前讲到的fread函数来看一下输出结果:

细心一点不难发现,这里是没有换行符的,但是在实际运用中,这一次输入后,为避免下一次输入紧贴在上一次的文本后边,是要换行的,这个可以自己手动在字符串结尾添加一个换行符:

] str="I am invincible!  --- 灭霸\r\n" ; size_tfwrite
4.3 想写多少写多少:fwrite()

 看一下官方的函数定义:

( constvoid
   * , size_tbuffer,
   size_t size,
   * count)
   FILE ;stream
char[

这个函数的结构和fwrite结构大致相同,因此这里就不做介绍了,具体可到官方文档中去查看。

首先我们来看一个最简单的例子,向文件中写入一段话:

	] str="我是天才哈哈哈哈哈哈哈哈哈哈哈" ; fwrite(
	,1str, strlen( ),str); fptypedefstruct

具体效果就不显示了,不过注意这个也是清空写。

但是一般我们不是用来写一段话的哈,为什么说这个函数这么强大呢,因为他不仅可以写一段话进去,我们大多时候会写一个数组,或者甚至是一堆结构体数组元素进去,这个也能做到。

比如:

Person char [ {
	10 name];char[
	4 sex];int;
	} age;
voidPersonmain
( )*=
{
	
	FILEfopen fp ( "D://test.txt","w+") ;if(
	== NULLfp ) exit( -1);="张三"

	Person p1 , { "男",12}; fwrite(
	&,sizeofp1(),Person1,);fpfclose(

	);fp}int
fseek
4. fseek()函数

 在很多时候,我们读取或者写入文件并不是从文件头开始的,我们会指定一个位置开始写,因此这里简单介绍一下文件指针定位函数:

( *,
   FILE longstream,
   int offset)
   ; origin
原始值含义

介绍一下这三个参数:第一个就是指明要对哪个文件进行 *** 作,也就是要 *** 作的文件流指针;第二个参数offfset,看名字就知道这个是偏移量,那么有偏移量肯定要指明是相对于哪个位置的偏移量,这就是第三个参数的含义,指明偏移相对的位置,通常有如下三种:

fseek(
SEEK_CUR文件指针的当前位置
SEEK_END文件尾
SEEK_SET文件头

比如,我想从当前位置往后第十个位置开始写,对应就是这样的:

,10fp,SEEK_CUR);fseek(

又如,从当前位置往前数是个位置开始写,猜对啦,就是写-10:

,-fp10,SEEK_CUR);
5. 结尾语

 这篇文章是心血来潮突然想写的,平时我用的也不多,文中难免有许多错误的地方,如有发现错误的伙伴请积极留言,帮助我解决哈哈哈。

本文适用于那些第一次接触文件 *** 作的同学们,例子不多,仅供参考。

其实最好的办法还是官方的帮助文档和自己的多练习!!!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/662272.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-18
下一篇 2022-04-18

发表评论

登录后才能评论

评论列表(0条)

保存