C语言:文件操作

为什么使用文件

在之前的通讯录程序中,我们介绍了如何使用文件来实现数据的持久化。然而,我们只是创建了储存功能,但只限于程序运行时。在程序退出以后,数据就不存在了。为了解决这个问题,我们通常会将数据存储到磁盘文件或数据库中。

使用文件我们可以将数据存放到电脑硬盘上,做到数据持久化

什么是文件

文件是指在磁盘或其他存储媒体上存储数据的一种数据结构。在程序设计中有两种文件:程序文件和数据文件。

程序文件

包括源程序(后缀位.c),目标文件(后缀为.obj),可执行文件(后缀为.exe)。

数据文件

文件的内容不一定是程序,而是程序运行时读写的数据。

文件名

文件名包括了三个部分:文件路径+文件名主干+文件后缀

eg:c:\code\test.txt#

文件的打开和关闭

文件指针

缓冲文件系统中关键概念是文件类型指针 ,简称文件指针。

每次被使用的文件都在内存中开辟了一个相应的文件信息区,用来存放文件的相关信息(如文件名,文件状态和文件的当前位置),这些信息保存在一个结构体变量中,该结构体变量由系统声明,取名:FILE。

在不同的编译环境下,FILE包含的内容大同小异。

每打开一个文件,系统会根据文件情况自动创建一个FILE,并填充其中信息

对一个程序员来说,一般通过FILE的指针来维护FILE结构变量。

文件的打开和关闭

文件在读写前应先打开文件,结束后应关闭文件。

在编写程序时,打开文件时会返回一个FILE的指针指向改文件,建立了指针与文件的关系。
ANIC规定用
fopen 打开文件 fclose*关闭文件

fopen

FILE * fopen(const charfilename,const char mode)

filename:这是一个字符串,包含了您想要打开的文件的路径。这个路径可以是相对路径或绝对路径。

相对路径:相对路径(Relative Path)是相对于当前工作目录(或者当前文件的位置)的路径。它不包含从根目录(如 Unix 系统中的 /,Windows 系统中的 C:\)开始的全部信息,而是从当前目录开始到目标文件或目录的路径。

例如,如果您的当前工作目录是 /home/user/documents,那么相对路径 subfolder/file.txt 指的是 /home/user/documents/subfolder/file.txt。
绝对路径 :绝对路径(Absolute Path)是一个完整的文件系统路径,它从文件系统的根目录开始,指定了文件或目录在文件系统中的确切位置。无论当前工作目录在哪里,绝对路径都能正确地定位到目标文件或目录。
mode:fopen 函数可以使用多种模式来打开文件。下面是所有有效的模式字符串列表:

"r": 打开文件用于读取。如果文件不存在,fopen 返回 NULL。

"w": 打开文件用于写入。如果文件存在,则清空内容;如果文件不存在,则创建新文件。

"a": 打开文件用于追加。如果文件存在,数据将被追加到文件末尾;如果文件不存在,则创建新文件。

"r+": 打开文件用于读写。如果文件不存在,fopen 返回 NULL。

"w+": 打开文件用于读写。如果文件存在,则清空内容;如果文件不存在,则创建新文件。

"a+": 打开文件用于读取和追加。如果文件存在,数据将被追加到文件末尾;如果文件不存在,则创建新文件。

"rb": 打开二进制文件用于读取。

"wb": 打开二进制文件用于写入。

"ab": 打开二进制文件用于追加。

"rb+": 打开二进制文件用于读写。

"wb+": 打开二进制文件用于读写。

"ab+": 打开二进制文件用于读取和追加。

此外,还可以在模式字符串中包含以下特殊字符:

"t": 文本模式(默认值)。

"b": 二进制模式。

"+": 同时进行读写操作。

eg:

FILE* pf=fopen("data.txt","r");

尝试打开名为 data.txt 的文件,以便进行读取操作。如果文件存在且打开成功,fopen 函数将返回一个指向 FILE 对象的指针,该指针可以用来 subsequent I/O 操作。如果文件不存在或者由于其他原因无法打开,fopen 函数将返回 NULL。

fclose

int fclose(FILE *stream);
参数说明:

stream: 这是一个 FILE 类型的指针,它指向之前通过 fopen 函数打开的文件。
函数返回值:

如果关闭成功,fclose 函数返回 0。

如果发生错误,fclose 函数返回 EOF(-1 的宏定义)。
功能说明:

当您完成文件的读写操作后,应该使用 fclose 函数来关闭文件。这不仅会释放文件描述符和相关资源,还会确保所有的缓冲区内容都被刷新到文件中,从而确保数据的持久保存。

文件的顺序读写

