c语言中的文件 *** 作

c语言中的文件 *** 作,第1张

目录

引入:


一、文件的打开和关闭

 1.文件名

 2.文件指针

3.文件的打开和关闭


二、对文件如何 *** 作和流

1.对文件 *** 作相关功能: 

2.流


三、文件 *** 作函数(顺序读写)

 1."w"输出流:

2."r" 输入流

3."wb"与"rb"二进制输出输入

4.sscanf与sprintf函数


四、fseek相关函数介绍(随机读写) 

1.fseek函数 

2.ftell

3.rewind


五、文件缓冲区 


引入:

磁盘上的文件都是文件。


对于日常生活中,我们在使用电子设备的时候,用到某个软件或者应用的时候,总是发现他们会在本地占用空间,即文件,是不是打开软件后,先前保留的数据都不见了呢?不是的,这些数据是被存放在文件里面的。


之前我们制作的动态储存版的通讯录(一个小项目,数据只能储存到内存中),程序是不是只要一关闭,保存的数据下次打开就消失了。


这是因为没有将其数据保存在文件中,而是内存中,结束程序内存的空间也自然被释放,从而无法保留。


那么如果想要改进这个通讯录自然要加上文件 *** 作的函数让其数据保留在我们的硬盘上:

而在程序设计中:文件一般包括两种,一种就是我们写程序所得到的文件(程序文件,比如:.cc语言源文件 .obj(windows环境下)目标文件,.exe可执行程序)第二种就是通过程序来 *** 作输入输出的文件(数据文件:从某个文件通过程序写入或者拿取数据的文件)。


所以,接下来我们一起来了解文件 *** 作的相关函数的知识吧!


一、文件的打开和关闭

 1.文件名

 首先了解一下文件名:

文件名 = 文件路径 + 文件名主干 + 文件后缀。


在Windows下要注意的是:

(1)文件名最长可以使用255个字符。


(2)可以使用扩展名,扩展名用来表示文件类型,也可以使用多间隔符的扩展名。


如win.ini.txt是一个合法的文件名,但其文件类型由最后一个扩展名决定。


(3)文件名中允许使用空格,但不允许使用下列字符(英文输入法状态):< > / \ | : " * ?

(4)windows系统对文件名中字母的大小写在显示时有不同,但在使用时不区分大小写。


 (上面参考百度百科)

那么,当文件被创建或者使用时,电脑又是如何感知到它的存在的呢?

 2.文件指针

那么这里就要涉及到指针相关的知识了。


