C语言——文件

        许多程序在实现过程中,依赖于把数据保存到变量中,而变量是通过内存单元存储数据的,数据的处理完全由程序控制。当一个程序运行完成或终止运行,所有变量的值不再保存。当输入输出数据量较大时,就会受到限制,带来不便。

        文件是解决上述问题的有效办法,它通过把数据存储在磁盘文件中,得以长久保存。当有大量数据输入时,可通过编辑工具事先建立输入数据的文件,程序运行时将不再从键盘输入,而从指定的文件上读入,从而实现数据一次输入多次使用。同样,当有大量数据输出时,可以将其输出到指定文件,不受屏幕大小限制,并且任何时候都可以查看结果文件。一个程序的运算结果还可以作为其他程序的输入,进一步加工。

         

(1)文件的概念

        文件系统功能是操作系统的重要功能和组成部分。文件是指驻留在外部介质中的一个有序数据集,可以是源文件,目标程序文件,可执行程序,也可以是待输入的原始数据,或是一组输出的结果。

        使用应用程序时,通常保存功能实现把数据从内存写入到文件,这就是所谓的存盘。打开功能实现把磁盘文件的内容读入到内存 。

(2)文本文件和二进制文件

        根据数据存储的形式,文件的数据流分为字符流和二进制流,前者称为文本文件或字符文件,后者称为二进制文件。

(3)缓冲文件系统

        缓冲文件系统的特点是:在内存开辟一个“缓冲区”,为程序中的每一个文件使用,当执行读文件的操作时,从磁盘文件将数据先读入内存“缓冲区”装满后再从内存“缓冲区”依此读入接收的变量。执行写文件的操作时,先将数据写入内存“缓冲区”,待内存“缓冲区”装满后再写入文件。由此可以看出,内存“缓冲区”的大小,影响着实际操作外存的次数内存“缓冲区”越大,则操作外存的次数就少,执行速度就快、效率高。一般来说,文件“缓冲区”的大小随机器 而定。

(4)文件结构与文件类型指针

FILE文件类型说明:

typedef struct{
	short level;            //缓冲区使用量 
	unsigned flags;         //文件状态标志 
 	char fd;                //文件描述符 
	short bsize;            //缓冲区大小 
	unsigned char *buffer;  //文件缓冲区的首地址 
	unsigned char *curp;    //指向文件缓冲区的工作指针 
	unsigned char hold;     //其他信息 
	unsigned istemp
	short token;
} FILE;

       上述定义中,文件结构本身用关键字 struct 进行定义,用typedef关键字把 struct 结构类型重新命名为 FILE 。struct 内部定义的成员包含了文件缓冲区的信息。

       自定义类型( typedef )不是用来定义一些新的数据类型,而是将C语言中的已有类型包括已定义的自定义类型重新命名,用新的名称代替已有数据类型,常用于简化对复杂数据类型定义的描述。如FILE就描述了整个 struct 的定义部分。

       自定义类型的一般形式为:

typedef <已有类型名> <新类型名>;

一般要求重新定义的类型名用大写。

       文件缓冲区是内存中用于数据存储的数据块,在文件处理过程中,程序需要访问该缓冲区实现数据的存取。因此,如何定义其中的具体数据,是文件操作类程序需要解决的首要问题。然而,文件缓冲区由系统自动分配,并不像数组那样可以通过数组名加下标来定位。为此,C语言引进FILE文件结构,其成员指针指向文件的缓冲区,通过移动指针实现对文件的操作。除此之外,在文件操作中还需用到文件的名字,状态,位置等信息

       C语言中的文件操作都是通过调用标准函数来完成的。由于结构指针的参数传递效率更高,因此C语言文件操作统一以文件指针方式实现。定义文件类型指针的格式为:

       FILE   * fp ;

