【C语言】文件操作 -- 详解(下)

简介: 【C语言】文件操作 -- 详解(下)

【C语言】文件操作 -- 详解(上)https://developer.aliyun.com/article/1514532?spm=a2c6h.13148508.setting.29.4b904f0ejdbHoA

3、字符输出函数 fputc

  • 将参数 char 指定的字符写入到指定的流 stream 中,并把位置标识符向前移动 (字符必须为一个无符号字符)。适用于所有输出流。

#include <stdio.h>
 
int main()
{
    FILE* pf = fopen("test.txt", "w");
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }
 
    fputc('a', pf);
    fputc('b', pf);
    fputc('c', pf);
 
    fclose(pf);
    pf = NULL;
 
    return 0;
}

abc 成功被写入。

倘若将代码修改为:

#include <stdio.h>
 
int main()
{
    FILE* pf = fopen("test.txt", "w");
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }
 
    /*fputc('a', pf);
    fputc('b', pf);
    fputc('c', pf);*/
 
    fclose(pf);
    pf = NULL;
 
    return 0;
}

此时再次运行,我们发现那个文件里的内容不见了(大小也变为0kb):


4、文本行输入函数 fgets

  • 从指定的流 stream 读取一行,并把它存储在 string 所指向的字符串中,当读取(n-1)个字符时,或者读取到换行符、到达文件末尾时,它会停止,具体视情况而定。适用于所有输入流。

注意:假如 n 是 100,读取到的就是 99 个字符(n-1),因为要留一个字符给 '\0'。

#include <stdio.h>
 
int main()
{
    char arr[10] = "xxxxxx"; // 存放处
 
    FILE* pf = fopen("test.txt", "r");
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }
 
    fgets(arr, 4, pf);
    printf("%s\n", arr);
 
    fgets(arr, 4, pf);
    printf("%s\n", arr);
 
    fclose(pf);
    pf = NULL;
 
    return 0;
}


5、文本行输出函数 fputs

  • 将字符串写入到指定的流 stream 中(不包括空字符)。适用于所有输出流。

#include <stdio.h>
 
int main()
{
    FILE* pf = fopen("test.txt", "w");
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }
 
    fputs("abcdef", pf);
    fputs("123456", pf);
 
    fclose(pf);
    pf = NULL;
 
    return 0;
}

如果想要在 "abcdef" 后面换行,只需在 fputs("abcdef\n", pf); 加上换行符即可。


6、 格式化输入函数 fscanf

  • fscanf 用于对格式化的数据进行读取,从流 stream 读取格式化输入。适用于所有输入流。

#include <stdio.h>
 
struct Student
{
    char name[10];
    int id;
    float score;
};
 
int main()
{
    struct Student s1 = { 0 };
 
    // 对格式化的数据进行写文件
    FILE* pf = fopen("test.txt", "r");
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }
 
    fscanf(pf, "%s %d %f", s1.name, &(s1.id), &(s1.score));
 
    printf("%s %d %f\n", s1.name, s1.id, s1.score);
 
    fclose(pf);
    pf = NULL;
 
    return 0;
}

注意: p1.name本身就是地址(不用&)。


7、格式化输出函数 fprintf

#include<stdio.h>
struct S
{
  char arr[10];
  int num;
  float sc;
};
 
int main()
{
  struct S s = { "abcdef", 10, 5.5f };
  FILE* pf1 = fopen("test.txt", "w");
  if(pf1 == NULL)
  {
    perror("fopen");
    return 1;
  }
 
  fprintf(pf1, "%s %d %f", s.arr, s.num, s.sc); // 将结构体格式化的数据写入文件
 
  fclose(pf1);
  pf1 = NULL;
 
  struct S temp = { 0 }; // 用于存储读出的数据 
  FILE* pf2 = fopen("test.txt", "r");
  if(pf2 == NULL)
  {
    perror("fopen");
    return 1;
  }
 
  fscanf(pf2, "%s %d %f", temp.arr, &(temp.num), &(temp.sc)); // 再把数据从文件中读到结构体中
 
  printf("%s %d %f\n", temp.arr, temp.num, temp.sc); // 打印
 
  fclose(pf2);
  pf2 = NULL;
  return 0;
}


