【C语言学习笔记】文件操作处理

本文详细介绍了C语言中文件的基础知识,包括文件分类、文件指针的使用,以及文件的打开、关闭、读写操作。涵盖了字符和字符串的读写、格式化读写函数、数据块的读写,还讨论了文件的随机读写功能,如fseek()、rewind()和ftell()函数。此外,还讲解了文件管理函数,如删除、重命名和复制文件的方法。
摘要由CSDN通过智能技术生成

目录

1. 文件基础

1.1 文件分类

1.2 文件指针

2. 文件的打开和关闭

3. 文件的读/写操作

3.1 字符的读写

3.2 字符串的读写

3.3 格式化读写函数

3.4 数据块的读写函数

3.5 其他读写函数

4. 文件的随机读写

4.1 fseek()函数

4.2rewind()函数

4.3 ftell()函数

5. 文件管理函数

5.1 删除文件

5.2 重命名文件

5.3 复制文件

6. 文件状态检测函数

6.1 feof()函数

6.2 ferror()函数

6.3 clearerr()函数


1. 文件基础

1.1 文件分类

用户角度,文件可分为:
1)普通文件:是指驻留在磁盘或其他外部介质上的有序数据集,程序文件、数据文件。
2)设备文件:是指与主机相连的各种外部设备,如显示器、打印机、键盘等。在操作系统中,把外部设备也看做是一个文件来进行管理,把它们的输入、输出等同于对磁盘文件的读和写。
—— 通常把显示器定义为标准输出文件,一般情况下在屏幕上显示有关信息就是向标准输出文件输出。如前面经常使用的printf()、putchar()函数就属于这类输出。
—— 通常把键盘指定为标准的输入文件,从键盘上输入就意味着从标准输入文件上输入数据。scanf,getchar函数就属于这类输入。

文件编码方式,文件可分为:
1) ASCII文件:也称为文本文件,这种文件在磁盘中存放时每个字符对应一个字节,用于存放对应的 ASCII 码。ASCII 码文件可在屏幕上按字符显示,例如源程序文件就是ASCII 文件,用DOS命令TYPE可显示文件的内容。由于是按字符显示,因此能读懂文件内容。
2)二进制文件:按二进制的编码方式来存放文件。
例如 5678的存储形式为:00010110 00101110
只占2字节。二进制文件虽然也可在屏幕上显示,但其内容无法读懂。C系统在处理这些文件时,并不区分类型,都看成是字符流,按字节进行处理。
输入/输出字符流的开始和结束只由程序控制而不受物理符号(如回车符)的控制。因此也把这种文件称作“流式文件”

在C语言中,把文件看作一组字符或二进制数据的集合,也称为数据流。数据流的结束标志为-1,在C语言中,规定文件的结束标志为 EOF。EOF为一符号常量,定义在头文件“stdio.h”中,形式如下:

#define EOF(-1)

1.2 文件指针

指向文件的指针变量,通过文件指针可以对所指的文件进行各种操作。定义:

FILE *fp;

fp是指向FILE 结构的指针变量,通过fp可以找到存放某个文件信息的结构变量,按结构变量中的信息找到该文件,实施对文件的操作。

2. 文件的打开和关闭

FILE *fopen (const char *path,const char *mode);// 打开文件

FILE *close (FILE *stream);//关闭文件

FILE *fopen 形参:
1) const char *path:文件名称,用字符串表示。
        全名:目录+文件名:char szFileName [256]="E\\temp\\123.c"(注意是 \\ )
        仅文件名:系统确定,一般为当前目录名。
2) const char *mode:文件打开方式,同样用字符串表示。

        r打开文件必须存在、w打开 不存在会创建,如果打开的文件已经存在 将删除重建。
        a给文件追加新信息,文件必须已经存在。 