其中,FILE是文件类型定义符,fp 是文件类型的指针变量。

       文件指针是特殊指针,指向的是文件类型结构,它是多项信息的综合体。每一个文件都有自己的FILE结构和文件缓冲区,FILE结构中有一个curp成员,通过 fp->curp, 可以指示文件缓冲区中数据存取的位置。

       注意:文件指针不像以前普通指针那样能进行 fp ++或 *fp 等操作, fp++将意味着指向下一个FILE结构(如果存在)。

       文件操作具有顺序性的特点,前一个数据取出后,下一次将顺序取后一个数据,fp->curp 会发生改变,但改变是隐含在文件读写操作中的,而不需要在编程时写上 fp->curp ++ ,这样类似的操作将由操作系统在文件读写时自动完成

(5)文件控制块

       已知文件缓冲区与磁盘文件之间的处理是由操作系统自动完成的,那么操作系统具体又是如何处理的呢?答案是通过操作文件控制块FCB( File Control Block)实现的。文件控制块包括文件属性,文件名,驱动器号,扩展名,文件长度以及文件记录状态等信息

       操作系统为了控制管理文件,采用文件表来管理文件,它给每一个文件顺序编号,并对应一个不同的FCB。程序要访问文件时,用一个FILE指针指向文件缓冲区,此时操作系统会把文件缓冲区与FCB相关联。因此不管有多少文件,操作系统都能有效地控制管理。如果程序想要对某个文件进行操作,只要告诉缓冲文件系统“要访问哪个文件”,操作系统会根据文件表立即在磁盘中找到该文件,并把相应的FCB编号与文件缓冲区关联。

(6)文件处理步骤

       1.定义文件指针

       2.打开文件:文件指针指向磁盘文件缓冲区

       打开文件功能用于建立系统与要操作的某个文件之间的关联,指定这个文件名并请求系统分配相应的文件缓冲区内存单元。打开文件由标准函数 fopen()实现,其一般调用形式为:

        fopen("文件名","文件打开方式");

说明:1.该函数有返回值。如果执行成功,函数将返回包含文件缓冲区等信息的FILE结构地址,赋给文件指针 fp。否则,返回一个NULL的FILE指针。        2.括号内的两个参数都是字符串。"文件名"指出要对哪个具体文件进行操作,一般要指定文件的路径,如果不写出路径,则默认与应用程序的当前路径相同。文件路径若包含绝对完整路径,则定位子目录用的斜杆‘ \ ’需要用双斜杠' \ '.

                                                                文件打开方式

文本文件(ASCII)
         使用方式 含义
" r " 打开文本文件进行只读
" w " 建立新文本文件进行只写
" a " 打开文本文件进行追加
" r+" 打卡文本文件进行读 / 写
" w+" 建立新文本文件进行读 / 写
" a+" 打开文本文件进行读 / 写 / 追加

                                                   二进制文件打开方式比文本文件多了个 ' b '

       下面两种方式都以只读的方式打开 abc.txt 文件:

 fp = fopen ( " abc.txt "," r ") ;

或 char *p = " abc.txt " ;

     fp = fopen( p ," r ");

        执行标准函数 fopen(),计算机将完成下述步骤工作 :

1. 在磁盘中找到指定文件 .

2. 在内存中分配保存一个FILE类型结构单元(16B).

3. 在内存中分配文件缓冲区单元(521B).

4. 返回FILE结构地址(回送给 fp ).

    

        文件打开的实质是把磁盘文件与文件缓冲区对应起来,这样后面的文件读写操作只需使用文件指针即可。如果 fopen()返回NULL,表明文件无法正常打开,其原因可能是文件不存在,路径不对,或者文件已经被别的程序打开,也可能是文件存储有问题。为了保证文件操作的可靠性,调用 fopen()函数时最好做一个判断,以确保文件正常打开后再进行读写

        其形式:

if (( fp = fopen (" abc.txt ", " r ")) == NULL ) {

       printf(" File open error !\n ") ;

       exit ( 0 ) ;

}

        其中 exit ( 0 ) 是系统标准函数,作用是关闭所有打开的文件,并终止程序的运行。参数 0 表示程序正常结束,非 0 参数通常表示不正常的程序结束。

        一旦文件经 fopen()正常打开,对该文件的操作方式就被确定,并且直至文件关闭都不变,即若一个文件按 r 的方式打开,则只能对该文件进行读操作,而不能进行写入操作。

        一般进行文件读写操作时,常用到如下规则:

if  读文件

        指定的文件必须存在,否则出错;

if  写文件(指定的文件可以存在或不存在)

        if  以 " w " 方式写

                 if  该文件已经存在

                     原文件将被删去然后重新建立;

                  else

                     按指定的名字新建一个文件 ;

        if  以 " a " 方式写

                   if  该文件已存在

                       写入的数据将被添加到指定文件原有数据的后面,不会删去原来的内容;

                   else

                      与 " w " 相同 ;

if  文件同时读和写

                      使用 " r++ " , " w++ " 或 " a++ " 打开文件 ;

       C语言允许同时打开多个文件,不同文件采用不同文件指针指示,但不允许同一文件在关闭前被再次打开。

       3.文件处理:文件读写操作

字符方式文件读写函数: fgetc() 和 fputc()  ;

       对于文本文件,存取的数据都是ASCII码字符文本,使用这两个函数读写文件时,逐个字符地进行文本读写

       fgetc()函数实现从 fp 所指示的磁盘文件读入一个字符到 ch

       函数调用格式:

ch = fgetc( fp );

       该函数与 getchar()函数功能类似,getchar()从键盘上读入一个字符。

       fputc()函数把一个字符 ch 写到 fp 所指示的磁盘文件上。

       函数调用格式:

fputc( ch ,fp );

       函数返回值若写文件成功为 ch ,若写文件失败则为 EOF 。

       该函数同 putchar()函数类似,putchar( ch )把ch显示在屏幕上。EOF值为-1.

       

字符串方式文件读写函数 :fgets()和 fputs()

       这两个函数以字符串的方式来对文本文件进行读写。读写文件时一次读取或写入的是字符串。

       fputs()函数用来向指定的文本文件写入一个字符串。

       函数调用格式:

fputs( s ,  fp ) ;

       其中, s 是要写入的字符串,可以是字符数组名,字符型指针变量或者字符串常量, fp 是文件指针。该函数把 s 写入文件时,字符串 s 的结束符 ' \0 ' 不写入文件。

       若函数执行成功,函数返回所写的最后一个字符 ;否则,函数返回 EOF。

       fgets()用来从文本文件中读取字符串。

       函数调用格式:

fgets( s ,n,fp ) ;

       s 与 fputs()一样,n是指定读入的字符个数, fp 是文件指针。函数被调用时,最多读取

n-1 个字符,并将读入的字符串存入指针 s 所指向内存地址开始的 n-1 个连续的内存单元中。当函数读取的字符达到指定的个数,或接收到换行符,或接收到文件结束标志 EOF 时,将在读取的字符后面自动添加一个 ' \0 ' 字符;若有换行符,则将换行符保留(换行符在 ' \0' 字符之前);若有EOF,则不保留EOF。该函数若执行成功,返回读取的字符串;如果失败,返回空指针,这时,s的内容不确定。

格式化方式文件读写函数 fscanf() 和 fprintf()

       fscanf() 用于从文件中按照给定的控制格式读取数据,而 fprintf()用于按照给定的控制格式向文件写入数据。

       函数调用格式:

fscanf( 文件指针,格式字符串,输入表) ;

fprintf( 文件指针,格式字符串,输出表) ;

       例如:

FILE *fp;
int n;
flaot x;
 fp = fopen("a.txt","r");
 fscanf(fp,"%d %f",&n,&x);
 //从文件a.txt分别读入整型数到变量n,浮点数到变量x 

        