要 *** 作一个文件,被使用的文件(无论读或者写)就会创建一个文件信息区(结构体对象,FILE类型,在内存里面创建,强关联,用来描述文件基本信息的

文件基本信息可以参考一下截图:

 就是类型,位置,大小等等信息了。


所以每次 *** 作文件的时候,内存内总会出现文件信息区与之关联,好让我们 *** 作。


通常,在c中,将文件指针变量如此定义:

FILE* pr //文件指针变量,通过该指针便就可以 *** 作相关文件。


3.文件的打开和关闭

现在重头戏来啦:我们已经了解了文件指针,那么要如何在程序中通过该指针对文件进行打开和关闭 *** 作呢?

(1).打开(open) :

FILE* pr = fopen("C:\Users\HP\Desktop HELLO WORLD", "r");

上面的 *** 作就是对我们之前创建的文件进行打开,并且*只读 *** 作。


 *部分不懂先不急,后续会补充。


该函数fopen即为文件打开函数:FILE* fopen(const char* name, const char* mod);返回文件信息区的地址,第一个是文件名,第二个是打开 *** 作,如果打开失败,返回NULL。


(2).关闭(close

fclose(pr);

上面的 *** 作就是对刚刚打开的文件信息区进行关闭,即关闭文件函数。


fclose即为文件关闭函数,int fclose( FILE *stream );文件关闭。


如果*流被成功关闭,fclose返回0。


函数返回EOF表示错误。


一般关闭文件后,需要给文件指针给NULL,防止成为野指针。


看下面的打开与关闭文件代码:

    FILE* or = fopen("test.txt", "r");//只是文件名加扩展名的话只在源文件目录下进行寻找,如果打开失败,那么就返回NULL
    if (or == NULL) {
        perror("fopen");
        return 1;
    }
    fclose(or);//关闭此文件。


关闭成功返回0,否则返回EOF即发生错误。



    or = NULL;
    return 0;

在了解了文件的打开与关闭后,我们就来了解一下对文件进行怎样 *** 作的相关功能和流的介绍:


二、对文件如何 *** 作和流

1.对文件 *** 作相关功能: 

fopen第二个参数 (使用方式)                             含义                       

 "r"                  打开,为了只读 。


如果打开文件不存在,报错。


 
"w"                  为了只写 如果文件存在,会将原来内容销毁,然后在写,没有就会在文件名下开辟
"a"                  为了追加,不会将内容摧毁的条件下写。



"rb"                 二进制只读,如果打开文件不存在,报错。


 
"wb"                二进制只写,文件存在,会将原来内容销毁,然后在写,没有就会在文件名下开辟
"wa"                二进制追加,不会将内容摧毁的条件下写。


                                                                

目前只对上述 *** 作进行介绍,并且在介绍其功能时,也一边将文件 *** 作函数带入:

针对r和w的含义,特别的:

    //在w只写的条件下,对文件输出字符。


如果找不到该文件,则会创建一个,如果找到了,则会销毁里面的内容。



    FILE* pr = fopen("test.txt", "w");
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    int a = fputc('x', pr);
    fclose(pr);
    pr = NULL;
    printf("%c\n", a);
    return 0; 

请多加留意一下文件里的内容哦! 

2.流

流可以形容成:水流
外部设备封装个流
电脑上有各种的外部设备(键盘、屏幕、u盘、硬盘、网卡】.......)
把数据写入流,读取数据从流里面读。


  


流就有:文件流(文件指针)标准输入(stdin键盘)、输出(stdout屏幕)、错误流(stderr屏幕)(只要c程序运行起来,这三个流默认打开,类型也是文件指针 FILE*))


三、文件 *** 作函数(顺序读写)  1."w"输出流:

前提,"w"表明在打开文件的时候,是用的 FILE* pr = fopen("test.txt", "w");方式打开,以只写的方式,即输出,向流输出,即输出流。


 

 (1).fputc 字符 *** 作  

函数原型:int fputc(int c, FILE* ar); c为要写入的字符变量,存入文件之中。


返回的是写入的字符的ASCII码值,出现错误就返回EOF

    //使用字符存入利用循环对文件进行存入字符串
    FILE* pr = fopen("test.txt", "w");
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    int a = 'A';
    for (int i = 0; i < 26; i++)
    {
        fputc(a++, pr); //往文件里面存入ABCDEFG........
    }
    fclose(pr);
    pr = NULL; 

(2).fputs 文本行(字符串) *** 作 

函数原型:int fputs(const char *string, FILE* ar);将字符串(传入的是该字符串或者文本首元素地址),写入文件。


如果成功,每个函数都会返回一个非负值。


当发生错误时,fputs返回EOF 

    //直接用字符串存入函数即可;
    FILE* pr = fopen("test.txt", "w");
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    fputs("ABCDEF\n", pr);//改完后文件里面存ABCDEF
    fclose(pr);
    pr = NULL; 

(3).fprintf 针对于所有输出流的格式化输出函数

函数原型:int fprintf( FILE *stream, const char *format [, argument ]...);格式化输出,所有输出流,后面和printf一致,针对于w只写

因为具有格式化进行输出了,那么就要有对于的格式化 *** 作符,下面例子使用结构体方便举例:

    struct student
    {
        char name[20];
        char xingbie;
        int age;
    };
    struct student c = { "小刘", 'L', 13 };
    FILE* arr = fopen("test3.txt", "w");
    if (arr == NULL)
    {
        perror("fopen");
        return 1;
    }
    fprintf(arr, "%s %c %d", c.name, c.xingbie, c.age);//上述代码会在源代码根目下创建一个新的test3文件,并且写入结构体内存入的数据。



    fclose(arr);
    arr = NULL;

其实就是和原来的只针对标准流输出的printf差了第一个文件指针而已,可以对比记忆哦! 

2."r" 输入流

前提,"r"表明在打开文件的时候,是用的 FILE* pr = fopen("test.txt", "r");方式打开,以只读的方式,即输入,从流输入程序,即输入流。


(1)fgetc 字符输入

 函数原型:int fgetc(FILE* ar);将文件中的字符输入程序中,返回的是读到的字符的ASCII码值 ,遇到错误或者结束就返回EOF。


    FILE* pr = fopen("E:\\学习资料\\C\\xuexi\\text_2022\\wenjiantest_4_6\\test2.txt", "r");
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    //利用字符 *** 作循环输入
    //printf("%c\n", fgetc(pr));
    //printf("%c\n", fgetc(pr));
    //printf("%c\n", fgetc(pr));
    int a = 0;
    while ((a = fgetc(pr)) != EOF)
    {
        printf("%c", a);
    }

     fclose(pr);
    arr = NULL;

上述是两种输入方法;另外:(fgetc(stdin)键盘里读)也是读取键盘里的字符。


(2).fgets 文本行输入

函数原型:char *fgets( char *string, int n, FILE *stream );三个参数分别是读文件所放位置,最大的个数(读多少个字符,最多读255个字符)读的时候实际上读n - 1个。


最后一个要给\0  函数返回字符串,如果读完或者没有,返回NULL 

    char arr[255] = { 0 };
    fgets(arr, 255, pr);
    printf("%s\n", arr);

自己对比记忆也可哦!

(3).fscanf 针对于所有输入流的格式化输入函数

函数原型:int fscanf( FILE *stream, const char *format [, argument ]... );格式化输入,所有输入流,后面和scanf一致。


针对于r只读 。


同理,也是用结构体来举例:

    struct student
    {
        char name[20];
        char xingbie;
        int age;
    };
    struct student c = { 0 };
    FILE* pr = fopen("test3.txt", "r");
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    fscanf(pr, "%s %c %d", c.name, &(c.xingbie), &(c.age));
    printf("%s %c %d", c.name, c.xingbie, c.age);
    fclose(pr);
    pr = NULL;

记忆与printf相似,多了一个文件指针。


3."wb"与"rb"二进制输出输入

上面的wb和rb只与w和r差别一个b,表现了使用二进制交换的,和之前的文本不同:

FILE* pr = fopen("test.txt", "wb");//FILE* pr =  fopen("test.txt", "rb");

与上面文件打开方式对应的文件 *** 作函数是:fwrite和fread

(1)fwrite (在"wb"条件下) 输出 *** 作

函数原型size_t fwrite( const void *buffer, size_t size, size_t count, FILE *stream );
                                         指针指向要写的数据 写多大 最多写几个数据   写入哪个流(单位字节)fwrite返回实际写入的完整项数,如果发生错误,这个数可能小于count。


此外,如果发生错误,则无法确定文件位置指示器。


    struct student
    {
        char name[20];
        char xingbie;
        int age;
    };
    struct student c = { "张三", 'L', 19 };
    FILE* pr = fopen("test4.txt", "wb");
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    fwrite(&c, sizeof(struct student), 1, pr);

打开文件后,会发现有乱码,这是因为是用二进制写入的。


(2). fread(在"rb"条件下) 输入 *** 作

函数原型:   size_t fread( void *buffer, size_t size, size_t count, FILE *stream );
                                   指针指向要存的数据地址  读多大  最多读几个数据   取出哪个流(单位字节) 返回实际读到的元素 

    struct student
    {
        char name[20];
        char xingbie;
        int age;
    };                                                                                                                                                struct student d = { 0 };
    FILE* pr = fopen("test4.txt", "rb");
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    fread(&d, sizeof(struct student), 2, pr);
    printf("%s %c %d", d.name, d.xingbie, d.age);
    fclose(pr);
    pr = NULL;

4.sscanf与sprintf函数

下面是两个函数的原型和解析:

                    sscanf - int sscanf( const char *buffer, const char *format [, argument ] ... );从一个字符串里面读取一个格式化的数据。


后面和scanf一致,第一个要提取字符串的地址
                    sprintf - int sprintf( char *buffer, const char *format [, argument] ... );把格式化的数据转化为字符串,后面和printf一致,前面是储存字符串的地址。


 

    struct student
    {
        char name[20];
        char xingbie;
        int age;
    };
    struct student c = { 0 };
    sscanf("小明 N 14", "%s %c %d", c.name, &(c.xingbie), &(c.age));
    printf("%s %c %d", c.name, c.xingbie, c.age);
    struct student
    {
        char name[20];
        char xingbie;
        int age;
    };
    struct student c = { "张三", 'L', 19 };
    char arr[256] = { 0 };
    sprintf(arr, "%s %c %d", c.name, c.xingbie, c.age);
    printf("%s\n", arr); 


四、fseek相关函数介绍(随机读写)  1.fseek函数 

上面介绍的全是关于文件里面顺序读写的。


那么,我们能否自由指定位置呢?当然是可以实现的。


在上面代码实现fgetc函数的时候,你会发现读取一个然后就会读取下一个,不会重复读取的存在,而写自然也是一样,这说明了文件指针也是会随着读写发生变化的,证明了我们是能够 *** 控这个位置的,下面我们首先向文件导入一串字符串:

int main()
{
    //文件的随机读写,我要往哪里去读写 文件里面一开始有记录读取文件的位置的,默认是起始位置。



    FILE* pr = fopen("test.txt", "w");//以只写的方式打开
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    fputs("nihaoshijie", pr);//先写入进去;
    fclose(pr);
    pr = NULL;
    return 0;
}
 

存好之后,我们会发现文本文件里面存在

 

下面介绍一下fseek函数的使用:

函数原型:int fseek( FILE *stream, long offset, int origin );三个参数:origin:SEEK_CUR -文件当前 SEEK_END 末尾 SEEK_SET 开始  offset:偏移量 表现了通过起始坐标的不同,我们便就可以 *** 作目前读取或者写入的位置了,下面以读取为例子:

int main()
{
    //文件的随机读写,我要往哪里去读写 文件里面一开始有记录读取文件的位置的,默认是起始位置。



    FILE* pr = fopen("test.txt", "r");//以只读的方式打开
    if (pr == NULL)
    {
        perror("fopen");
        return 1;
    }
    printf("%c", fgetc(pr));//正常按顺序读
    printf("%c", fgetc(pr));//每次读取你会发现不会读取重复的,即文件指针也随着读取(或者写入)在变化
    //此时使用文件的随机读,所谓随机,即我们自己去控制
    //使用函数int fseek( FILE *stream, long offset, int origin );
    //origin:SEEK_CUR -文件当前 SEEK_END 末尾 SEEK_SET 开始  offset:偏移量
    //此时文件指针应该指向的是    h的位置,我们让它指向o
    fseek(pr, 2, SEEK_CUR);//为了让其指向o,自然从文件指针当前指向位置向后偏移两个单位即可
    printf("%c", fgetc(pr));//此时读取的就是o了,字符串和写入也是如此
    fclose(pr);
    pr = NULL;
    return 0;

下面还有几个方便使用的函数:

2.ftell

long ftell( FILE *stream );
返回当前位置与起始位置的偏移量

3.rewind

让文件回归到起始位置 void rewind(FILE *stream) 文件指针的位置回到起始位置。


 

以上就是随机读写的全部知识啦,自己写的不是很好,嘿嘿,谢谢指正说明啦~


五、文件缓冲区 

c语言中会自动为程序中使用的文件开辟缓冲区 是在内存里面
而读取的时候 先将硬盘中数据放入输入缓冲区,放满,在放入程序里面,反之同理。


一下代码只是提供测试,在刷新缓存区,或者关闭文件 *** 作都可以是直接将缓存区的数据给文件或者程序的。


 

#include
#include
//VS2013 WIN10环境测试

int main()
{
    FILE* pf = fopen("test.txt", "w");
    fputs("abcdef", pf);//先将代码放在输出缓冲区

    printf("睡眠20秒-已经写数据了,打开test.txt文件,发现文件没有内容\n");
    Sleep(20000);//睡眠10秒

    printf("刷新缓冲区\n");
    fflush(pf);//刷新缓冲区时,才将输出缓冲区的数据写到文件(磁盘)
    //注:fflush 在高版本的VS上不能使用了
    printf("再睡眠20秒-此时,再次打开test.txt文件,文件有内容了\n");
    Sleep(20000);

    fclose(pf);
    //注:fclose在关闭文件的时候,也会刷新缓冲区
    pf = NULL;
    return 0;
}


所以得到结论:想让数据瞬间到文件里面去,刷新数据或者关闭文件即可,如果不关闭,可能造成数据丢失。


 

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

原文地址: http://outofmemory.cn/langs/568385.html

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

发表评论

登录后才能评论

评论列表(0条)

保存