【C/Cpp】文件操作

数据要进行持久化存储就需要用到文件。C/C++ 将文件看作有序的字节流,每个文件都是以 EOF(文件结束标志)结束。

按数据的组织形式,文件可分为文本文件和二进制文件。
数据在内存中以二进制形式存储的,如果不加转换地输出到外存,就是二进制文件。如果要求在外存上以 ASCII 码形式存储,需要在存储前进行转换。以ASCII 字符的形式存储的文件是文本文件。

  • 字符一律以 ASCII 形式存储
  • 数据型数据既可以用 ASCII 形式存储,也可以使用二进制形式存储。
    例如:整数 12345,如果以 ASCII 码的形式存储输出到磁盘,则占用5个字节,而以二进制形式输出,则在磁盘上只占4个字节。

在 C 语言中,流(stream)可以被视为一种特殊的数据结构,它负责在程序和外部设备(如键盘,显示器,文件等)之间进行数据传输。

C语言提供了标准流方便进行输入输出操作。C/C++ 程序在启动时,默认打开3个流:

  • stdin: 标准输入流,一般从键盘输入
  • stdout: 标准输出流,一般输出至控制台
  • stderr: 标准错误流,一般输出到控制台
    这三个流的类型都是 FILE*,通常称为文件指针。
    C/C++中就是通过文件指针来维护流的各种操作的。

文件指针

每个打开的文件都在内存中开辟一块区域,用来存放文件的相关信息(如文件名称,文件状态,文件当前位置等),这些信息是保存在一个 FILE 类型的结构体变量中的。

struct _iobuf {
    char *_ptr;
    int _cnt;
    char *_base;
    int _flag;
    int _file;
    int _charbuf;
    int _bufsiz;
    char *_tmpfname;
  };
typedef struct _iobuf FILE;

一般通过 FILE* 指针来维护该结构体变量,通过文件指针能间接找到它关联的文件。

文件操作函数

文件的操作步骤:

  1. 打开文件
  2. 对文件进行读、写操作
  3. 使用完文件后要关闭文件

打开文件 fopen() 和 关闭文件 fclose()

打开文件,fopen() 函数的声明:

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

参数:

  • filename: 文件名。默认为当前工作目录,可以指定路径,如:"D:\file\data.txt"
  • mode: 文件打开模式,即文件访问的权限

文本文件打开模式:

  • "r" : 只读,文件必须存在
  • "w" : 只写,会覆盖原内容,文件不存在会新建文件
  • "a" : 只写,写入数据会被附加到文件末尾,文件不存在会新建文件
  • "r+" : 读/写,文件必须存在
  • "w+" : 读/写,写会覆盖原内容,文件不存在将新建文件
  • "a+" : 读/写,写入数据会被附加到文件末尾,文件不存在会新建文件
    二进制文件打开模式:"rb"、"wb"、"ab"、"rb+"、"wb+"、"ab+"。

关闭文件,fclose() 的声明:

int fclose(FILE* stream);

使用方式:

FILE *in, *out;
in = fopen("data.in", "r");
out = fopen("data.out", "w");
//读、写操作
fclose(in);
fclose(out);

对文件进行读/写操作

文件光标操作

fseek()

移动光标。

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

参数:

  • stream: 指明要操作的文件
  • offset: 偏移量,以字节为单位
  • origin: 偏移的起始位置
    • SEEK_SET:文件开始位置
    • SEEK_CUR:光标当前位置
    • SEEK_END: 文件末尾

rewind()

光标偏移到文件头。

void rewind(FILE* fp);

feof()

判断光标是否在文件末尾。

int feof(FILE* stream);

返回值: 当光标在文件尾时返回非零值,不是返回0。

ftell()

获取光标位置

long ftell(FILE* fp);

例如,测算文件大小

fseek(fp, 0L, SEEK_END);
int size = ftell(fp);

字符输入 fgetc() 和输出 fputc()

fgetc()从指定的流获取一个字符,并将位置标识符往前移动,声明:

int fgetc(FILE* stream);

参数:

  • stream: 文件,要被读取的流
    返回值:以无符号 char 强制转换为 int 的形式返回读取的字符,如果到达文件末尾或发生错误,则返回 EOF。

fputc()把参数 char 指定的字符写入到指定的流中,并将位置标识符往前移动,声明:

int fputc(int char, FILE* stream); 

参数:

  • char: 要写入的字符,以对应的 int 值进行传递
  • stream: 文件指针,要被写入字符的流

举例:

#include <cstdio>

int main()
{
  FILE* pfread = fopen("data1.txt", "r");
  if (pfread == nullptr)
  {
    perror("fopen->data1.txt");//打开data1.txt失败
    return 1;
  }
  FILE* pfwrite = fopen("data2.txt", "w");
  if (pfwrite == nullptr)
  {
    fclose(pfread);//关闭data1.txt
    pfread = nullptr;//释放pread的空间
    perror("fopen->data2.txt");//打开data2.txt失败
    return 1;
  }
  //数据的读写(拷贝)
  int ch = 0;
  while ((ch = fgetc(pfread)) != EOF)
  {
    fputc(ch, pfwrite);
  }
  fclose(pfread);
  fclose(pfwrite);
  return 0;
}

文本行输入 fgets() 和输出 fputs()

fgets() 函数声明:

char* fgets(cahr* buf, int max_count, FILE* stream);