8、二进制输入函数 fread

  • 从流中读取,从给定流 stream 读取数据到 buffer 所指向的数组中。

#include <stdio.h>
// 二进制的形式读
 
struct S
{
    char arr[10];
    int num;
    float score;
};
 
int main()
{
    struct S s = { 0 };
 
    FILE* pf = fopen("test.txt", "r");
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }
 
    fread(&s, sizeof(struct S), 1, pf);
 
    printf("%s %d %f", s.arr, s.num, s.score);
 
    fclose(pf);
    pf = NULL;
 
    return 0;
}

9、二进制输出函数 fwrite

  • 写一个数据到流中去,把 buffer 所指向的数组中的数据写入到给定流 stream 中。

#include <stdio.h>
// 二进制的形式写
 
struct S
{
    char arr[10];
    int num;
    float score;
};
 
int main()
{
    struct S s = { "abcde", 10, 5.5f };
 
    FILE* pf = fopen("test.txt", "w");
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }
    fwrite(&s, sizeof(struct S), 1, pf);
 
    fclose(pf);
    pf = NULL;
 
    return 0;
}

为什么是乱码?为什么 abcde 不是乱码?
  1. 我们刚才用的都是文本编译器,文本编译器打开二进制形式的文件完全是两种状态。
  2. 因为字符串以文本形式写进去和以二进制形式写进去是一样的,但是对于整数、浮点数等来说就不一样了,文本形式写入和二进制形式写入完全是两个概念。

结论:fwritefread 是一对,fwrire 写进去用 fread 读。


八、文件的随机读写

1、文件指针定位函数 fseek

  • 根据文件指针的位置和偏移量来定位文件指针。

int fseek (FILE* stream, long int offset, int origin);
 
// offset是偏移量,origin是起始位置,有三种选项:
 
// 1、SEEK_CUR - 当前文件指针的位置开始偏移。
// 2、SEEK_END - 文件的末尾位置开始偏移。
// 3、SEEK_SET - 文件的起始位置开始偏移。

#include <stdio.h>
 
int main()
{
    FILE* pf = fopen("test.txt", "r");
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }
 
    int ch = fgetc(pf);
    printf("%c\n", ch);
    ch = fgetc(pf);
    printf("%c\n", ch);
    ch = fgetc(pf);
    printf("%c\n", ch);
 
    fclose(pf);
    pf = NULL;
 
    return 0;
}

如果我想得到 a a b,该怎么处理呢?
#include <stdio.h>
 
int main()
{
    FILE* pf = fopen("test.txt", "r");
    if (pf == NULL) 
    {
        perror("fopen");
        return 1;
    }
 
    int ch = fgetc(pf);
    printf("%c\n", ch);
 
    // 调整文件指针
    fseek(pf, -1, SEEK_CUR); // SEEK_CUR为当前文件指针位置,偏移量为-1,向前移动1个单位
    ch = fgetc(pf);
    printf("%c\n", ch);
    ch = fgetc(pf);
    printf("%c\n", ch);
 
    fclose(pf);
    pf = NULL;
 
    return 0;
}


⚪用 SEEK_SET ,打印 a d e

#include <stdio.h>
 
int main()
{
    FILE* pf = fopen("test.txt", "r");
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }
 
    int ch = fgetc(pf);
    printf("%c\n", ch);
 
    // 调整文件指针
    fseek(pf, 2, SEEK_CUR); // SEEK_SET为文件的起始位置,偏移量为2,向后移动2个单位
    ch = fgetc(pf);
    printf("%c\n", ch);
    ch = fgetc(pf);
    printf("%c\n", ch);
 
    fclose(pf);
    pf = NULL;
 
    return 0;
}


⚪用 SEEK_END ,打印 a e f

#include <stdio.h>
 