函数名称 作用 原型 返回值 说明
fgetc 从文件中读取一个字符 int fgetc(FILE *stream); 读取的字符或 EOF 每次调用读取文件的一个字符
fputc 将一个字符写入文件 int fputc(int c, FILE *stream); 写入的字符或 EOF 每次调用写入文件的一个字符
fgets 从文件中读取一行文本 char *fgets(char *str, int n, FILE *stream); 读取的文本或 NULL 每次调用读取文件的一行文本
fputs 将一行文本写入文件 int fputs(const char *str, FILE *stream); 写入的字节数或 EOF 每次调用写入文件的一行文本
fscanf 读取文件并转换成变量 int fscanf(FILE *stream, const char *format, ...); 读取并转换的输入项数 根据格式字符串读取文件并转换成变量
fprintf 将变量格式化后写入文件 int fprintf(FILE *stream, const char *format, ...); 写入的字节数 根据格式字符串将变量写入文件
fread 从文件中读取数据 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 读取的字节数或 EOF 每次调用读取指定数量的字节
fwrite 将数据写入文件 size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); 写入的字节数 每次调用写入指定数量的字节
eg 1: 使用 fgetc 读取文件中的一个字符
c 复制代码
#include <stdio.h>
int main() {
    FILE *fp;
    char ch;
    fp = fopen("data.txt", "r"); // 打开文件用于读取
    if (fp == NULL) {
        perror("Error opening file");
        return -1;
    }
    ch = fgetc(fp); // 读取文件中的第一个字符
    printf("Read character: %c\n", ch);
    fclose(fp); // 关闭文件
    return 0;
}

eg 2: 使用 fgets 读取文件中的一行文本

c 复制代码
#include <stdio.h>
int main() {
    FILE *fp;
    char buffer[100];
    fp = fopen("data.txt", "r"); // 打开文件用于读取
    if (fp == NULL) {
        perror("Error opening file");
        return -1;
    }
    fgets(buffer, 100, fp); // 读取文件中的一行文本
    printf("Read line: %s\n", buffer);
    fclose(fp); // 关闭文件
    return 0;
}

eg 3: 使用 fwrite 写入数据到文件

c 复制代码
#include <stdio.h>
int main() {
    FILE *fp;
    int data[] = {1, 2, 3, 4, 5};
    size_t bytes_written;
    fp = fopen("data.bin", "wb"); // 打开文件用于写入
    if (fp == NULL) {
        perror("Error opening file");
        return -1;
    }
    bytes_written = fwrite(data, sizeof(int), 5, fp); // 写入数据到文件
    printf("Written bytes: %zu\n", bytes_written);
    fclose(fp); // 关闭文件
    return 0;
}

每个例子都包含了打开文件、执行读写操作和关闭文件的步骤。这些例子展示了如何在 C 语言中使用这些基本的文件操作函数。

文件的随机读写

文件的随机读写是指可以读取或写入文件中的任意位置的数据,而不必按照文件的顺序进行。随机读写函数允许你直接跳转到文件中的特定位置,读取或写入一个或多个字节。以下是一些常用的文件随机读写函数。

在 C 语言中,文件的随机读写是指可以读取或写入文件中的任意位置的数据,而不必按照文件的顺序进行。随机读写函数允许你直接跳转到文件中的特定位置,读取或写入一个或多个字节。以下是一些常用的文件随机读写函数:

文件随机读取函数

  1. fseek

    c 复制代码
    int fseek(FILE *stream, long int offset, int whence);

    fseek 函数用于设置文件的读/写位置。stream 参数是指向 FILE 对象的指针,offset 是一个长整型数,表示从 whence 指定的位置开始的偏移量。whence 参数可以是 SEEK_SET(文件开头)、SEEK_CUR(当前位置)或 SEEK_END(文件末尾)。函数返回 0 表示成功,或者返回 -1 表示失败。

  2. ftell

    c 复制代码
    long int ftell(FILE *stream);

    ftell 函数用于获取当前文件的读/写位置。函数返回当前位置相对于文件开头的偏移量,如果出错,返回 -1

  3. fgetc(用于随机读取)

    c 复制代码
    int fgetc(FILE *stream);

    fgetc 函数用于读取文件的一个字符。为了实现随机读取,你首先需要使用 fseek 函数将文件指针移动到想要读取的位置。

文件随机写入函数

  1. fputc(用于随机写入)

    c 复制代码
    int fputc(int c, FILE *stream);

    fputc 函数用于写入文件的一个字符。为了实现随机写入,你首先需要使用 fseek 函数将文件指针移动到想要写入的位置。

  2. fwrite

    c 复制代码
    size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

    fwrite 函数用于向文件中写入数据。为了实现随机写入,你首先需要使用 fseek 函数将文件指针移动到想要写入的位置。

示例代码

以下是一个简单的示例,展示了如何使用 fseekfwrite 函数进行文件的随机写入:

c 复制代码
#include <stdio.h>
int main() {
    FILE *fp;
    int data[] = {1, 2, 3, 4, 5};
    size_t bytes_written;
    fp = fopen("data.bin", "w+b"); // 打开文件用于写入和读取
    if (fp == NULL) {
        perror("Error opening file");
        return -1;
    }
    // 将文件指针移动到文件末尾
    fseek(fp, 0, SEEK_END);
    // 写入数据到文件末尾
    bytes_written = fwrite(data, sizeof(int), 5, fp);
    printf("Written bytes: %zu\n", bytes_written);
    // 关闭文件
    fclose(fp);
    return 0;
}

在上面的示例中,我们使用了 fseek 函数将文件指针移动到文件末尾,然后使用 fwrite 函数向文件末尾写入数据。这样可以实现文件的随机写入操作。

rewind 函数

用于将文件指针重新定位到文件的开始位置。这个函数非常有用,当你想要从头开始重新读取一个文件时,而不想从头开始逐字节移动文件指针。以下是 rewind 函数的原型:

c 复制代码
void rewind(FILE *stream);

rewind 函数接受一个指向 FILE 对象的指针 stream,表示要重置的文件流。调用 rewind 函数后,文件指针将移动到文件的开头,就像文件刚刚被打开一样。

下面是一个使用 rewind 函数的示例:

c 复制代码
#include <stdio.h>
int main() {
    FILE *fp;
    int ch;
    fp = fopen("data.txt", "r"); // 打开文件用于读取
    if (fp == NULL) {
        perror("Error opening file");
        return -1;
    }
    // 读取文件的一个字符
    ch = fgetc(fp);
    printf("Read character: %c\n", ch);
    // 将文件指针重新定位到文件开始位置
    rewind(fp);
    // 重新读取文件的一个字符
    ch = fgetc(fp);
    printf("Rewinded and read character: %c\n", ch);
    // 关闭文件
    fclose(fp);
    return 0;
}

在上述示例中,我们首先读取了文件 data.txt 的一个字符,然后调用了 rewind 函数将文件指针重新定位到文件的开头。之后,我们再次读取了文件的一个字符。由于 rewind 函数的作用,第二次读取的是文件开头的同一个字符。
rewind 函数通常用于数据处理任务,比如当你需要对文件进行多次读取操作,并且每次都需要从文件开始处读取时,使用 rewind 函数可以简化代码,避免手动移动文件指针的复杂性。

文件读取结束的判定

在 C 语言中,文件操作是编程中的一个常见任务。当我们读取文件时,我们需要一种方法来确定何时我们已经到达文件的末尾。这里,我们要讨论的是如何使用 feof 函数来帮助我们判断文件是否已经读取完毕。

首先,我们需要明确一点:feof 函数并不直接告诉我们文件是否结束,而是告诉我们文件指针是否已经到达文件末尾。这意味着,即使 feof 返回 1,文件也可能还有数据,只是文件指针已经超出了数据区域。因此,我们不能仅仅依赖 feof 的返回值来判断文件是否结束。

正确的方法是,在每次读取操作之后,检查读取函数的返回值。例如,当我们使用 fgetcfgets 读取文件时,如果文件已经读取完毕,fgetc 会返回 EOF(End Of File),而 fgets 会返回一个空字符串(\0)。这样,我们就可以确定文件是否已经读取完毕。

下面是一个示例,展示了如何正确使用 fgetcfeof

c 复制代码
#include <stdio.h>
int main() {
    FILE *fp;
    int ch;
    fp = fopen("data.txt", "r"); // 打开文件用于读取
    if (fp == NULL) {
        perror("Error opening file");
        return -1;
    }
    // 逐字符读取文件
    while ((ch = fgetc(fp)) != EOF) {
        printf("Read character: %c\n", ch);
    }
    // 检查是否到达文件末尾
    if (feof(fp)) {
        printf("Reached end of file due to EOF.\n");
    } else {
        printf("Did not reach end of file.\n");
    }
    // 关闭文件
    fclose(fp);
    return 0;
}

在这个示例中,我们使用 while 循环逐字符读取文件。每次 fgetc 函数读取一个字符后,我们检查返回值是否为 EOF。如果是,我们使用 feof 函数来确认是否已经到达文件末尾。如果 feof 返回 1,我们输出 "Reached end of file due to EOF.";如果返回 0,我们输出 "Did not reach end of file."。

这样,我们就通过正确的逻辑来判断文件是否读取结束,而不是直接依赖 feof 的返回值。这种方法可以确保我们在文件末尾正确地处理数据,而不会错误地认为文件中还有未读取的数据。

相关推荐
魔道不误砍柴功4 分钟前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
NiNg_1_2345 分钟前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
闲晨7 分钟前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
2401_8504108330 分钟前
文件系统和日志管理
linux·运维·服务器
Chrikk2 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*2 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue2 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man2 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
测开小菜鸟2 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
一只哒布刘2 小时前
NFS服务器
运维·服务器