3) 函数返回值:FILE 类型指针。如果运行成功,fopen()返回文件的地址,否则返回NULL。用于判断文件打开是否成功 

        把一个文本文件读入内存时,要将 ASCII码转换成二进制码,而把文件以文本方式写入磁盘时,也要把二进制码转换成ASCII码,因此文本文件的读/写要花费较多的转换时间。对二进制文件的读/写不存在这种转换。
        标准输入文件(键盘)、标准输出文件(显示器)、标准出错输出(出错信息)是由系统打开的,可直接使用。

FILE *close 形参:

  • FILE*stream:打开文件的地址。
  • 函数返回值:int类型,如果为0,则表示文件关闭成功,否则表示失败。

3. 文件的读/写操作

3.1 字符的读写

读字符:getc()&fgetc()函数:完全相同互换。

ch = fgetc(fp);

此时文件必须是读或读写方式打开的。
在文件内部的位置指针用来指向文件的当前读/写字节。当打开文件时,该指针总是指向文件的第一个字节。使用fgetc()函数后,该位置指针将向后移动一个字节。因此可连续多次使用fgetc()函数来读取多个字符。

写字符:putc()&fputc()函数:

fputc('a',fp);

被写入的文件可以用 写、读写、追加方式打开;写、读写时,文件已存在将清除原来内容,从文件首开始写。文件不存在将创建。
每当写入一个字符,文件内部指针将向后移动一个字节。
返回值:成功写入,返回写入的字符,否则返回EOF。

#include <stdio.h>

int main(){
	FILE *fp;
	char ch;
	// 打开文件写入字符
	fp = fopen("test.txt","w+");
	if(fp != NULL){
		printf("请输入向写入文件的字符,以回车结束\n");
		ch = getchar();
		while(ch != '\n'){
			fputc(ch,fp);
			ch = getchar();
		}
		fclose(fp);
	}else printf("文件打开失败");
	
	// 打开文件读取字符
	fp = fopen("test.txt","r");
	do{
		ch = fgetc(fp);
		putchar(ch);
	}while(ch!=EOF);
	
	return 0;
}

3.2 字符串的读写

读取字符串:fgets()函数:从fp所指的文件中读取n-1个字符给字符数组str中。

fgets(str,n,fp);
  • str:字符数组名;
  • n:正整数,读取出字符串不超过n-1个字符(最后加上'\0');
  • fp:文件指针。
  • 如在n-1之前遇到了换行或EOF,则读出结束。
  • 返回值:为字符数组的首地址

写入字符串:fputs()函数:向fp所指的文件中写入一个字符串"abcd";

fputs("abcd",fp);

字符串可以是字符串常量、字符数组名、指针变量。

#include <stdio.h>
#include <conio.h> 	//getch()
#include <stdlib.h>	//exit()

int main () {
	FILE *fp;
	char ch,strin[20] ,strout[20];
	if((fp=fopen ( "123.txt" , "wt+"))==NULL)//打开文件123.txt
	{
		printf ( "can not open" );
		getch() ;
		exit(1);
	}
	printf ( "input a string : \n");
	gets(strin);//输入字符串
	fputs(strin,fp);//将该字符串写入123.txt文件中
	fclose(fp);//关闭文件
	
	if((fp=fopen("123.txt", "r"))==NULL)//再次打开文件123.txt
	{
		printf ( "can not open");
		getch() ;
		exit(1);
	}
	fgets (strout,21,fp);//从文件这个读取字符串
	puts(strout); //输出字符串
	fclose(fp);//关闭文件
	getch() ;
}

3.3 格式化读写函数

fscanf(fp,"%d%s",&i,&s);
fprintf(fp,"%d%s",j,ch);

scanf()和printf()读写对象是键盘和显示器;fscanf()和fprintf()读写对象是磁盘文件。

#include<stdio.h>
#include <conio.h> 	//getch()
#include <stdlib.h>	//exit()

struct stu {
	char name[10] ;
	int age;
}boya[2], boyb[2], *pp, *qq;

