C文件操作详解及测试

/* 经过这个测试明白:

    1:在文件的缓冲区内,标准I/O函数读取到底几个字节,文件指示器便指在这个字节的下一个字节处,只是再次调用标准I/O函数时,其会从文件指示器所指处开始读取数据,并将文件指示器指向下一个位置

    2:ftell()函数返回文件指示器当前位置距离文件开始处的字节数。理解一下:如果返回7,意思是说再当前位置前面有7个字节,那么既是文件指示器当前指在第8个字节处

    2:通过fseek()和ftell()可以自由处理文件数据的任意自己出的数据

    3:对二进制流和文本流还是不太理解

    4:在这个测试中好像我这个环境下,用二进制模式打开文本文件,其行末尾就是'\n'。(这个真不确定)

*/

/*干脆在这里整理一下这些内容:

    1:首先介绍一个常用的编程技巧:常用命令行参数来对要打开的文件名进行存储,然后通过检查命令行参数进行判定是否输入正确。然后再用fopen()函数打开文件

    2:还有一个编程技巧:在读取文件时,为了避免读取到空文件,应当使用入口检测循环(for或while),并在检测前进行获取

    2:然后提醒几个注意点:

        a:打开fopen()的文件一定要注意在不再时候时调用fclose()进行关闭,并且一定要注意检查是否关闭成功,因为磁盘已满或者移动硬盘被拔除,或者标准I/O出现错误均会导致fclose()调用失败

            fopen()为安全起见,也可以检查是否打开文件成功,如果失败,会返回NULL

        b:

    3:文件操作函数简介

        a:FILE*fopen(char*,cahr*mode):第一个参数为指定的文件名(是一个字符串),第二个参数为打开文件的模式(也是一个字符串)。如果正常打开文件然会一个FILE*,如果打开文件失败返回NULL

        b:int fclose(FILE*):参数为指定的文件。正确关闭文件返回0,失败返回EOF;

            这个函数要注意检查是否调用成功(原因写在了提醒里).

        c:  int fgetc(FILE*):从文件中获取一个字符。返回读取的字符的ASCII码值,如果读取错误,将返回EOF. 常用的getchar()函数便是用fgetc()函数通过stdin(标准输入)定义的

            int fputc(char,FILE*):向指定的文件中输出一个字符。第一个参数为带输出的字符,第二个参数为指定的文件。常用的putchar()函数便是用fputc()通过stdout(标准输出)定义的

        d:标准I/O函数有:fprintf().fscanf(),fgets(),fputs()

            这四个函数的共同特点便是第一个参数为指定的文件(FILE*).其余的东西和printf(),scanf()用法一致。

            fgets(),fputs()在第十一章中介绍过了。

            char*fgets(char*,int,FILE*):第一个参数为存储的位置,第二个参数为最大存储字符数,第三个参数为从指定的文件中获取。正确读取时返回第一个参数的指针,读取错误或者遇到文件末尾时返回NULL。这个函数会连同'\n'一起存储,建议于int fputs(char*,FILE*)搭配使用,fputs()不会自动在输出的末尾添加换行符

            int *fputs(char*,FILE*):第一个参数为待输出的字符串,第二个参数为指定的输出的文件

        e:rewind()函数:将文件指示器指向文件开始处

            void rewind(FILE*);

        f:fseek()函数:(这个函数可是很重要的,这个函数和下面的ftell()函数可以帮助你处理文件任意出的数据)

            int fseek(FILE *_File, long _Offset, int _Origin):第一个参数为指定的文件,第二个参数为偏移量(offset),指的时从起始点开始要移动的距离。第三个参数时模式,该参数确定起始点。

            要点:调用这个函数的前提是文件已经用fopen()打开

                作用:在打开的文件中,将文件指示器的位置任意移动

                该函数第二个参数必须是long类型的数值,此时考虑在数据后面加上L可以将该类型的数据转化为long类型。eg:0L:表示long类型的0.该参数的正负分别表示前移和后移,0表示保持不动。此时在考虑ftell()函数的返回值为long类型,所以是不是ftell()可以作为fseek()函数的第二个参数呢?显然是可以的呢!

                该函数第三个参数模式:在stdio.h定义了这样三个明示常量的模式:SEEK_SET:文件开始处

                                                    SEEK_CUR:当前位置(current:形容词:现时发生的,当前的)

                                                    SEEK_END:文件末尾

                             eg:fseek(fp,0L,SEEKEND);//定位在文件结尾处,或者用rewind(FILE*)也可以定位在文件末尾

                返回值为int类型,当一切正常时返回值为0,如果出现错误,返回值为-1(EOF).eg:试图移动的距离超出文件末尾

        g:ftell()函数作用:返回参数指向的文件的文件指示器当前的位置距离文件开始处的字节数

            long ftell(FILE*);告知你文件指示器此时所处的位置,如果再次调用标准I/O函数时,会从下一个位置开始获取字符。

            该ftell()函数的返回值可以作为fseek()函数的第二个参数,都是long类型的

            补充知识:文件得第一个字节到文件得开始处的距离为0.因为第一个字节就是文件的开始处。所以为0

            这里要注意一个使用这个两个函数(fseek,ftell)要注意一点,就是可以用fseek指定到文件末尾处,然后用fetll返回文件的总字节数。

            fseek(fp,0L,SEEK_END);定位到了文件末尾,此时文件指示器在文件末尾处(可能是Ctrl+Z处),并非最后一个字符处,所以想要指向最后一个字符其实还需要向前偏移一个字节。同时还需要注意第一个字节需要向前偏移ftell返回的字节数

        h:

        fseek()和ftell()函数存在一个潜在的问题,就是他们都把文件的大小限制在了long类型能表示的范围内,当处理较大的文件时就会出现问题。所以这里就涉及到了这两个函数的可移植性

        ANSI C定义了如何使用fpos_t和fgettops()函数。这里就不详细介绍了。有兴趣可自行学习(C Primer Puls第366 page)

    4:介绍标准I/O机理:(这个要注重理解)

        a:理解前,我门首先要对文件指针(FILE*)进行了解

            文件指针是一个指向FILE的指针,FILE是定义在stdio.h中的派生类型。

            文件指针并不指向实际的文件,它指向的是一个包含文件信息的数据对象(数据对象是一个结构)。

            该数据对象包含:操作文件的I/O函数所用的缓冲区的信息。

                因为在标准库中的I/O函数都使用缓冲区,所以他们不仅要知道缓冲区的位置(记住,后面要用于理解机理),还要知道缓冲区被填充的程度(记住,后面也要用于理解机理)以及操作那个文件(记住,同上)

                标准I/O函数根据这些信息,在必要的时候决定再次填充或清空缓冲区

        b:在理解一个东西:

            缓冲区:缓冲区是一块有大小的存储区域,其大小因实现而异,一般是512字节或他的倍数。随着发展,缓冲区的大小也越来越大

        c:标准I/O机理:

            (1):通常,使用标准I/O的第一步是:调用fopen()函数打开文件    

                    理解:fopen()不仅会打开一个文件,还会创建一个缓冲区(读写模式打开回创建两个缓冲区),以及一个包含文件和缓冲区数据的结构,然后fopen()函数回返回指向该结构的指针(文件指针:是一个结构)

                        这个结构通常包含一个指定流中当前位置的文件指示器。还包含错误和文件结尾的指示器,一个指向缓冲区开始的处的指针,一个文件标识符和一个计数(统计实际拷贝进缓冲区的字节数)

                            包含的这些信息都是有用的,用于帮助I/O函数获取文件内容

                    补充一种说法:将该结构指针赋值给指针变量fp————称打开了一个流

                            以文本模式打开文件:打开了一个文本流;  以二进制模式打开文件:打开了一个二进制流。

(主要考虑输入)(2):通常,第二步是:调用一个定义在stdio.h中的输入函数。

                    理解:a:一旦调用了这些函数,文件中缓冲大小的数据块就被拷贝给缓冲区(前面的填充缓冲区)

                            最初调用函数的时候,除了填充缓冲区,还会设置fp所指向的结构中的值,尤其是要设置流中当前位置(通常当前位置从字节0开始)(这便是初始化数据结构)和拷贝进缓冲区的字节数

                         b:调用标准I/O函数后,将会初始化结构(文件指针所指向的结构)和填充缓冲区。(记住前面所里缓冲区是有大小限制的)

                            输入函数随后从缓冲区读取数据。他在读取数据时,文件指示器被设置为指向刚读取字符的下一个字符

                            (由于stdio.h系列的所有输入函数都使用相同的缓冲区,所以调用任何一个函数都会将从上一次函数停止调用的位置开始获取字符)

                            eg: 在缓冲区中有:0123456789    然后上一次调用I/O函数获取了字符4,那么文件指示器的位置将指在5的位置,然后在调用I/O函数,将获取文件指示器所指的位置的字符5,然后文件指示器再往后指一个位置,此时指向6,若此是调用ftell函数,将返回6L,因为字符6前面有012345这6个字符,实际文件指示器指再第7个字节的位置上

                        c:当输入函数发现已读完缓冲区中的所有字符时,会请求把下一个缓冲区大小的数据块冲文件拷贝至该缓冲区中。

                            以这种方式,输入函数可读取文件的所有内容,直到文件末尾。函数咋i读取缓冲区中的最后一个字符后,把结尾指示器设置(FILE*指向的结构中包含该数据,用于帮助函数检测是否读取到了文件末尾)为真。于是,下一次被调用的输入函数返回EOF

                            (前面再解释文件指针FILE*的时候,说标准I/O函数根据这些信息,在必要的时候决定再次填充或清空缓冲区。现在应该就能理解了,这些信息就是文件指针所指向的结构中的信息,决定再次填充还是清空缓冲区也能理解了,因为缓冲区有大小限制,文件可能无法一次全部拷贝进缓冲区,所以允许再次填充,一次只能拷贝缓冲区大下数据块。所以需要信息决定时再次填充还是清空)

                        d:输出函数以类似的方式把数据写入缓冲区。当缓冲区被填满时,数据将被拷贝到文件中。

*/                          

