• 12_文件操作


    12.1 文件类型指针(FILE类型指针)

    在C语言中用一个指针变量指向一个文件,这个指针称为文件指针。 

    typedef struct

    {

    short           level; //缓冲区"满"或者"空"的程度

    unsigned        flags; //文件状态标志

    char            fd; //文件描述符

    unsigned char   hold; //如无缓冲区不读取字符

    short           bsize; //缓冲区的大小

    unsigned char   *buffer;//数据缓冲区的位置

    unsigned        ar;  //指针,当前的指向

    unsigned        istemp; //临时文件,指示器

    short           token; //用于有效性的检查

    }FILE;

    FILE是系统使用typedef定义出来的有关文件信息的一种结构体类型结构中含有文件名、文件状态和文件当前位置等信息

    声明FILE结构体类型的信息包含在头文件“stdio.h”中,一般设置一个指向FILE类型变量的指针变量,然后通过它来引用这些FILE类型变量。通过文件指针就可对它所指的文件进行各种操作。

    C语言中有三个特殊的文件指针由系统默认打开,用户无需定义即可直接使用:

    1. stdin: 标准输入,默认为当前终端(键盘),我们使用的scanf、getchar函数默认从此终端获得数据。
    2. stdout:标准输出,默认为当前终端(屏幕),我们使用的printf、puts函数默认输出信息到此终端。
    3. stderr:标准出错,默认为当前终端(屏幕),我们使用的perror函数默认输出信息到此终端。

    12.2 文件的打开与关闭(fopen, fclose)

    12.2.1 文件的打开

    任何文件使用之前必须打开:

    #include 

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

    功能:打开文件

    参数:

    filename:需要打开的文件名,根据需要加上路径

    mode:打开文件的模式设置

    返回值:

    成功:文件指针

    失败:NULL

    第一个参数的几种形式:

    FILE *fp_passwd = NULL;

    //相对路径:

    //打开当前目录passdw文件:源文件(源程序)所在目录

    FILE *fp_passwd = fopen("passwd.txt", "r");

    //打开当前目录(test)下passwd.txt文件

    fp_passwd = fopen(". / test / passwd.txt", "r");

    //打开当前目录上一级目录(相对当前目录)passwd.txt文件

    fp_passwd = fopen(".. / passwd.txt", "r");

    //绝对路径:

    //打开C盘test目录下一个叫passwd.txt文件

    fp_passwd = fopen("c:/test/passwd.txt","r");

    第二个参数的几种形式(打开文件的方式):

    打开模式

    含义

    r或rb

    以只读方式打开一个文本文件(不创建文件,若文件不存在则报错)

    w或wb

    以写方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件)

    a或ab

    以追加方式打开文件,在末尾添加内容,若文件不存在则创建文件

    r+或rb+

    以可读、可写的方式打开文件(不创建新文件)

    w+或wb+

    以可读、可写的方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件)

    a+或ab+

    以添加方式打开文件,打开文件并在末尾更改文件,若文件不存在则创建文件

    注意:

    1. b是二进制模式的意思,b只是在Windows有效,在Linux用r和rb的结果是一样的
    2. Unix和Linux下所有的文本文件行都是\n结尾,而Windows所有的文本文件行都是\r\n结尾
    3. 在Windows平台下,以“文本”方式打开文件,不加b:
      1. 当读取文件的时候,系统会将所有的 "\r\n" 转换成 "\n"
      2. 当写入文件的时候,系统会将 "\n" 转换成 "\r\n" 写入
      3. 以"二进制"方式打开文件,则读\n写都不会进行这样的转换
    1. 在Unix/Linux平台下,“文本”与“二进制”模式没有区别,"\r\n" 作为两个字符原样输入输出

    int main(void)

    {

    FILE *fp = NULL;

    // "\\"这样的路径形式,只能在windows使用

    // "/"这样的路径形式,windows和linux平台下都可用,建议使用这种

    // 路径可以是相对路径,也可是绝对路径

    fp = fopen("../test", "w");

    //fp = fopen("..\\test", "w");

    if (fp == NULL) //返回空,说明打开失败

    {

    //perror()是标准出错打印函数,能打印调用库函数出错原因

    perror("open");

    return -1;

    }

    return 0;

    }

    12.2.2 文件的关闭

    任何文件在使用后应该关闭:

    1. 打开的文件会占用内存资源,如果总是打开不关闭,会消耗很多内存
    2. 一个进程同时打开的文件数是有限制的,超过最大同时打开文件数,再次调用fopen打开文件会失败
    3. 如果没有明确的调用fclose关闭打开的文件,那么程序在退出的时候,操作系统会统一关闭。

    #include 

    int fclose(FILE * stream);

    功能:关闭先前fopen()打开的文件。此动作让缓冲区的数据写入文件中,并释放系统所提供的文件资源。

    参数:

    stream:文件指针

    返回值:

    成功:0

    失败:-1

    FILE * fp = NULL;

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

    fclose(fp);

    12.3 文件的读写

    12.3.1 按照字符读写文件fgetc、fputc

    1)写文件

    #include 

    int fputc(int ch, FILE * stream);

    功能:将ch转换为unsigned char后写入stream指定的文件中

    参数:

    ch:需要写入文件的字符

    stream:文件指针

    返回值:

    成功:成功写入文件的字符

    失败:返回-1

    char buf[] = "this is a test for fputc";

    int i = 0;

    int n = strlen(buf);

    for (i = 0; i < n; i++)

    {

    //往文件fp写入字符buf[i]

    int ch = fputc(buf[i], fp);

    printf("ch = %c\n", ch);

    }

    2)文件结尾

    在C语言中,EOF表示文件结束符(end of file)。在while循环中以EOF作为文件结束标志,这种以EOF作为文件结束标志的文件,必须是文本文件。在文本文件中,数据都是以字符的ASCII代码值的形式存放。我们知道,ASCII代码值的范围是0~127,不可能出现-1,因此可以用EOF作为文件结束标志。

    #define EOF     (-1)

    当把数据以二进制形式存放到文件中时,就会有-1值的出现,因此不能采用EOF作为二进制文件的结束标志。为解决这一个问题,ANSI C提供一个feof函数,用来判断文件是否结束。feof函数既可用以判断二进制文件又可用以判断文本文件。

    #include 

    int feof(FILE * stream);

    功能:检测是否读取到了文件结尾。判断的是最后一次“读操作的内容”,不是当前位置内容(上一个内容)

    参数:

    stream:文件指针

    返回值:

    非0值:已经到文件结尾

    0:没有到文件结尾

    3)读文件

    #include 

    int fgetc(FILE * stream);

    功能:从stream指定的文件中读取一个字符

    参数:

    stream:文件指针

    返回值:

    成功:返回读取到的字符

    失败:-1

    char ch;

    #if 0

    while ((ch = fgetc(fp)) != EOF)

    {

    printf("%c", ch);

    }

    printf("\n");

    #endif

    while (!feof(fp)) //文件没有结束,则执行循环

    {

    ch = fgetc(fp);

    printf("%c", ch);

    }

    printf("\n");

    12.3.2 按照行读写文件fgets、fputs

    1)写文件

    #include 

    int fputs(const char * str, FILE * stream);

    功能:将str所指定的字符串写入到stream指定的文件中,字符串结束符 '\0'  不写入文件。

    参数:

    str:字符串

    stream:文件指针

    返回值:6

    成功:0

    失败:-1

    char *buf[] = { "123456\n", "bbbbbbbbbb\n", "ccccccccccc\n" };

    int i = 0;

    int n = 3;

    for (i = 0; i < n; i++)

    {

    int len = fputs(buf[i], fp);

    printf("len = %d\n", len);

    }

    2)读文件

    #include 

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

    功能:从stream指定的文件内读入字符,保存到str所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1个字符为止,最后会自动加上字符 '\0' 作为字符串结束。

    参数:

    str:字符串

    size:指定最大读取字符串的长度(size - 1)

    stream:文件指针

    返回值:

    成功:成功读取的字符串

    读到文件尾或出错: NULL

    char buf[100] = 0;

    while (!feof(fp)) //文件没有结束

    {

    memset(buf, 0, sizeof(buf));

    char *p = fgets(buf, sizeof(buf), fp);

    if (p != NULL)

    {

    printf("buf = %s", buf);

    }

    }

    12.3.3 按照格式化文件fprintf、fscanf

    1)写文件

    #include 

    int fprintf(FILE * stream, const char * format, ...);

    功能:根据参数format字符串来转换并格式化数据,然后将结果输出到stream指定的文件中,指定出现字符串结束符 '\0'  为止。

    参数:

    stream:已经打开的文件

    format:字符串格式,用法和printf()一样

    返回值:

    成功:实际写入文件的字符个数

    失败:-1

    fprintf(fp, "%d %d %d\n", 1, 2, 3);

    2)读文件

    #include 

    int fscanf(FILE * stream, const char * format, ...);

    功能:从stream指定的文件读取字符串,并根据参数format字符串来转换并格式化数据。

    参数:

    stream:已经打开的文件

    format:字符串格式,用法和scanf()一样

    返回值:

    成功:参数数目,成功转换的值的个数

    失败: - 1

    int a = 0;

    int b = 0;

    int c = 0;

    fscanf(fp, "%d %d %d\n", &a, &b, &c);

    printf("a = %d, b = %d, c = %d\n", a, b, c);

    12.3.4 按照块读写文件fread、fwrite

    1)写文件

    #include 

    size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

    功能:以数据块的方式给文件写入内容

    参数:

    ptr:准备写入文件数据的地址

    size: size_t 为 unsigned int类型,此参数指定写入文件内容的块数据大小

    nmemb:写入文件的块数,写入文件数据总大小为:size * nmemb

    stream:已经打开的文件指针

    返回值:

    成功:实际成功写入文件数据的块数目,此值和 nmemb 相等

    失败:0

    typedef struct Stu

    {

    char name[50];

    int id;

    }Stu;

    Stu s[3];

    int i = 0;

    for (i = 0; i < 3; i++)

    {

    sprintf(s[i].name, "stu%d%d%d", i, i, i);

    s[i].id = i + 1;

    }

    int ret = fwrite(s, sizeof(Stu), 3, fp);

    printf("ret = %d\n", ret);

    2)读文件

    #include 

    size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

    功能:以数据块的方式从文件中读取内容

    参数:

    ptr:存放读取出来数据的内存空间

    size: size_t 为 unsigned int类型,此参数指定读取文件内容的块数据大小

    nmemb:读取文件的块数,读取文件数据总大小为:size * nmemb

    stream:已经打开的文件指针

    返回值:

    成功:实际成功读取到内容的块数,如果此值比nmemb小,但大于0,说明读到文件的结尾。

    失败:0

    typedef struct Stu

    {

    char name[50];

    int id;

    }Stu;

    Stu s[3];

    int ret = fread(s, sizeof(Stu), 3, fp);

    printf("ret = %d\n", ret);

    int i = 0;

    for (i = 0; i < 3; i++)

    {

    printf("s = %s, %d\n", s[i].name, s[i].id);

    }

    12.4文件的定位

    #include 

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

    功能:移动文件流(文件光标)的读写位置。

    参数:

    stream:已经打开的文件指针

    offset:根据whence来移动的位移数(偏移量),可以是正数,也可以负数,如果正数,则相对于whence往右移动,如果是负数,则相对于whence往左移动。如果向前移动的字节数超过了文件开头则出错返回,如果向后移动的字节数超过了文件末尾,再次写入时将增大文件尺寸。

    whence:其取值如下:

    SEEK_SET:从文件开头移动offset个字节

    SEEK_CUR:从当前位置移动offset个字节

    SEEK_END:从文件末尾移动offset个字节

    返回值:

    成功:0

    失败:-1

    #include 

    long ftell(FILE *stream);

    功能:获取文件流(文件光标)的读写位置。

    参数:

    stream:已经打开的文件指针

    返回值:

    成功:当前文件流(文件光标)的读写位置

    失败:-1

    #include 

    void rewind(FILE *stream);

    功能:把文件流(文件光标)的读写位置移动到文件开头。

    参数:

    stream:已经打开的文件指针

    返回值:

    无返回值

    typedef struct Stu

    {

    char name[50];

    int id;

    }Stu;

    //假如已经往文件写入3个结构体

    //fwrite(s, sizeof(Stu), 3, fp);

    Stu s[3];

    Stu tmp;

    int ret = 0;

    //文件光标读写位置从开头往右移动2个结构体的位置

    fseek(fp, 2 * sizeof(Stu), SEEK_SET);

    //读第3个结构体

    ret = fread(&tmp, sizeof(Stu), 1, fp);

    if (ret == 1)

    {

    printf("[tmp]%s, %d\n", tmp.name, tmp.id);

    }

    //把文件光标移动到文件开头

    //fseek(fp, 0, SEEK_SET);

    rewind(fp);

    ret = fread(s, sizeof(Stu), 3, fp);

    printf("ret = %d\n", ret);

    int i = 0;

    for (i = 0; i < 3; i++)

    {

    printf("s === %s, %d\n", s[i].name, s[i].id);

    }

  • 相关阅读:
    Java,异常处理,异常的概述
    关于js实现斐波那契数列的一些思考(递归、循环、尾递归优化)
    web期末网站设计大作业 奶茶店网站美食餐饮网站设计与实现(HTML+CSS+JavaScript)
    【毕业设计】基于单片机的智能饮水机系统 - stm32 物联网 嵌入式
    注解与反射_注解
    【图神经网络论文整理】(七)—— Graph Transformer Networks:GTNs
    Go语言数据结构-堆
    Django — 介绍和搭建
    【JavaWeb】EL表达式&JSTL标签库
    PHP导出word方法(一phpword)
  • 原文地址:https://blog.csdn.net/MengSao1001/article/details/126446552