int main()
{
	FILE *fp;
	int i;
	pp=boya;
	qq=boyb;
	if((fp=fopen ("123.txt" , "wb+"))==NULL){
		printf( "Cannot open!" );
		getch();
		exit(1);
	}
	printf ( "\ninput data\n");
	for(i=0; i<2; i++,pp++){
		scanf("%s %d", pp->name ,&pp->age);
	}
	pp=boya;
	for(i=0; i<2; i++,pp++){
		fprintf(fp,"%s %d\n" , pp->name, pp->age);
	}
	rewind(fp);//将文件内指针指向开头
	for(i=0; i<=2; i++,qq++){
		fscanf(fp, "%s %d\n" , qq->name, &qq->age);
	}
	printf ( "\n\nname\tnumber\n" );
	qq=boyb;
	for(i=0; i<2; i++,qq++){
		printf("%s\t%5d\n", qq->name, qq->age);
	}
	fclose(fp);
	getch();
}

3.4 数据块的读写函数

fread()和fwrite()读写整块数据和一组数据:一个数组元素、一个结构变量的值。

fread(buffer,size,count,fp);
fwrite(buffer,size,count,fp);
  • buffer:指针,fread()中表示存放输入数据的首地址;fwrite()中表示存放输出数据的首地址。
  • size:表示数据块的字节数。
  • count:表示要读/写的数据块块数。
  • fp:表示文件指针。

函数fread()和fwrite()能够直接将内存中的一块信息以其二进制形式进行整体读/写,所以常用于实现二进制文件的读/写工作。在使用fwrite()函数将不同类型的数据写到同一文件中时,需要注意读取信息的顺序。

#include<stdio.h>
#include <conio.h> 	//getch()
#include <stdlib.h>	//exit()
#define N 2

struct stu{
	char name[10]; //姓名
	int num; //学号
	int age; //年龄
	float score; //成绩
}boya[N], boyb[N], *pa, *pb;

int main(){
	FILE *fp;
	int i;
	pa = boya;
	pb = boyb;
	if( (fp=fopen("F:\\demo.txt", "wb+")) == NULL ){
		printf("Cannot open file, press any key to exit!\n");
		getch();
		exit(1);
	}
	
	//从键盘输入2组数据
	printf("Input data:\n");
	for(i=0; i<N; i++,pa++){
		scanf("%s %d %d %f",pa->name, &pa->num,&pa->age, &pa->score);
	}
	//将数组 boya 的数据写入文件
	fwrite(boya, sizeof(struct stu), N, fp);
	//将文件指针重置到文件开头
	rewind(fp);
	//从文件读取数据并保存到数据 boyb
	fread(boyb, sizeof(struct stu), N, fp);
	//输出数组 boyb 中的数据
	for(i=0; i<N; i++,pb++){
		printf("%s %d %d %f\n", pb->name, pb->num, pb->age, pb->score);
	}
	fclose(fp);
	return 0;
}

3.5 其他读写函数

int getw (FILE *stream);

getw():从文件中读取一个字(2字节)的信息。

  • FILE*stream:文件地址。
  • 函数返回值:如果成功读取,则返回当前读入的信息,否则返回EOF。
int putw (int w,FILE *stream);

putw():向文件中写一个字信息,其返回值是当前写入的信息,是一个整数。

  • FILE*stream:文件地址。
  • int w:整型数据。
  • 函数返回值:如果成功,与输入参数w的值相等,否则返回EOF 。
#include "stdio.h"
#include <conio.h> 	//getch()
#include <stdlib.h>	//exit()