#include

#include

int main(int argc,char** argv)

{

    

    char ch;

    //char name[40];

    FILE*fp_1;//定义一个文件指针(该文件指针为一个结构)

    if(argc<2){//检查命令行参数

        fprintf(stderr,"Usaeg: %s filename\n",argv[0]);//这里简单介绍一下fprintf()函数,该函数第一个参数为文件指针,后面其他的参数与printf一样,用法也一样,只是fprintf函数第一个参数(文件指针)用于指定待处理的文件

        exit(EXIT_FAILURE);//程序异常中止 

    }

    //fgets(argv[1],39,stdin);

    //fputs(argv[1],stdout);

    fp_1=fopen(argv[1],"rb");//以二进制只读模式打开该文件,目的是为了检测我这个系统,这个环境下的文本文件的行末尾和文件结尾处是如歌表示的,因为以二进制模式打开文本文件,c程序不会对其进行映射  //  打开给文件的同时还会创建一个缓冲区,并建立一个结构体,然后返回指向这个结构体的指针(文件指针)

    if(fp_1==NULL){

        printf("the file open failed\n");

    }

    while((ch=fgetc(fp_1))!=EOF){//从文件中获取一个字符,检测是否到达文件末尾,如果到达文件末尾,那么将会退出获取字符的循环

        fputc(ch,stdout);//直接输出到屏幕

    }//这里我读取到了文件末尾处,ch此时为EOF

    if((fputc(ch,stdout))==EOF)//尝试输出EOF

    fprintf(stdout,"未成功输出EOF\n");

    //这里文件指示器的位置在EOF下一个字符的位置,很可能时控制符

    

    if(fgetc(fp_1)!='\0')//因为在MS_DOS的文件结尾符可能更在Ctrl+Z后面,并且在后面可能用空字符填充文件,使其成为256字节的倍数

    fprintf(stdout,"文件结尾后面的字符不是空字符\n");

    

    rewind(fp_1);//是文件中的文件指示器回到文件开头

    

        long cur;//ftell()返回值类型为long类型

        char ch_3;

        char ch_2;

        cur=ftell(fp_1);

        printf("now 文件指示器指在第 %ld 个字节处\n",cur);

        for(int i=0;i<10;i++){

            fscanf(fp_1,"%c",&ch_2);//获取十个字符,然后使文件指示器的位置指向在第十一个字符的位置

            fprintf(stdout,"%c\t",ch_2);

            cur=ftell(fp_1);

        printf("now 文件指示器指在第 %ld 个字节处\n",cur);

        }

        fscanf(fp_1,"%c",&ch_3);//获取第十一个字符并输出s

        fprintf(stdout,"%c\n",ch_3);

        cur=ftell(fp_1);

        printf("now 文件指示器指在第 %ld 个字节处\n",cur);

        fscanf(fp_1,"%c",&ch_3);//获取第十二个字符并输出s

        fprintf(stdout,"%c\n",ch_3);

        

        fseek(fp_1,-cur,SEEK_CUR);//将文件指示器指会文件开始处

        ch_2=fgetc(fp_1);//再次获取第一个字节的数据,然后输出,看看是不是指到了文件开始处

        fputc(ch_2,stdout);//输出第一个字节

        printf("\n");

       

    printf("结束\n");

    fclose(fp_1);//非常要注意,打开的文件一定要在不再使用后进行关闭,并且要注意检查是否关闭成功。因为磁盘已满或者移动硬盘被拔除,或者I/O出错都会导致fclose()调用失败

    getchar();

    getchar();

    return 0;

}

你可能感兴趣的:(c语言)