int main()
{
    FILE* pf = fopen("test.txt", "r");
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }
 
    int ch = fgetc(pf);
    printf("%c\n", ch);
 
    // 调整文件指针
    fseek(pf, -2, SEEK_END); // SEEK_END为当前文件末尾位置,偏移量为-2,向前移动2个单位
    ch = fgetc(pf);
    printf("%c\n", ch);
    ch = fgetc(pf);
    printf("%c\n", ch);
 
    fclose(pf);
    pf = NULL;
 
    return 0;
}


2、返回偏移量函数 ftell

  • 返回文件指针相对于起始位置的偏移。

long int ftell ( FILE * stream );
#include <stdio.h>
 
int main()
{
    FILE* pf = fopen("test.txt", "r");
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }
 
    // 调整文件指针
    fseek(pf, 5, SEEK_CUR); // SEEK_CUR为当前文件指针位置,偏移量为5,向后移动5个单位
 
    int ch = fgetc(pf);
    printf("%c\n", ch); // f
 
    // 返回偏移量
    int ret = ftell(pf);
    printf("%d\n", ret); // 6
 
    fclose(pf);
    pf = NULL;
 
    return 0;
}


3、文件指针回到起始位置函数 rewind

  • rewind(意为倒带,磁带倒带),设置文件位置为给定流 stream 的文件的开头,让文件指针回到起始位置

#include <stdio.h>
 
int main()
{
    FILE* pf = fopen("test.txt", "r");
    if (pf == NULL)
    {
        perror("fopen");
        return 1;
    }
 
    // 调整文件指针
    fseek(pf, 5, SEEK_CUR); // SEEK_CUR为当前文件指针位置,偏移量为5,向后移动5个单位
 
    // 返回偏移量
    int loc = ftell(pf);
    printf("fseek调整文件指针后:%d\n", loc); // 6
 
    // 让文件指针回到起始位置
    rewind(pf);
 
    // 再次返回偏移量,看看是不是回到起始位置了
    loc = ftell(pf);
    printf("使用rewind后:%d\n", loc); // 6
 
    fclose(pf);
    pf = NULL;
 
    return 0;
}


九、文件读取结束的判定

1、被错误使用的 feof

  • 在文件结束时判断文件因为何种原因导致文件结束的函数判断是因为读取失败而结束,还是因为遇到文件尾而结束。如果文件结束,则返回非 0 值,否则返回 0。

注意feof 函数是个经常被错误使用的一个函数。在文件读取过程中,不能用 feof 函数的返回值直接判断文件是否结束!feof 函数绝对不是用来判断文件是否结束的函数!feof 不是用来判定文件是否结束了的,还是在文件已经结束时,判断是什么原因导致文件结束的

文本文件读取是否结束,判断返回值是否为EOF fgetc),或者NULLfgets

  • fgetc 判断是否为 EOF。
  • fgets 判断返回值是否为 NULL。
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    int ch = 0; // 注意:int,非char,要求处理EOF
    FILE* pf = fopen("test.txt", "r");
    if (!pf)
    {
        perror("fopen");
        return EXIT_FAILURE; // 符号常量EXIT_FAILURE,表示没有成功地执行一个程序
    }
 
    // fgetc - 当读取失败的时候或者遇到文件结束的时候,都会返回EOF
    while ( (ch = fgetc(pf)) != EOF )
    {
        putchar(ch);
    }
    printf("\n");
 
    // 判断文件结束的原因
    if (ferror(pf)) // ferror - 检查是否出现错误
    {  
        puts("读取失败错误(I/O error when reading)");
    }
    else if (feof(pf))
    {
        puts("遇到文件尾而结束(End of file reached successfully)");
    }
 
    fclose(pf);
    pf = NULL;
}


二进制文件的读取结束判断,判断返回值是否小于实际要读的个数

  • fread 判断返回值是否小于实际要读的个数。
#include <stdio.h>
 
enum { SIZE = 5 };
 