int main(){
	FILE *fp;
	int word;
	fp = fopen ( "123.txt","wb" );//打开二进制文件,只允许写数据
	if (fp == NULL)//判断返回值是否为空
	{ //若是则输出错误信息,并退出程序
		printf ("Error ! !\n");
		exit (1);
	}
	word = 100;
	if (putw (word+80,fp) == word+80)//将整数写入文件中
	{	
		printf ( "Successful write \n");
	}
	fclose(fp);//关闭文件
	fp =fopen("123.txt","rb");//重新打开二进制文件,只允许读数据
	if(fp ==NULL)//判断返回值是否为空
	{//若是则输出错误信息,并退出程序
			printf ( "Error opening file 123.txt\n" );
			exit(1);
	}
	word = getw(fp) ;//从文件中读取一个整数,赋给变量word
	printf ( "read: word = %d\n", word) ;
	fclose(fp);//关闭文件
	getch();
}

4. 文件的随机读写

  • 在计算机系统中,可以将文件理解为一个完整的数据流,可以将“数据流”分为文件头、文件尾和文件主体三个部分。
  • 在C语言中,可以通过FILE类型指针来描述文件流的位置,所以又称FILE类型指针为文件指针
  • 在默认情况下,文件的读取是按顺序进行的。在完成一段信息的读/写之后,文件指针移动到其后的位置上准备读取下一次读/写。
  • 在特殊情况下,需要对文件进行随机读/写,即读取当前位置的信息后,并不读取紧接其后的信息,而是根据需要读取特定位置处的信息。
  • 在C语言中,提供了专门的文件指针定位函数来实现对文件的随机读/写处理。

4.1 fseek()函数

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

形参说明如下:

  • FILE *stream:文件地址。
  • long offset:文件指针偏移量。表示移动的字节数,要求位移量是long 型数据,以保证文件长度大于64KB 时不会出错。当使用常量表示位移量时,要求加后缀“L”。
  • int whence:偏移起始位置。表示从何处开始计算位移量,规定的起始点有3种:文件首,当前位置和文件尾。这三种起始点的表示方法如表所示。计算单位为字节,负值表示反方向偏移。
  • 函数返回值:非零值表示成功,0表示失败。

#include<stdlib.h>
#include<stdio.h>
#include <conio.h> 	//getch()

#define MAX 50
int main () {
	FILE *fp;//文件指针
	int num,i,array[MAX];
	long offset; //定义位移量
	for(i=0 ;i<MAX ;i++)
		array[i] = i+10;//字符数组赋值
	if((fp=fopen ( "123.txt", "wb" ) )==NULL)//打开二进制文件
	{	
		printf ( "Error! ! \n" ) ;
		exit(1);
	}
	if (fwrite(array,sizeof(int),MAX,fp)!=MAX)//将数组中的元素写入文件中
	{	
		printf( "Error! !\n");
		exit(1);
	}
	fclose(fp);//关闭文件
	if((fp=fopen ("123.txt","rb") )==NULL)//打开二进制文件
	{	printf ("Error! !\n");
		exit(1);
	}
	while (1)
	{
		printf ( "Please input offset (input -1 to quit) :\n");
		scanf("%ld",&offset);		//输入位移量
		if(offset<0) break; 		//若输入-1或任何负数就退出循环
		if(fseek(fp, (offset*sizeof(int)), SEEK_SET)! = 0)//文件定位
		{	
			printf ("Error using fseek ().\n");
			exit (1);
		}
		fread (&num, sizeof(int),1, fp);	//从文件中读取当前位置上的数
		//输出结果
		printf ("The offset is %ld , its value is %d .\n ",offset ,num);
	}
	fclose(fp);//关闭文件
	getch ();
}

  • DOS可以管理的文件大小上限是2048 MB,即2GB。
  • 函数fseek()对文本文件和二进制文件的处理方式有所不同,对于二进制文件,可以获得准确的定位。对于文本文件要注意如下的问题,首先文件偏移量必须为0或通过ftell()函数获得的文件指针的当前位置,并且相对位置的起始点必须为SEEK_SET。
  • 另外,函数 fseek()将指针移动到文件的开始和结束位置时,产生一个文件状态标志,必须使用clearerr()函数清除文件状态标志后,才可以继续读/写此文件。

