当前位置: 首页 > article >正文

Linux自学day20-流的输入与输出

 一     fputc、fgetc的用法

1.1用fputc和fgetc完成文件内容的拷贝,代码如下:

// 函数功能:将源文件的内容复制到目标文件
// 参数:pdstfilename - 目标文件名的指针,psrcfilename - 源文件名的指针
// 返回值:成功返回0,失败返回-1
int copy_file_content(const char *pdstfilename, const char *psrcfilename)
{
    FILE *fsrc = NULL;  // 定义一个文件指针,用于指向源文件
    FILE *fdst = NULL;  // 定义一个文件指针,用于指向目标文件
    char ch = 0;  // 用于存储从源文件读取的字符

    // 以只读模式打开源文件
    fsrc = fopen(psrcfilename, "r");
    if (NULL == fsrc)
    {
        ERR_MSG("打开源文件失败");  // 若打开源文件失败,输出错误信息
        goto err_open_srcfile;  // 跳转到错误处理标签
    }

    // 以写入模式打开目标文件
    fdst = fopen(pdstfilename, "w");
    if (NULL == fdst)
    {
        ERR_MSG("打开目的文件失败");  // 若打开目标文件失败,输出错误信息
        goto err_open_dstfile;  // 跳转到错误处理标签
    }

    while (1)
    {
        // 从源文件中读取一个字符
        ch = fgetc(fsrc);
        // 如果读取到文件末尾(EOF表示文件结束符),则退出循环
        if (EOF == ch)
        {
            break;
        }
        // 将读取到的字符写入目标文件
        fputc(ch, fdst);
    }

    // 关闭源文件和目标文件
    fclose(fsrc);
    fclose(fdst);
    
    return 0;  // 复制成功,返回0

// 打开目标文件失败的错误处理
err_open_dstfile:
    fclose(fsrc);  // 关闭已打开的源文件
// 打开源文件失败的错误处理
err_open_srcfile:
    return -1;  // 复制失败,返回-1
}

// 主函数,程序入口
int main(int argc, const char **argv)
{
    char srcfilename[32] = {0};  // 存储源文件名的数组
    char dstfilename[32] = {0};  // 存储目标文件名的数组
    int ret = 0;  // 存储函数返回值

    // argv[0] = ./a.out ,程序本身的名称
    // argv[1] = srcfilename ,第一个命令行参数,即源文件名
    // argv[2] = dstfilename ,第二个命令行参数,即目标文件名
    if (argc != 3)
    {
        printf("usage:./a.out srcfilename dstfilename\n");  // 若参数个数不正确,输出使用方法
        printf("command: this command is used to copy srcfilename content to dstfilename\n");
        return -1;  // 参数错误,返回-1
    }

    // 调用copy_file_content函数进行文件复制
    ret = copy_file_content(argv[2], argv[1]);
    if (ret != 0)
    {
        printf("拷贝失败!\n");  // 若复制失败,输出错误信息
        return -1;  // 复制失败,返回-1
    }

    printf("拷贝成功!\n");  // 复制成功,输出成功信息

    return 0;  // 程序正常结束,返回0
}

1.2fgetc 函数的用法

fgetc 函数用于从指定的文件流中读取一个字符。其原型为:

int fgetc(FILE *stream);
  • stream 是指向文件的指针,表示要读取的文件流。
  • 函数返回值:如果读取成功,返回读取到的字符(以 int 类型返回);如果到达文件末尾或发生错误,返回 EOFEOF 是一个宏,通常定义为 -1)。

1.3fputc 函数的用法

fputc 函数用于将一个字符写入指定的文件流。其原型为:

int fputc(int c, FILE *stream);
  • c 是要写入的字符(以 int 类型传入)。
  • stream 是指向文件的指针,表示要写入的文件流。
  • 函数返回值:如果写入成功,返回写入的字符;如果发生错误,返回 EOF

二     fputs、fgets的用法 

2.1 用fputs、fgets完成文件内容的拷贝,代码如下:

// 函数功能:将源文件的内容复制到目标文件
// 参数:pdstfile - 目标文件的路径
//       psrcfile - 源文件的路径
// 返回值:0 - 复制成功,-1 - 复制失败
int copy_file_content(const char *pdstfile, const char *psrcfile)
{
    FILE *psrc = NULL;  // 源文件的文件指针,初始化为NULL
    FILE *pdst = NULL;  // 目标文件的文件指针,初始化为NULL
    char *pret = NULL;  // fgets函数的返回值,用于判断是否读取到数据
    char tmpbuff[1024] = {0};  // 临时缓冲区,用于存储从源文件读取的数据

    // 以只读模式打开源文件
    psrc = fopen(psrcfile, "r");
    if (NULL == psrc)
    {
        ERR_MSG("打开文件失败");  // 打开源文件失败,输出错误信息
        goto err_open_srcfile;  // 跳转到错误处理标签
    }

    // 以写入模式打开目标文件
    pdst = fopen(pdstfile, "w");
    if (NULL == pdst)
    {
        ERR_MSG("打开文件失败");  // 打开目标文件失败,输出错误信息
        goto err_open_dstfile;  // 跳转到错误处理标签
    }

    // 循环读取源文件内容并写入目标文件
    while (1)
    {
        // 从源文件中读取一行数据,最多读取 sizeof(tmpbuff)-1 个字符
        pret = fgets(tmpbuff, sizeof(tmpbuff), psrc);
        if (NULL == pret)
        {
            break;  // 读取结束,跳出循环
        }
        // 将读取到的数据写入目标文件
        fputs(tmpbuff, pdst);
    }
    
    // 关闭源文件和目标文件
    fclose(psrc);
    fclose(pdst);

    return 0;  // 复制成功,返回0

err_open_dstfile:
    fclose(psrc);  // 关闭已打开的源文件
err_open_srcfile:
    return -1;  // 复制失败,返回-1
}

// 主函数,程序入口
int main(int argc, const char **argv)
{
    int ret = 0;  // 用于存储 copy_file_content 函数的返回值

    // 检查命令行参数个数是否正确
    if (argc != 3)
    {
        printf("Usage:./a.out srcfile dstfile\n");  // 输出使用说明
        return -1;  // 参数个数错误,返回-1
    }

    // 调用 copy_file_content 函数进行文件复制
    ret = copy_file_content(argv[2], argv[1]);
    if (ret != 0)
    {
        printf("拷贝失败!\n");  // 复制失败,输出错误信息
    }
    else 
    {
        printf("拷贝成功!\n");  // 复制成功,输出成功信息
    }

    return 0;  // 程序正常结束,返回0
}

2.2 fgets 函数

  • 函数原型char *fgets(char *str, int n, FILE *stream);
  • 功能:从指定的文件流中读取一行数据,最多读取 n-1 个字符,并将读取到的数据存储到 str 指向的缓冲区中。读取的数据会自动在末尾添加 '\0' 作为字符串的结束符。
  • 参数
    • str:指向存储读取数据的缓冲区的指针。
    • n:要读取的最大字符数(包括 '\0')。
    • stream:指向要读取的文件的文件指针。
  • 返回值
    • 如果读取成功,返回 str 的地址。
    • 如果到达文件末尾或出错,返回 NULL

2.3 fputs 函数

  • 函数原型int fputs(const char *str, FILE *stream);
  • 功能:将 str 指向的字符串写入到指定的文件流中。
  • 参数
    • str:要写入的字符串。
    • stream:指向要写入的文件的文件指针。
  • 返回值
    • 如果写入成功,返回非负整数。
    • 如果出错,返回 EOF

三     fputc、fgetc和fputs、fgets的异同

在文件内容拷贝场景中,通常有两种常见的实现思路:一种是按字符拷贝(使用 fgetc 和 fputc 函数),另一种是按行拷贝(使用 fgets 和 fputs 函数)。

3.1 相同点

1. 最终目的相同

两种方法的核心目的都是将一个文件(源文件)的内容复制到另一个文件(目标文件)中,完成文件内容的拷贝操作。

2. 基本操作流程类似

都需要进行文件的打开、内容读取、内容写入以及文件关闭这些基本操作。以按字符拷贝和按行拷贝为例,一般都遵循以下步骤:

  • 打开源文件和目标文件。
  • 从源文件读取内容。
  • 将读取的内容写入目标文件。
  • 关闭源文件和目标文件。
3. 错误处理机制类似

在文件打开、读取和写入过程中,都需要进行错误检查和处理。如果文件打开失败、读取出现错误或者写入失败,都需要相应地处理错误情况,以保证程序的健壮性。

3.2 不同点