按组进行读写
fwrite()
size_t fwrite( const void*buffer , size_t size , size_t count , FILE *fp)
// 将一个数据块写入fp指向的文件中
// buffer是指向所要输出参数的地址 
// size是每次所要写入的字节数
// count是写入次数
// fp目标文件指针
// 读写成功则返回写入字节数

 


fread()
size_t fread( const void*buffer , size_t size , size_t count , FILE *fp)
// 从fp指向的文件中读取一个数据块
// buffer是指向所要读出的地址 
// size是每次所要读出的字节数
// count是读出次数
// fp目标文件指针
// 读写成功则返回读出字节数

 


位置指针
rewind()
void rewind( FILE *fp )
// 将位置指针重新定位到文件开头
// 没有返回值

fseek()
int fseek( FILE *fp ,long offset ,int start)
// fseek()可以按照需要任意移动位置
// offset是以start参数为起始位置的偏移量
// start 取值如下:
// SEEK_SET SEEK_CUR SEEK_END 
// 相应整数值为:0,1,2
// 分别表示:文件开头位置,当前位置,文件结束位置
// 成功则返回0,否则返回非0

ftell()
long ftell( FILE *fp )
// 获取位置指针当前位置相对于文件首的偏移字节数
// 函数调用成功则返回当前文件的读写位置,否则返回-1

文件出错检测
ferror()
int ferror( FILE *fp )
// 检测对文件指针fp所指向的文件读写操作出现的错误
// 没有出错则返回0,否则返回非0
// 由于每次进行读写操作后,再调用ferror()函数都会产生一个新的值
// 因此在调用读写操作函数后要及时地调用ferror()函数对其经行检测,否则信息会丢失

clearerr()
void clearerr( FILE *fp )
// 复位错误标志,无返回值

perror()
void perror( const char *string)
// 将上一个函数发生错误的原因输出到标准设备
// 参数string所指字符串先被打印,再加上错误原因字符串

strerror()
char *strerror( int errnum)
// 将错误的数值信息转化为易于理解的字符串信息


原文链接:https://blog.csdn.net/qq_45279570/article/details/108050221

       4.关闭文件

       当文件操作完成后应关闭它,防止不正常的操作。如果把数据写入文件,首先是写到文件缓冲区,只要当写满512B,才会由系统真正写入磁盘扇区。如果写的数据不到512B,发生程序异常终止,那么这些缓冲区中的数据就会丢失。当文件操作结束时,即使未写满512B,通过文件关闭,能强制把缓冲区中的数据写入磁盘扇区,确保写文件的正常完成。

       关闭文件通过调用标准函数 fclose ()实现,其一般格式为:

fclose (文件指针) ;

       该函数将返回一个整数,若 0 表示正常关闭文件,反之异常。所以关闭文件也应使用条件判断:

       if ( fclose ( fp )) {

            printf (" Can not close the file !\n");

               exit ( 0 ) ;

       关闭文件除了强制把缓冲区的数据写入磁盘外,还将释放文件缓冲区单元和FILE结构,使文件指针与具体文件脱钩

       在编写程序时应养成文件使用结束后及时关闭文件的习惯,一则确保数据完整写入文件,二则及时释放不用的文件缓冲区单元

题目:从键盘输入一些字符,逐个把它们送到磁盘上去,直到输入一个#为止。

#include<stdio.h>
#include<stdlib.h>
int main()
{
    FILE*fp=NULL;
    char filename[25];
    char ch;
    printf("输入你要保存到的文件的名称:\n");
    gets(filename);
    if((fp=fopen(filename,"w"))==NULL)
    {
        printf("error: cannot open file!\n");
        exit(0);
    }
    printf("现在你可以输入你要保存的一些字符,以#结束:\n");
    getchar();
    while((ch=getchar())!='#'){
        fputc(ch,fp);
    }
    fclose(fp);
    system("pause");
    return 0;
}

以上实例运行输出结果为:

输入你要保存到的文件的名称:
test.txt
现在你可以输入你要保存的一些字符,以#结束:
www.runoob.com
#

    实例:    有五个学生,每个学生有3门课的成绩,从键盘输入以上数据(包括学生号,姓名,三门课成绩),计算出平均成绩,况原有的数据和计算出的平均分数存放在磁盘文件"stud"中

#include<stdio.h>
#include<stdlib.h>
typedef struct{
    int ID;
    int math;
    int English;
    int C;
    int avargrade;
    char name[20];
}Stu;
int main()
{
    FILE*fp;
    Stu stu[5];
    int i,avargrade=0;
    printf("请输入5个同学的信息:学生号,姓名,3门成绩:\n");
    for(i=0;i<5;i++)
    {
        scanf("%d %s %d %d %d",&(stu[i].ID),stu[i].name,&(stu[i].math),&(stu[i].English),&(stu[i].C));
        stu[i].avargrade=(stu[i].math+stu[i].English+stu[i].C)/3;
    }
    
    if((fp=fopen("stud","w"))==NULL)
    {
        printf("error :cannot open file!\n");
        exit(0);
    }
    for(i=0;i<5;i++)
        fprintf(fp,"%d %s %d %d %d %d\n",stu[i].ID,stu[i].name,stu[i].math,stu[i].English,
                stu[i].C,stu[i].avargrade);
    
    fclose(fp);
    // system("pause");
    return 0;
}

以上实例运行输出结果后:

请输入5个同学的信息:学生号,姓名,3门成绩:
1 a 60 70 80
2 b 60 80 90
3 c 59 39 89
4 e 56 88 98
5 d 43 88 78

打开 stud文件,内容如下

1 a 60 70 80 70
2 b 60 80 90 76
3 c 59 39 89 62
4 e 56 88 98 80
5 d 43 88 78 69

题目:有两个磁盘文件A和B,各存放一行字母,要求把这两个文件中的信息合并(按字母顺序排列),输出到一个新文件C中。

程序分析:你需要先创建 A.txt 与 B.txt。

A.txt文件内容:

123

B.txt文件内容:

456
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
    FILE*fa,*fb,*fc;
    int i,j,k;
    char str[100],str1[100];
    char tem;
    if((fa=fopen("A.txt","r"))==NULL) // A.txt 文件需要存在
    {
        printf("error: cannot open A file!\n");
        exit(0);
    }
    fgets(str,99,fa);
    fclose(fa);
    if((fb=fopen("B.txt","r"))==NULL)  // B.txt 文件需要存在
    {
        printf("error: cannot open B file!\n");
        exit(0);
    }
    fgets(str1,100,fb);
    fclose(fb);
    strcat(str,str1);
    for(i=strlen(str)-1;i>1;i--)
        for(j=0;j<i;j++)
            if(str[j]>str[j+1])
            {
                tem=str[j];
                str[j]=str[j+1];
                str[j+1]=tem;
            }
    
    if((fc=fopen("C.txt","w"))==NULL)  // 合并为 C.txt
    {
        printf("error: cannot open C file!\n");
        exit(0);
    }
    fputs(str,fc);
    fclose(fc);
    system("pause");
    return 0;
}

以上实例运行输出结果后,打开 C.txt 内容如下:

123456

题目:从键盘输入一个字符串,将小写字母全部转换成大写字母,然后输出到一个磁盘文件"test"中保存。 输入的字符串以!结束。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
 
int main()
{
    FILE*fp=NULL;
    char str[50];
    int i,len;
    printf("输入一个字符串:\n");
    gets(str);
    len=strlen(str);
    for(i=0;i<len;i++)
    {
        if(str[i]<='z'&&str[i]>='a')
            str[i]-=32;
    }
    if((fp=fopen("test","w"))==NULL)
    {
        printf("error: cannot open file!\n");
        exit(0);
    }
    fprintf(fp,"%s",str);
    fclose(fp);
    
    system("pause");
    return 0;
}

上一篇:文件习题


下一篇:2