4.2rewind()函数

rewind()函数:将当前文件指针重新移动到文件的开始位置

void rewind(FILE *stream);
  • FILE*stream:文件地址。
  • 函数返回值:无

rewind()函数可以将文件指针移动到文件头,并清除状态标志。其作用相当于如下的程序:

fseek(fp,0L,SEEK_SET);
clearerr(fp);

4.3 ftell()函数

ftell()函数:用于获取文件的当前读/写位置,返回值是当前读/写位置偏离文件头部的字节数。

long ftell(FILE *fp)
ban = ftell(fp);

上述代码的功能是获取fp指定的文件的当前读/写位置,并将其值传给变量 ban。
通过使用ftell()函数可以方便地获取一个文件的长度,例如下面的代码:

ftell(fp, 0L,SEEK_END); //将当前位置移到文件的末尾
len = ftell(fp)        // 获取当前位置相对文件开头的位移,为字节数。
#include<stdlib.h>
#include<stdio.h>
#include <conio.h> 	//getch()

int main()
{
	FILE *fp;
	char buf[4], str[80];
	printf("please input : \n" );
	scanf("%s",str);//输入字符串
	if((fp=fopen("Ex1215", "wb+"))==NULL)//打开二进制文件
	{	
		printf( "Error !\n");
		exit(1);
	}
	if(fputs(str,fp)==EOF)//将字符串写入文件中
	{	
		printf ("Error!\n" ) ;
		exit(1);
	}
	rewind(fp);//将文件中的位置指针移到文件的开头
	printf("current position = %ld\n" , ftell(fp));//输出当前位置
	
	fgets(buf,4,fp);//从文件中读取3个字符
	//输出读取后文件中位置指针的位置
	printf ("After reading in %s, Current position = %ld\n", buf, ftell(fp));
	
	fgets(buf,4, fp);//再次从文件中读取3个字符
	//再次输出读取后文件中位置指针的位置
	printf ("Then, After reading in %s,Current position=%ld\n" , buf, ftell(fp));
	
	rewind (fp);//将文件中的位置指针移到文件的开头
	printf ("The position is back at %ld", ftell(fp));
	
	fclose(fp);//关闭文件
	getch();
}

 ftell()函数通常跟 fseek()一起使用,作为它的 offset那个参数的值。

5. 文件管理函数

5.1 删除文件

remove()函数实现文件的删除:

remove(fp);

5.2 重命名文件

rname()函数实现文件重新命名:需要遵循C语言文件命名规则。

rname(oldname,newname);

如果函数调用成功,则返回0,如果发生错误则返回-1。重命名处理的常见错误有如下3点。
1) 指定的“旧文件名”不存在;
2) 设置的“新文件名”已经存在;
3) 视图将文件重命名并将其移动到其他目录中。

5.3 复制文件

C标准库没有专用的文件复制函数,可以自定义:

#include <stdio.h>

int copy_file(char *oldname , char *newname)
{
	FILE *fpnew, *fpold;//定义文件指针
	int ch;
	if((fpnew= fopen (newname, "wb" ) )== NULL)//打开或建立文件
		return -1;
	if ((fpold = fopen (oldname , "rb" ) )== NULL)//打开已有文件
		return -1;
	while (1){
		ch = fgetc(fpold);	//从文件 oldname 中读取一个字符
		if ( !feof(fpold))	// 未到文件尾
			fputc(ch,fpnew);//将该字符写入data.txt文件中
		else
			break;//若newname 文件结束则退出循环
	}
	fclose (fpnew);//关闭文件
	fclose (fpold);
	return 0;
}