1. 读取和写入单位不同
  • 按字符拷贝(fgetc 和 fputc:每次从源文件读取一个字符,然后将这个字符写入目标文件。这种方式逐字符处理文件内容,适用于处理任意类型的文件,包括二进制文件和文本文件。
  • 按行拷贝(fgets 和 fputs:每次从源文件读取一行文本,然后将这一行文本写入目标文件。这种方式更适用于处理文本文件,因为它是以换行符作为行的分隔标志。
2. 性能差异
  • 按字符拷贝:由于每次只处理一个字符,会频繁进行文件的读取和写入操作,系统开销较大,尤其是对于大文件,性能相对较低。
  • 按行拷贝:一次性读取和写入一行数据,减少了文件操作的次数,系统开销相对较小,在处理文本文件时性能通常优于按字符拷贝。
3. 适用场景不同
  • 按字符拷贝:适用于所有类型的文件,因为它不依赖于特定的文本格式,可以准确地复制任何文件内容,包括二进制文件(如图片、视频等)。
  • 按行拷贝:主要适用于文本文件,因为它假设文件内容是以行为单位组织的。如果用于处理二进制文件,可能会因为换行符的处理问题导致数据损坏。
4. 代码复杂度
  • 按字符拷贝:代码逻辑相对简单,只需要一个循环不断读取和写入字符即可。
  • 按行拷贝:需要处理行缓冲区的大小问题,并且要确保读取的行不会超出缓冲区的范围,代码相对复杂一些。

四       fprintf、fscanf的用法

4.1 fprintf的用法

 fprintf是一个用于将格式化的数据写入文件的函数,其原型为:

int fprintf(FILE *stream, const char *format, ...);
  • FILE *stream:指向要写入的文件的指针。

  • const char *format:格式化字符串,类似于printf中的格式化字符串,用于指定输出的格式。

  • ...:可变参数列表,根据格式化字符串中的格式说明符提供相应的参数。

4.2 fscanf的用法

fscanf是一个用于从文件中读取格式化数据的函数,其原型为:

int fscanf(FILE *stream, const char *format, ...);
  • FILE *stream:指向要读取的文件的指针。
  • const char *format:格式化字符串,类似于scanf中的格式化字符串,用于指定输入的格式。
  • ...:可变参数列表,根据格式化字符串中的格式说明符提供相应的变量地址,用于存储读取到的数据。
// argc是命令行参数的数量,argv是指向命令行参数字符串数组的指针
int main(int argc, const char **argv)
{
    FILE *fp = NULL;  // 定义一个文件指针fp,并初始化为NULL
    char tmpbuff[1024] = {0};  // 定义一个字符数组tmpbuff,用于存储从文件中读取的字符串,初始化为全0
    int num = 0;  // 定义一个整型变量num,用于存储从文件中读取的整数,初始化为0
    double d = 0;  // 定义一个双精度浮点型变量d,用于存储从文件中读取的浮点数,初始化为0

    // 以只读模式打开名为"a.txt"的文件
    fp = fopen("a.txt", "r");
    if (NULL == fp)  // 检查文件是否成功打开
    {
        ERR_MSG("fail fopen");  // 如果文件打开失败,输出错误信息
        return -1;  // 返回错误码-1
    }

    // 从文件中读取一个字符串到tmpbuff中,遇到空格或换行符停止读取
    fscanf(fp, "%s", tmpbuff);
    printf("tmpbuff = %s\n", tmpbuff);  // 打印读取到的字符串

    // 再次从文件中读取一个字符串到tmpbuff中,遇到空格或换行符停止读取
    fscanf(fp, "%s", tmpbuff);
    printf("tmpbuff = %s\n", tmpbuff);  // 打印读取到的字符串

    fgetc(fp);  // 从文件中读取一个字符,用于跳过换行符

    // 从文件中读取一个整数,格式为"num = 数字",将数字存储到num变量中
    fscanf(fp, "num = %d", &num);
    printf("num = %d\n", num);  // 打印读取到的整数

    fgetc(fp);  // 从文件中读取一个字符,用于跳过换行符

    // 从文件中读取一个双精度浮点数,格式为"d = 浮点数",将浮点数存储到d变量中
    fscanf(fp, "d = %lf", &d);
    printf("d = %lf\n", d);  // 打印读取到的浮点数

    fclose(fp);  // 关闭文件

#if 0
    FILE *fp = NULL;  // 定义一个文件指针fp,并初始化为NULL
    int num = 100;  // 定义一个整型变量num,初始化为100
    double d = 3.14;  // 定义一个双精度浮点型变量d,初始化为3.14

    // 以写入模式打开名为"a.txt"的文件
    fp = fopen("a.txt", "w");
    if (NULL == fp)  // 检查文件是否成功打开
    {
        ERR_MSG("fopen failed");  // 如果文件打开失败,输出错误信息
        return -1;  // 返回错误码-1
    }

    // 将字符串"hello world\n"写入文件
    fprintf(fp, "hello world\n");
    // 将格式化的字符串"num = 数字\n"写入文件,其中数字为num的值
    fprintf(fp, "num = %d\n", num);
    // 将格式化的字符串"d = 浮点数\n"写入文件,其中浮点数为d的值
    fprintf(fp, "d = %lf\n", d);

    fclose(fp);  // 关闭文件
#endif

    return 0;  // 程序正常结束,返回0
}

五     fwrite、fread的用法

5.1 fwrite 函数的用法

  • 函数原型size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
  • 功能:将数据从内存写入到文件中。
  • 参数
    • ptr:指向要写入数据的内存地址的指针。
    • size:每个数据项的字节大小。
    • count:要写入的数据项的数量。
    • stream:指向要写入的文件的指针。
  • 返回值:返回实际写入的数据项的数量。如果发生错误,返回值可能小于 count

5.2 fread 函数的用法

  • 函数原型size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
  • 功能:从文件中读取数据到内存中。
  • 参数
    • ptr:指向用于存储读取数据的内存地址的指针。
    • size:每个数据项的字节大小。
    • count:要读取的数据项的数量。
    • stream:指向要读取的文件的指针。
  • 返回值:返回实际读取的数据项的数量。如果到达文件末尾或发生错误,返回值可能小于 count。如果返回值为 0,表示文件读取结束或发生错误。

5.3 写入和读取学生信息:

// 定义一个名为 student 的结构体,用于存储学生信息
typedef struct student 
{
    char name[32];  // 学生姓名,最多31个字符加上字符串结束符 '\0'
    char sex;       // 学生性别,用一个字符表示
    int age;        // 学生年龄
    int score;      // 学生成绩
} stu_t;

// 主函数,程序的入口点
int main(int argc, const char **argv)
{
    stu_t s[5];      // 定义一个包含5个 stu_t 结构体的数组,用于存储从文件中读取的学生信息
    FILE *fp = NULL; // 定义一个文件指针,初始化为 NULL
    size_t nret = 0; // 用于存储 fread 函数的返回值,表示实际读取的对象数量
    int i = 0;       // 循环计数器

    // 以只读模式打开名为 "a.txt" 的文件
    fp = fopen("a.txt", "r");
    if (NULL == fp)
    {
        ERR_MSG("fail to fopen"); // 如果文件打开失败,输出错误信息
        return -1;                // 程序返回 -1 表示错误
    }

    // 循环读取文件内容,直到读取结束
    while (1)
    {
        // 从文件中读取最多5个 stu_t 结构体对象到数组 s 中
        nret = fread(s, sizeof(stu_t), 5, fp);
        if (0 == nret)
        {
            break; // 如果读取到的对象数量为0,表示文件读取结束,退出循环
        }
        printf("读到 %ld 个对象\n", nret); // 输出实际读取到的对象数量

        // 遍历读取到的对象,打印每个学生的信息
        for (i = 0; i < nret; i++)
        {
            printf("姓名:%s\n", s[i].name); // 打印学生姓名
            printf("性别:%c\n", s[i].sex);  // 打印学生性别
            printf("年龄:%d\n", s[i].age);  // 打印学生年龄
            printf("成绩:%d\n", s[i].score); // 打印学生成绩
        }
    }

    // 关闭文件
    fclose(fp);

#if 0
    // 定义一个包含3个 stu_t 结构体的数组,并初始化学生信息
    stu_t s[3] = {
        {"zhangsan", 'm', 19, 100},
        {"lisi", 'f', 18, 90},
        {"wager", 'm', 17, 60},
    };
    int i = 0;       // 循环计数器
    int cnt = 0;     // 用于存储 fwrite 函数的返回值,表示实际写入的对象数量

    FILE *fp = NULL; // 定义一个文件指针,初始化为 NULL

    // 以写入模式打开名为 "a.txt" 的文件
    fp = fopen("a.txt", "w");
    if (NULL == fp)
    {
        ERR_MSG("fail to fopen"); // 如果文件打开失败,输出错误信息
        return -1;                // 程序返回 -1 表示错误
    }

    // 将数组 s 中的3个 stu_t 结构体对象写入文件
    cnt = fwrite(s, sizeof(stu_t), 3, fp);

    printf("写入 %d 个元素\n", cnt); // 输出实际写入的对象数量

    // 逐个将数组 s 中的 stu_t 结构体对象写入文件
    for (i = 0; i < 3; i++)
    {
        fwrite(s + i, sizeof(stu_t), 1, fp);
    }

    // 关闭文件
    fclose(fp);
#endif

    return 0; // 程序正常结束,返回 0
}

六     fseek、ftell的用法

6.1 fseek 函数的用法

fseek 函数用于设置文件指针的位置。其函数原型为:

int fseek(FILE *stream, long int offset, int whence);
  • stream:指向要操作的文件的指针。
  • offset:偏移量,表示要移动的字节数。可以是正数(向前移动)、负数(向后移动)或 0。
  • whence:起始位置,有以下三个取值:
    • SEEK_SET:从文件开头开始偏移。
    • SEEK_CUR:从文件指针的当前位置开始偏移。
    • SEEK_END:从文件末尾开始偏移。

fseek 函数的返回值为 0 表示成功,非 0 表示失败。

6.2 ftell 函数的用法

ftell 函数用于获取文件指针的当前位置。其函数原型为:

long int ftell(FILE *stream);
  • stream:指向要操作的文件的指针。

ftell 函数返回文件指针的当前位置,即从文件开头到当前位置的字节数。如果发生错误,返回 -1L。

6.3 用fseek、ftell获得文件大小:

// 函数功能:获取指定文件的长度
// 参数:pfilename - 指向文件名的指针
// 返回值:如果文件打开成功,返回文件的长度(字节数);如果文件打开失败,返回 -1
long get_file_len(char *pfilename)
{
    FILE *fp = NULL;  // 定义一个文件指针,初始化为 NULL
    long len = 0;     // 用于存储文件长度的变量,初始化为 0

    // 以只读模式打开文件
    fp = fopen(pfilename, "r");
    // 检查文件是否成功打开,如果文件打开失败,fopen 返回 NULL
    if (NULL == fp)
    {
        return -1;  // 文件打开失败,返回 -1 表示错误
    }

    // 将文件指针移动到文件末尾
    // fseek 函数用于移动文件指针,第一个参数是文件指针,第二个参数是偏移量,第三个参数是起始位置
    // 这里偏移量为 0,起始位置为 SEEK_END,表示从文件末尾开始偏移 0 个字节,即移动到文件末尾
    fseek(fp, 0, SEEK_END);

    // 获取文件指针的当前位置,即文件的长度
    // ftell 函数用于获取文件指针的当前位置,返回值是从文件开头到当前位置的字节数
    len = ftell(fp);

    // 关闭文件,释放文件资源
    fclose(fp);

    return len;  // 返回文件的长度
}

// 主函数,程序的入口点
int main(int argc, const char **argv)
{
    char filename[32] = {0};  // 定义一个字符数组,用于存储用户输入的文件名,初始化为 0
    long len = 0;             // 用于存储文件长度的变量,初始化为 0

    // 提示用户输入文件名
    printf("请输入文件名:\n");
    // 读取用户输入的文件名,注意:gets 函数存在缓冲区溢出的风险,建议使用 fgets 替代
    gets(filename);

    // 调用 get_file_len 函数获取文件长度
    len = get_file_len(filename);

    // 输出文件的长度
    printf("文件大小: %ld\n", len);

    return 0;  // 程序正常结束,返回 0
}


http://www.kler.cn/a/549824.html

相关文章:

  • iPhone 智能进化:Siri 调用 DeepSeek 大模型
  • 二.国产化系统—银河麒麟系统网络无法访问/无法使用/网络受限
  • 高等代数笔记—欧几里得空间、双线性函数
  • JavaScript 中处理 object 对象类型的常见方法
  • 支持列表拖拽嵌套,AI流式输出的多模态文档编辑器flowmix/docx: 全面升级
  • 《图解设计模式》笔记(十)用类来表现
  • 从2025年起:数字化建站PHP 8.1应成为建站开发的基准线
  • 解锁网络安全:穿越数字世界的防护密码
  • GPU(Graphics Processing Unit)详解
  • 图论- 经典最小生成树算法
  • 用xml配置spring, bean标签有哪些属性?
  • 深度解析HTTP/HTTPS协议:从原理到实践
  • 多模态基础模型训练笔记-第一篇InternVL-g
  • vSamOut
  • APIPark 新增 AI模型负载均衡,APIKey 资源池以及 AI Token 消耗统计等重磅功能!
  • day09_实时类标签/指标
  • 【2025最新版】软件测试面试题总结(150道题含答案解析)
  • JavaScript前端开发:构建交互式网页的魔法
  • repo学习使用
  • 传统混合专家模型MoE架构详解以及python示例(DeepSeek-V3之基础)