参数:

  • buf: 字符数组指针,该数组存储要读取的字符串
  • max_count: 要读取的最大字符数。
  • stream: 要被读取的流
    返回值:读取到的字符串,读完后再读返回值是 NULL。
    fgets() 从 stream 中读取字符串,并将字符串存入 buf 中。当读取到 max_count-1 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止。

fputs() 声明:

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

参数:

  • str: 要写入的字符串
  • stream : 要被写入的流
    返回值:成功写入返回非负值,失败返回EOF。

举例:

#include <cstdio>
#include <iostream>

int main() {
    FILE *pf = fopen("data.txt", "w+");
    if(pf == nullptr) {
        return 1;
    }
    //写文件。fputs不会自动换行
    fputs("abcd\n", pf);
    fputs("efgh\n", pf);
    rewind(pf);
    //读取
    char buf[20];
    fgets(buf, 20, pf);
    std::cout << buf;
    fclose(pf);
    pf = nullptr;
    return 0;
}

格式化输入 fscanf() 和输出 fprintf()

使用方法类似 scanf()printf()。格式化输入 fscanf() 和输出 fprintf()声明:

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

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

举例:

#include <iostream>

struct Stu
{
    int num;
    char name[20];
    char sex;
    int age;
};
int main()
{
    Stu s{0};
    FILE* pf = fopen("data.txt", "r");
    if (pf == nullptr) {
        return 1;
    }
    //写读文件
    fscanf(pf, "%d%s %c%d", &s.num, s.name, &s.sex, &s.age);
    fprintf(stdout, "%d %s %c %d\n", s.num, s.name, s.sex, s.age);
    fclose(pf);
    pf = nullptr;
    return 0;
}

二进制输入 fread() 和输出 fwrite()

fread() 从给定流 stream 读取数据到 _DstBuf 所指向的空间,声明:

size_t fread(void* _DstBuf,size_t _ElementSize,size_t _Count,FILE* stream);

参数:

  • _DstBuf:指向一块内存空间的指针,其大小至少为_ElementSize*_Count 字节
  • _ElementSize: 要读取的每个元素的大小,以字节为单位
  • _Count: 要读取的元素个数
  • stream: 输入流

fwrite() 将 _Str 所指向的数据写入到给定流 stream 中,声明:

size_t fwrite(const void* _Str,size_t _Size,size_t _Count,FILE* stream);

参数:

  • _Str: 指向被写入的数据的指针
  • _Size: 要被写入的每个元素的大小,以字节为单位
  • _Count: 要读取的元素个数
  • stream: 输出流

举例

#include <iostream>

struct Stu
{
    int num;
    char name[20];
    char sex;
    int age;
};

int main()
{
    Stu s1[3] = {1, "HH", 'M', 20,
                2, "ZZ", 'M', 21,
                3, "YY", 'M', 19,};
    Stu *s2 = (Stu*)malloc(sizeof(s1[0]) * 3);
    FILE* pf = fopen("data.txt", "wb+");
    if (pf == nullptr)
    {
        return 1;
    }
    //二进制的形式读写文件
    fwrite(s1, sizeof(s1[0]), 3, pf);
    rewind(pf);
    fread(s2, sizeof(s1[0]), 3, pf);
    for (int i = 0; i < 3; i++) {
        printf("%d %s %c %d\n", (s2+i)->num, (s2+i)->name,
               (s2+i)->sex, (s2+i)->age);
    }
    fclose(pf);
    pf = nullptr;
    return 0;
}

freopen 函数

freopen 函数将 filename 所指文件与给定的 stream 流关联起来,主要用来重定向标准流。freopen() 函数的声明:

FILE* freopen(const char* filename, const char* mode, FILE* stream);

参数:

  • filename: 要打开文件的名称
  • mode: 文件打开模式,即文件访问的权限
  • stream: 文件要关联的流,通常是标准文件流(stdin/stdout)或标准错误输出流(stderr)
    返回值:成功则返回关联的 stream 指针

使用方式:

#include <cstdio> //freopen 所在头文件
#include <iostream>

using namespace std;
int main() {
    int x, y;
    freopen("data.in", "r", stdin); //以只读模式打开文件,并重定向stdin
    freopen("data.out", "w", stdout);//以只写模式打开文件,并重定向stdout
    
    //使用 printf/scanf/cin/cout
     cin >> x >> y; //从 data.in 读取数据到 x 和 y
     cout << x +y; //把 x+y 的结果写入 data.out
     fclose(stdin); //关闭输入流
     fclose(stdout); //关闭输出流
     return 0;
}

printf/scanf/cin/cout 等函数默认使用 stdin/stdout,将 stdin/stdout 重定向后,这些函数将输入/输出到被定向的文件。

ifstream/ofstream 文件输入输出流

C++ 提供了 ifstream 和 ofstream 来两个文件流用于进行文件输入输出操作

#include <fstream>
using namespace std;  // 两个类型都在 std 命名空间里

int main() {
    char data[100];
    //以读模式打开文件
    ifstream fin("in.txt");
    //以写模式打开文件
    ofstream fout("out.txt");
    //读取,写入操作类似cin/cout
    fin.getline(data, 100);
    fout << data;
    //关闭流
    fin.close();
    fout.close();
    return 0;
}
posted @   hzyuan  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)

喜欢请打赏

扫描二维码打赏

支付宝打赏

点击右上角即可分享
微信分享提示