int main(){
	char sou[80] ,des[80];
	printf ( "Input source file : \n" ) ;
	gets(sou);//输入原文件名
	printf ( "Input destination file1 : \n");
	gets(des);//输入目标文件名
	if(copy_file(sou, des)==0)//调用copy_file函数复制文件
		puts ( "copy successful . \n" ) ;
	else
		printf ( "Error ! ! ! ");
	// 复制出第二个文件,并删除它
	printf ( "Input destination file2 : \n");
	gets(des);//输入目标文件名
	if(copy_file(sou, des)==0)//调用copy_file函数复制文件
		puts ( "copy successful . \n" ) ;
	else
		printf ( "Error ! ! ! ");

	if (remove(des)==0)//删除文件
		printf ("file %s has been deleted. \n" , des);
	else
		printf ("Remove Error! ! ! %s.\n" ,des);
	//复制出第三个文件,重命名它
	printf ( "Input destination file3 : \n");
	gets(des);//输入目标文件名
	if(copy_file(sou, des)==0)//调用copy_file函数复制文件
		puts ( "copy successful . \n" ) ;
	else
		printf ( "Error ! ! ! ");
	
	char newname [80];
	printf ("New name : " );
	gets (newname) ;//输入新文件名
	if (rename (des,newname)==0)//给文件重命名
		printf ("Renamed %s to %s.\n", des, newname);
	else
		printf ( "Error! ! %s. \n" , des);
}

也可以使用API函数CopyFile()和CopyFileEx()来实现文件复制。

CopyFile function (winbase.h) - Win32 apps | Microsoft Learnhttps://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-copyfile

6. 文件状态检测函数

在C语言中,提供了一组函数来检查文件的读写状态,可以跟踪文件的读/写状态,并检测读/写中是否出现未知的错误。

6.1 feof()函数

函数feof():检验文件指针是否到达文件末尾

#define feof(f)((f)->flags&_F_EOF)

在文件处理过程中,一般应用此函数检测文件指针是否到达文件末尾。
如果其返回值为非0,说明文件指针到达文件尾;否则返回值为0。
例如,在模拟实现MS-DOS系统中的COPY命令的程序代码中,使用feof()函数检测文件指针是否到达文件末尾。代码如下:

void main (){
    FILE *fpFrom,*fpTo;
    ......
    while ( ! feof(fpFrom))
    fputc(fgetc(fpFrom) , fpTo);
    ......
}

6.2 ferror()函数

ferror()函数:检验文件的错误状态

#define ferror(f) ((f)->flags&_F_ERR)

如果函数的返回值为非0,则说明对当前文件的操作出错,否则说明当前的文件操作正常。
ferror()函数仅反映上一次文件操作的状态,因此必须在执行一次文件操作后,执行下一文件操作前调用ferror(),才可以正确反应此次操作的错误状态。

6.3 clearerr()函数

当文件操作出错后,文件状态标志为非0,此后所有的文件操作均无效。如果希望继续对文件进行操作,必须使用clearerr()函数清除此错误标志后才可以继续操作。

void clearerr(FILE *stream) ;

如:文件指针到文件末尾时会产生文件结束标志,须执行此函数后,才可以继续对文件进行操作。因此在执行 fseek (fp,0L,SEEK_SET)和fseek (fp,0,SEEK_END)语句后,注意调用此函数。

#include <stdio.h>
void main (){
	FILE *fp;
	fp= fopen ( "123.txt","w");//以只写方式打开文件Ex1219
	//用读取只写打开的文件来产生错误
	(void) getc (fp);//从文件中读区一个字符
	if (ferror(fp) )//检测读取字符时是否有错误
	{	
		//若有错误则显示错误信息
		printf ( "Error reading from 123.txt\n");
		clearerr(fp);//清除错误标志
	}
	fclose(fp);
}

在Win32 APl中,也有一系列函数来进行文件操作:

Fileapi.h 标头 - Win32 apps | Microsoft Learn
CreateFileA 函数 (fileapi.h) - Win32 apps | Microsoft Learn

参考:

张玲玲. C语言编程新手自学手册[M].北京: 机械工业出版社,2011.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值