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

C语言编程笔记:文件处理的艺术

大家好,这里是小编的博客频道
小编的博客:就爱学编程

很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!!

本文目录

  • 引言
  • 正文
    • 一、为什么要用文件
    • 二、文件的分类
    • 三、文件指针
    • 四、文件的打开与关闭
      • 1. 文件的打开
      • 2. 文件的关闭
    • 五、文件缓冲区
    • 六、文件的基本操作
      • (1)打开文件
      • (2)关闭文件
      • (3)检测文件末尾和错误
      • (4)清除文件错误标志
    • 七、顺序读写文件
      • (1)字符级操作函数
        • fgetc()
        • fputc()
      • (2)字符串级操作函数
        • fgets()
        • fputs()
      • (3)格式化输入输出函数
        • fprintf()
        • fscanf()
    • 八、随机读写文件
        • (1)文件指针与定位函数
        • 示例代码
          • 示例1:基本随机读写
          • 示例2:使用`ftell`获取当前位置
          • 示例3:二进制文件的随机读写
    • 九、读取错误检查
        • (1)错误检查函数
        • (2)错误处理策略
        • (3)示例代码
          • 示例1:使用`ferror`检查读取错误
          • 示例2:使用`feof`检查文件末尾
    • 十、总结
  • 快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞和收藏支持一下,在此非常感谢!!!

引言

在C语言编程中,文件操作是数据处理的重要部分。通过文件,我们可以实现数据的持久化存储和共享。下面重点通过多个例子讲清文件的打开和关闭这两个函数。那现在宝子们就跟着小编的步伐一起进入本章知识的学习。Go!Go!Go!

在这里插入图片描述


那接下来就让我们开始遨游在知识的海洋!

正文


一、为什么要用文件

这是因为:

  • 在程序执行过程中,有时需要将数据保存下来供以后使用,或者从外部获取数据进行处理。这时,我们就需要使用到文件。文件是一种用于存储数据的媒介,它可以是磁盘上的物理文件,也可以是内存中的虚拟文件。通过使用文件,我们可以方便地实现数据的长期保存和跨程序的数据共享

二、文件的分类

在C语言中,文件通常按照其内容和用途进行分类,主要包括文本文件二进制文件两种类型:

  1. 文本文件以ASCII码或Unicode码形式存储的文件,主要用于存储人类可读的字符信息,如源代码、文档等。
  1. 二进制文件以二进制形式存储的文件,主要用于存储机器可直接读取和处理的数据,如图像、音频、视频等多媒体文件。

三、文件指针

在C语言中,文件操作是通过文件指针来实现的。 文件指针是一个指向FILE结构体的指针,该结构体包含了文件的各种信息,如文件名、文件位置指示器、文件结束标志等。通过文件指针,我们可以对文件进行各种操作,如读写、定位等。


四、文件的打开与关闭

1. 文件的打开

在C语言中,打开文件需要使用fopen()函数。该函数原型如下:

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

其中,filename参数是要打开的文件名(包括路径),mode参数指定了文件的打开模式。常见的打开模式有:

  • "r":只读模式。如果文件不存在,则打开失败;如果文件存在,则只能从中读取数据。
  • "w":写模式。如果文件不存在,则以指定的文件名创建新文件;如果文件已存在,则删除原有内容,从头开始写入新数据。
  • "a":追加模式。如果文件不存在,则以指定的文件名创建新文件;如果文件已存在,则在文件末尾追加新数据。
  • "rb""wb""ab":分别表示以二进制方式打开文件,进行读、写、追加操作。

以下是一些使用fopen()函数打开文件的示例:

示例1:以只读模式打开文本文件

#include <stdio.h>