int main(void)
{
    double a[SIZE] = {1.0,2.0,3.0,4.0,5.0};
    double b = 0.0;
    size_t ret_code = 0;
 
    FILE *fp = fopen("test.bin", "wb"); // 必须用二进制模式
    fwrite(a, sizeof(*a), SIZE, fp); // 写double的数组
    fclose(fp);
    fp = fopen("test.bin","rb");
 
    // 读 double 的数组
    while((ret_code = fread(&b, sizeof(double), 1, fp))>=1)
    {
        printf("%lf\n",b);
    }
 
    if (feof(fp))
    {
        printf("Error reading test.bin: unexpected end of file\n");
    }
    else if (ferror(fp))
    {
        perror("Error reading test.bin");
    }
 
    fclose(fp);
    fp = NULL;
}


2、正确判定文件是否读取结束的方法

  • 文本文件读取是否结束,判断返回值是否为 EOFfgetc),或者 NULLfgets)。
  1. fgetc 函数在读取结束时会返回 EOF,正常读取时,返回读取到的字符的 ASCII 码值
  2. fgets 函数在读取结束时会返回 NULL,正常读取时,返回存放字符串的空间的起始地址
  3. fread 函数在读取结束时会返回实际读取到的完整元素的个数,如果发现读取到的完整的元素个数小于指定的元素个数,那么就是最后一次读取了。


相关文章
|
27天前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
44 9
|
28天前
|
存储 数据管理 C语言
C 语言中的文件操作:数据持久化的关键桥梁
C语言中的文件操作是实现数据持久化的重要手段,通过 fopen、fclose、fread、fwrite 等函数,可以实现对文件的创建、读写和关闭,构建程序与外部数据存储之间的桥梁。
|
1月前
|
算法 C语言
C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项
本文深入讲解了C语言中的文件操作技巧,涵盖文件的打开与关闭、读取与写入、文件指针移动及注意事项,通过实例演示了文件操作的基本流程,帮助读者掌握这一重要技能,提升程序开发能力。
109 3
|
1月前
|
存储 C语言
【c语言】玩转文件操作
本文介绍了C语言中文件操作的基础知识,包括文件的打开和关闭、文件的顺序读写、文件的随机读写以及文件读取结束的判定。详细讲解了`fopen`、`fclose`、`fseek`、`ftell`、`rewind`等函数的使用方法,并通过示例代码展示了如何进行文件的读写操作。最后,还介绍了如何判断文件读取结束的原因,帮助读者更好地理解和应用文件操作技术。
51 2
|
2月前
|
存储 C语言
C语言文件操作(2)
【10月更文挑战第2天】
|
3月前
|
C语言
C语言——文件操作
本文介绍了文件的基本操作,包括文件的打开、关闭、读取和写入。使用`fopen`函数以不同模式(如“r”、“w”等)打开文件,并通过`fclose`关闭。文章详细解释了如何利用`fputc`、`fputs`及`fprintf`进行格式化写入,同时介绍了`fgetc`、`fgets`和`fscanf`用于文件内容的读取。此外,还涵盖了二进制文件的读写方法以及如何通过`fseek`、`ftell`和`rewind`实现文件的随机访问。
60 1
C语言——文件操作
|
2月前
|
程序员 编译器 C语言
C语言底层知识------文件操作
本文详细介绍了文件操作的基本概念,包括文件的分类(程序文件和数据文件,其中着重于数据文件的文本文件和二进制文件),流的概念及其在C程序中的应用,以及标准输入输出流stdin、stdout和stderr的作用。作者通过示例展示了如何使用fopen、fclose和常见的读写函数如fgetc、fputc和fgets进行文件操作。
35 2
|
2月前
|
存储 缓存 编译器
文件操作——C语言
文件操作——C语言
|
2月前
|
存储 C语言
简述C语言文件操作
简述C语言文件操作
12 0
|
2月前
|
存储 文件存储 C语言
深入C语言:文件操作实现局外影响程序
深入C语言:文件操作实现局外影响程序