int main() {
    FILE *fp;
    fp = fopen("text.txt", "r"); // 打开名为"text.txt"的文本文件,以只读模式
    if (fp == NULL) {
        printf("无法打开文件
");
        return 1;
    } else {
        printf("文件成功打开
");
        // 在此处可以进行文件读取等操作
        fclose(fp); // 关闭文件
    }
    return 0;
}

示例2:以写模式打开文件

#include <stdio.h>

int main() {
    FILE *fp;
    fp = fopen("output.txt", "w"); // 打开名为"output.txt"的文件,以写模式
    if (fp == NULL) {
        printf("无法打开文件
");
        return 1;
    } else {
        fprintf(fp, "Hello, World!
"); // 向文件中写入字符串
        fclose(fp); // 关闭文件
    }
    return 0;
}

示例3:以追加模式打开文件

#include <stdio.h>

int main() {
    FILE *fp;
    fp = fopen("append.txt", "a"); // 打开名为"append.txt"的文件,以追加模式
    if (fp == NULL) {
        printf("无法打开文件
");
        return 1;
    } else {
        fprintf(fp, "This is a new line.
"); // 向文件末尾追加字符串
        fclose(fp); // 关闭文件
    }
    return 0;
}

示例4:以二进制方式打开文件

#include <stdio.h>

int main() {
    FILE *fp;
    fp = fopen("binaryfile.bin", "rb"); // 打开名为"binaryfile.bin"的文件,以二进制方式读取
    if (fp == NULL) {
        printf("无法打开文件
");
        return 1;
    } else {
        // 在此处可以进行二进制文件的读取等操作
        fclose(fp); // 关闭文件
    }
    return 0;
}

2. 文件的关闭

在完成文件操作后,我们需要使用fclose()函数来关闭文件。该函数原型如下:

int fclose(FILE *stream);

其中,stream参数是一个指向要关闭的文件的指针。fclose()函数会关闭文件,并释放与该文件相关的资源。如果关闭文件时发生错误,则返回非零值(通常为EOF);否则返回0。

以下是一个使用fclose()函数关闭文件的示例:

示例5:关闭文件

#include <stdio.h>

int main() {
    FILE *fp;
    fp = fopen("example.txt", "r"); // 打开一个文件
    if (fp != NULL) {
        // 在此处可以进行文件读取等操作
        if (fclose(fp) != 0) { // 关闭文件,并检查是否成功
            perror("Error closing file"); // 如果关闭失败,打印错误信息
        }
    } else {
        printf("无法打开文件
");
    }
    return 0;
}

需要注意的是:

  • 在使用完文件后一定要及时关闭它,以避免资源泄露和潜在的文件损坏问题。同时,如果在打开文件时使用了fopen()函数并且成功打开了文件,那么在关闭文件时一定要确保传递给fclose()函数的指针是与fopen()返回的指针相同的。

五、文件缓冲区

为了提高文件操作的效率,C语言引入了文件缓冲区的概念。当向文件写入数据时,数据首先被写入到缓冲区中,而不是直接写入到磁盘上。只有当缓冲区满或者显式地调用刷新函数(如fflush())时,缓冲区中的数据才会被写入到磁盘上。同样地,当从文件读取数据时,也是先从磁盘上将数据读取到缓冲区中,然后再从缓冲区中读取数据给程序使用。

这种缓冲区机制可以减少磁盘I/O操作的次数,从而提高文件操作的效率。但是,在某些情况下(如需要立即将数据写入磁盘以确保数据安全时),我们可能需要手动刷新缓冲区或者使用无缓冲的文件操作方式。

综上所述:

  • C语言的文件操作涉及多个方面,包括文件的打开与关闭、读写操作、文件指针的使用以及文件缓冲区的处理等。通过掌握这些基础知识,我们可以更加高效地进行文件操作和数据处理工作。

六、文件的基本操作

(1)打开文件

使用fopen函数可以打开一个文件。函数的原型如下:

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

其中,filename是文件名(包含路径),mode是文件的打开模式(如读、写等)。

常见的模式有:

"r" : 只读方式打开文件,该文件必须存在。

"w" : 写入方式打开文件,若文件存在则长度被截为0,即该文件内容会消失;若文件不存在则创建新文件。

"a" : 以附加的方式打开文件,写入的数据会被添加到文件尾,即使使用了 fseek 之类的函数也不会改变。

"r+" : 可读写的方式打开文件,该文件必须存在。

"w+" : 可读写方式打开文件,若文件存在则文件长度被截为0,即该文件内容会消失;若文件不存在则创建新文件。

"a+" : 以可读写的方式打开文件,写入的数据会被添加到文件尾。

(2)关闭文件

使用 fclose 函数可以关闭一个已打开的文件。函数的原型如下:

int fclose(FILE *stream);

(3)检测文件末尾和错误

feof(FILE *stream): 检测是否到达文件末尾

ferror(FILE *stream): 检测是否发生读写错误

(4)清除文件错误标志

使用 clearerr 函数可以清除文件错误标志和文件结束标志。函数的原型如下:

void clearerr(FILE *stream);


顺序读写是指按照文件中数据的存储顺序依次进行读写操作

七、顺序读写文件

在C语言中,文件的顺序读写涉及多个函数,这些函数允许我们按照文件中的存储顺序来读取或写入数据。以下是顺序读写相关的函数详细介绍及代码示例:

(1)字符级操作函数

fgetc()

功能:

  • 从指定的文件流中读取下一个字符(一个无符号字符),并将其作为int类型的值返回。如果到达文件末尾(EOF)或发生错误,则返回EOF

原型: int fgetc(FILE *stream);

示例:

#include <stdio.h>
int main() {
   FILE *pf = fopen("text.txt", "r"); // 打开已经创建好的文件
   if (pf == NULL) {
       perror("fopen");
       return 1;
   }
   // 读文件
   int ret = fgetc(pf);
   printf("%c


", ret);
ret = fgetc(pf);
printf("%c
", ret);
ret = fgetc(pf);
printf("%c
", ret);
// 关闭文件
fclose(pf);
pf = NULL;
return 0;
}

fputc()

功能:

  • 将一个字符写入到指定的文件流中。

原型:int fputc(int character, FILE *stream);

示例:

#include <stdio.h>
int main() {
    FILE *pf = fopen("text.txt", "w"); // 以写模式打开文件
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
    // 写文件
    fputc('a', pf);
    fputc('b', pf);
    fputc('c', pf);
    // 关闭文件
    fclose(pf);
    pf = NULL;
    return 0;
}
 

(2)字符串级操作函数

fgets()

功能:

  • 从指定的文件流中读取一行文本(包括换行符,如果有的话,但最多读取到数组大小减一的位置),并将其存储在字符串中。如果成功,它会返回一个指向该字符串的指针;如果发生错误或到达文件末尾(EOF)而没有读取任何字符,则返回NULL

原型: char *fgets(char *string, int n, FILE *stream);

示例:

#include <stdio.h>
int main() {
    char arr[10] = {0};
    FILE *pf = fopen("text.txt", "r"); // 以读模式打开文件
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
    // 读文件
    fgets(arr, sizeof(arr), pf);
    printf("%s
 

", arr);
// 关闭文件
fclose(pf);
pf = NULL;
return 0;
}

fputs()

功能:

  • 将字符串写入到指定的文件流中,但不包括空字符’\0’。如果成功,它返回非负值;如果发生错误,则返回EOF。

原型: int fputs(const char *string, FILE *stream);

示例:

#include <stdio.h>
int main() {
    FILE *pf = fopen("text.txt", "w"); // 以写模式打开文件
    if (pf == NULL) {
        perror("fopen");
        return 1;
    }
    // 写文件
    fputs("Hello, World!
 

", pf);
// 关闭文件
fclose(pf);
pf = NULL;
return 0;
}

(3)格式化输入输出函数

fprintf()

功能:

  • 向指定的输出流(如文件)写入格式化的数据。

原型: int fprintf(FILE *stream, const char *format[, argument]...);

示例:

#include <stdio.h>
struct S {
   char arr[10];
   int num;
   float sc;
};
int main() {
   struct S s = {"abcde", 10, 5.5f};
   FILE *pf = fopen("text.txt", "w"); // 以写模式打开文件
   if (pf == NULL) {
       perror("fopen");
       return 1;
   }
   // 写文件
   fprintf(pf, "%s %d %f", s.arr, s.num, s.sc);
   // 关闭文件
   fclose(pf);
   pf = NULL;
   return 0;
}
fscanf()

功能:

  • 从指定的输入流(如文件)中按照指定格式读取数据。

原型: int fscanf(FILE *stream, const char *format[, argument]...);

示例:

#include <stdio.h>
struct S {
   char arr[10];
   int num;
   float sc;
};
int main() {
   struct S s = {0};
   FILE *pf = fopen("text.txt", "r"); // 以读模式打开文件
   if (pf == NULL) {
       perror("fopen");
       return 1;
   }
   // 读文件
   fscanf(pf, "%s %d %f", s.arr, &(s.num), &(s.sc));
   // 打印
   printf("%s %d %f


", s.arr, s.num, s.sc);
// 关闭文件
fclose(pf);
pf = NULL;
return 0;
}

在C语言中,文件的随机读写是指能够在文件的任意位置进行读取或写入数据,而不仅仅是顺序地从头到尾处理文件。这种能力在处理大型数据文件时尤为重要,因为它允许程序高效地访问和修改文件中的特定部分。

八、随机读写文件

(1)文件指针与定位函数

要实现文件的随机读写,首先需要了解几个关键概念:

  1. 文件指针:用于标识打开的文件及其在存储设备上的当前位置。

  2. 定位函数:如fseek()ftell()rewind()等,用于移动文件指针到指定位置或获取当前位置。

  • fseek(FILE *stream, long offset, int whence):将文件指针移动到相对于某个位置的偏移量处。
  • whence可以是SEEK_SET(文件开头)、SEEK_CUR(当前位置)或SEEK_END(文件末尾)。
  • ftell(FILE *stream):返回当前文件指针的位置(相对于文件开头的字节数)。
  • rewind(FILE *stream):将文件指针重新定位到文件的开头。
示例代码

以下是一些具体的例子,展示了如何在C语言中进行文件的随机读写操作。

示例1:基本随机读写
#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *file = fopen("example.txt", "w+");
    if (!file) {
        perror("Failed to open file");
        return EXIT_FAILURE;
    }

    // 写入一些初始数据
    fprintf(file, "Hello, World!
This is a test.
");

    // 移动到文件的第7个字节处(索引从0开始)
    fseek(file, 6, SEEK_SET);
    // 在此处写入新字符
    fputc('C', file);

    // 重置文件指针到文件开头
    rewind(file);

    // 读取并打印文件内容
    char buffer[100];
    while (fgets(buffer, sizeof(buffer), file)) {
        printf("%s", buffer);
    }

    fclose(file);
    return EXIT_SUCCESS;
}

在这个例子中,我们打开了一个名为example.txt的文件,写入了初始数据,然后将文件指针移动到第7个字节处(即'H''e'之间),并将该位置的字符替换为'C'。最后,重置文件指针并打印整个文件的内容。

示例2:使用ftell获取当前位置
#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *file = fopen("numbers.txt", "w+");
    if (!file) {
        perror("Failed to open file");
        return EXIT_FAILURE;
    }

    // 写入一系列数字
    for (int i = 0; i < 10; ++i) {
        fprintf(file, "%d
", i);
    }

    // 移动到文件的第5行(实际上是第4个换行符之后)
    fseek(file, 4 * (sizeof(int) + 1), SEEK_SET); // 假设每个数字和换行符占用固定大小的空间

    // 获取当前文件指针位置
    long position = ftell(file);
    printf("Current file pointer position: %ld
", position);

    // 从当前位置继续写入
    fprintf(file, "Inserted Number
");

    // 重置文件指针并打印文件内容
    rewind(file);
    char buffer[100];
    while (fgets(buffer, sizeof(buffer), file)) {
        printf("%s", buffer);
    }

    fclose(file);
    return EXIT_SUCCESS;
}

这个例子中,我们创建了一个包含数字的文件,然后尝试在第5行的位置插入一个新字符串。注意,这里对文件指针的移动是基于假设的固定大小的数字和换行符,实际应用中可能需要更精确的计算或使用其他方法来确定正确的偏移量。

示例3:二进制文件的随机读写
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int id;
    float value;
} Record;

int main() {
    FILE *file = fopen("records.bin", "wb+");
    if (!file) {
        perror("Failed to open file");
        return EXIT_FAILURE;
    }

    // 写入一些记录
    Record records[] = {{1, 1.1}, {2, 2.2}, {3, 3.3}};
    fwrite(records, sizeof(Record), 3, file);

    // 移动到第二条记录的位置(索引从0开始)
    fseek(file, sizeof(Record), SEEK_SET);

    // 读取并修改第二条记录
    Record temp;
    fread(&temp, sizeof(Record), 1, file);
    temp.value += 10.0;

    // 将修改后的记录写回原位置(覆盖旧记录)
    fseek(file, -sizeof(Record), SEEK_CUR); // 或者直接使用之前的ftell结果
    fwrite(&temp, sizeof(Record), 1, file);

    // 重置文件指针并验证内容(这里仅为了演示,实际应关闭文件后重新打开读取)
    rewind(file);
    Record readRecords[3];
    fread(readRecords, sizeof(Record), 3, file);

    for (int i = 0; i < 3; ++i) {
        printf("ID: %d, Value: %.2f
", readRecords[i].id, readRecords[i].value);
    }

    fclose(file);
    return EXIT_SUCCESS;
}
  • 在这个例子中,我们处理一个二进制文件,其中存储了结构体数组。我们展示了如何移动到特定的记录位置,读取它,进行修改,然后再将其写回到文件中。注意,由于我们在同一文件流中进行了读取和写入操作而没有关闭再重新打开文件,这里的验证步骤在实际应用中可能不准确;通常,你应该在修改完文件后关闭它,并在需要时以只读模式重新打开它以验证更改。

在C语言中,对文件进行读写操作时,进行错误检查是至关重要的。这不仅可以确保数据的完整性,还能提高程序的健壮性和可靠性。本文将详细介绍C语言文件操作中如何进行读取错误检查,并通过多个例子加以说明。

九、读取错误检查

(1)错误检查函数

C语言提供了几个函数来检查文件操作的错误状态:

  1. ferror:用于检查文件流上的最后一个I/O操作是否出错。其原型为int ferror(FILE *stream);。如果返回非零值,则表示发生了错误。
  1. perror:用于打印描述最近一次错误的字符串。其原型为void perror(const char *s);。其中,s是一个用户提供的错误信息前缀,通常用于指示发生错误的上下文。
  1. feof:用于检查是否已经到达文件末尾。其原型为int feof(FILE *stream);。如果返回非零值,则表示已经到达文件末尾。
(2)错误处理策略

在进行文件操作时,应该遵循以下错误处理策略:

  1. 在每次调用文件操作函数后,立即检查其返回值或错误状态。
  1. 如果发现错误,根据具体情况采取适当的措施,如打印错误信息、清理资源并退出程序等。
  1. 使用clearerr函数清除文件流的错误标志和文件结束标志,以便进行下一轮的文件操作。
(3)示例代码

以下是几个使用上述函数进行文件读取错误检查的示例代码:

示例1:使用ferror检查读取错误
#include <stdio.h>

int main() {
    FILE *fp = fopen("sample.txt", "r");
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }

    char buffer[100];
    size_t bytesRead = fread(buffer, sizeof(char), sizeof(buffer) - 1, fp);
    if (ferror(fp)) {
        perror("Error reading file");
        fclose(fp);
        return 1;
    }

    // 确保缓冲区以null字符结尾
    buffer[bytesRead] = '\0';
    printf("Read from file: %s
", buffer);

    fclose(fp);
    return 0;
}

在这个例子中,我们尝试从一个名为sample.txt的文件中读取数据。如果在读取过程中发生错误,我们使用ferror函数进行检查,并使用perror函数打印错误信息。

示例2:使用feof检查文件末尾
#include <stdio.h>

int main() {
    FILE *fp = fopen("numbers.txt", "r");
    if (fp == NULL) {
        perror("Failed to open file");
        return 1;
    }

    int number;
    while (!feof(fp)) {
        if (fscanf(fp, "%d", &number) != 1) {
            // 检查是否是因为到达文件末尾而失败
            if (!feof(fp) && ferror(fp)) {
                perror("Error reading number from file");
                fclose(fp);
                return 1;
            }
            // 如果是文件末尾,则跳出循环
            break;
        }
        printf("Read number: %d
", number);
    }

    fclose(fp);
    return 0;
}

在这个例子中,我们尝试从一个名为numbers.txt的文件中读取整数。我们使用feof函数来检查是否已经到达文件末尾,同时使用ferror函数来检查是否发生了其他读取错误。


十、总结

顺序读写按照文件中数据的存储顺序依次进行读写操作,常用函数有 fprintf , fscanf , fgets , fputs等。

随机读写可以在文件中的任意位置进行读写操作,常用函数有fseek , ftell , rewind

错误检查在进行文件操作时,应始终检查返回值并使用 feof ferror 来检测是否到达文件末尾或发生错误。


快乐的时光总是短暂,咱们下篇博文再见啦!!!不要忘了,给小编点点赞和收藏支持一下,在此非常感谢!!!


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

相关文章:

  • Navicat 17 功能简介 | 商业智能 BI
  • Linux使用SSH连接GitHub指南
  • 爬虫第二篇
  • 5. 推荐算法的最基础和最直观的认识
  • CV与NLP经典大模型解读
  • riscv架构下linux4.15实现early打印
  • [Qt] Box Model | 控件样式 | 实现log_in界面
  • JavaScript 中的 `find` 方法
  • Ardupilot开源无人机之Geek SDK进展2024-2025
  • LabVIEW 蔬菜精密播种监测系统
  • 数据分析的新利器-微软开源的GraphRAG
  • 搭建Hadoop源代码阅读环境
  • vscode项目依赖问题
  • 网络编程 | UDP广播通信
  • 渗透测试之越权漏洞详解 水平越权 垂直越权 目录越权 SQL跨库查询越权 以及未授权漏洞 一篇文章说明白
  • 2024春秋杯密码题第一、二天WP
  • HTML学习笔记(5)
  • Java日志配置
  • 蓝桥杯 连续奇数和问题解析
  • 支持向量机算法(三):非线性支持向量原理层层拆解,精读公式每一处细节
  • JavaScript 代码规范
  • MarsCode青训营打卡Day5(2025年1月18日)|稀土掘金-148.小A的子数组权值、304.计算特定条件下的四元组数量
  • 1.6 从 GPT-1 到 GPT-3.5:一路的风云变幻
  • 蓝桥杯算法日常|枚举[*找到最多的数]
  • ASP.NET Core 中的 JWT 鉴权实现
  • recat与vue相比有什么优缺点