Logo

郎哥编程

C语言文件流详解

2024-05-17 32

解读文件流

C语言对文件的写入和读取涉及到流的概念,写入为输出流,读取为输入流。如何理解流的概念呢?可以把流看成流动的自来水,打开水龙头,自来水就会通过自来水管从水源流到用户家中,同样的道理,水库中的水也会通过管道流入到水源。从水源流出到用户住家为自来水的输出流,从水库流入到水源为自来水的输入流,只有这样,自来水才能源源不断地输送到用户家中。

如果把水源看成文件,用户住家为读取文件的函数,水库为写入文件的函数,就很容易理解C语言的输入与输出流了。当C语言的写入函数(水库)需要将数据写入到文件(水源)时,需要建立一条从写入函数(水库)到文件(水源)的通道,这个通道就是输入流;当C语言的读取函数(用户住家)需要读取文件(水源)时,也需要建立一条从文件(水源)到读取函数(用户住家)的通道,这个通道就是输出流。

C语言的读写函数在读写文件时,并不会直接把数据写入到文件,或直接读取到程序接收文件数据的变量,它会建立一个文件缓冲区用来存放读写的数据。当进行文件读取时,读取函数先打开数据流,将磁盘上的文件信息拷贝到缓冲区内,然后程序再从缓冲区中读取所需数据。当写入函数将数据写入文件时,并不会马上写入磁盘中,而是先写入缓冲区,只有在缓冲区已满或“关闭文件”时,才会将数据写入磁盘文件。

在C语言中,文件流是通过FILE结构体来表示的。FILE结构体在C标准库中定义,包含了文件操作的所有信息,如文件指针、缓冲区等。程序通过文件指针来访问和操作文件流。

将文本数据写入文件

文本数据写入文本文件的流程:调用fopen()函数打开或创建一个新的文件,调用fputc()函数或fputs()函数进行单字符写入或文本行写入,最后调用fclose()函数关闭文件。

函数声明

int fputc ( int character, FILE * stream );

函数将字符character写入到stream指向的文件缓冲区。若写入成功返回写入的字符,否则返回EOF。

函数声明

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

函数将str字符串写入到stream指向的文件缓冲区,字符串的结束符不会写入到文件缓冲区。若写入成功返回非负值,否则返回EOF。

函数声明

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

函数将格式化字符串 format写入到stream指向的文件缓冲区,如果format包含格式说明符(以%开头的子序列),则格式化format后面的附加参数,并将其插入结果字符串中,替换其各自的说明符。若写入成功,返回写入的字符数,否则返回负值。

下面的示例程序展示了如何将文本数据写入文件。

#include <stdio.h>
int main() {
    // 定义文件名
    const char *filename = "example.txt";
	// 文本数据
    const char *text = "Hello, World! This is an example text.";
    // 打开文件,准备写入。如果文件不存在,则创建它。
    // "w" 模式表示写入模式,会覆盖文件中的任何现有内容。
    FILE *file = fopen(filename, "w");
    if (file == NULL) {
        // 如果文件无法打开或创建,打印错误消息并退出程序
        perror("Error opening file");
        return 1;
    }
    fputs(text, file);
    // 关闭文件
    fclose(file);
    // 打印成功消息
    printf("Text written to file successfully.\n");
    return 0;
}

上述程序使用fputs()函数将指针变量text指向的文本数据写入example.txt文件,程序会在当前目录下创建一个名为 example.txt 的文件(如果该文件已经存在,其内容将被覆盖)。

注意:程序在写入文件时,需要有写入文件的权限,并且文件路径是有效的。如果文件打开或写入失败,fopen 函数将返回 NULL,程序将打印一个错误消息并退出。

读取文本文件内容到缓冲区

C语言提供了多个C函数用于读取文本文件,其中最常用的是fscanf、fgets和fread。

fscanf函数

fscanf函数用于从文件中读取格式化输入。函数原型:

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

stream 通过fopen函数返回的文件指针。

format 控制字符串,指定了读取数据的格式。

... 表示格式字符串中指定的额外参数。

 

例如,如果有一个包含整数的文本文件,可以使用fscanf来读取这些整数:

int number;
FILE *file = fopen("numbers.txt", "r");
if (file != NULL) {
    while (fscanf(file,"%d",&number) != EOF) {
        printf("%d\n", number);
    }
    fclose(file);
}

上述例子打开一个名为 "example.txt" 的文本文件,并使用 fscanf 从文件中读取一个十进制整数,将其存储到 number 变量中。如果读取成功,打印出读取到的整数;如果读取失败,打印一个错误消息。

fgets函数

fgets函数用于从文件中读取一行文本。函数原型:

char *fgets(char *str, int n, FILE *stream);

str 是一个字符数组,用于存储读取的文本行。

n 是要读取的最大字符数(包括空字符)。

stream 通过fopen函数返回的文件指针。

例如,可以使用fgets来逐行读取文本文件的内容:

char line[100];
FILE *file = fopen("example.txt", "r");
if (file != NULL) {
    while (fgets(line, sizeof(line), file) != NULL) {
        printf("%s", line);
    }
    fclose(file);
}

上述例子打开一个名为 "example.txt" 的文本文件,并使用 fgets函数 从文件中逐行读取文本内容,将其存储到字符数组line内。如果读取成功,打印出读取到的内容。

文件随机访问

在C语言中,fseek和ftell函数用于文件随机访问,它们可以操作文件指针,实现对文件任意位置的读写操作。rewind函数将文件内部的位置指针重新指向开始。

fseek函数

fseek是C语言中的一个标准库函数,用于移动文件的位置指针到指定的位置,函数定义在 <stdio.h> 头文件中。fseek 允许程序在读取或写入文件时,不按照顺序从头到尾或从尾到头进行,而是可以直接跳转到文件的任意位置进行操作。

函数原型:

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

参数:

stream:指向 FILE结构体的指针标识一个打开的文件流。

offset:表示偏移量的长整数。它表示从 whence 指定的位置开始移动的字节数。

whence:决定了 offset 的起始位置。它有三个可能的值:

SEEK_SET:从文件开始位置计算偏移量。

SEEK_CUR:从当前文件位置计算偏移量。

SEEK_END:从文件末尾计算偏移量。

返回值:

如果函数执行成功,fseek 返回零。如果发生错误,它会返回非零值。

ftell函数

C语言中的ftell函数用于确定文件流中的当前读写位置,函数定义在<stdio.h>头文件中,主要用于处理文件操作。

函数原型:

long ftell(FILE *stream);

ftell函数接受一个指向FILE结构体的指针,函数返回一个long类型的值,表示从文件开头到当前读写指针位置的字节偏移量。如果发生错误,函数将返回-1。

ftell函数配合fseek函数可以获取文件的大小;在读取或写入大文件时,可以通过ftell函数来监控文件的读写进度;fseek函数用于设置文件流中的读写位置,ftell可以用于验证fseek是否设置成功。

rewind函数

C语言中的rewind函数将文件流中的位置指针指向流的开始,函数定义在<stdio.h>头文件中。

函数原型:

void rewind(FILE *stream);

rewind函数作用等同于fseek(stream, 0L, SEEK_SET)。

读写二进制文件

C语言提供了fread函数从文件读取二进制数据,fwrite函数将二进制数据写入到文件。

fread函数

该函数从stream输入流读取count个元素,每个元素的大小为size个字节,并存储到ptr指向的内存中。若读取成功,返回读取的元素总个数,若返回小于count的数值,则表示在读取时发生读取错误或到达文件末尾,在这种情况下,可以使用可以分别用ferror和feof进行检查。

函数原型

size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );

size_t是标准C库中使用typedef关键字定义的基础数据类型的别名,在64位系统中为long long unsigned int,非64位系统中为long unsigned int。

函数原型

int ferror ( FILE * stream );

函数检查是否发生了与stream关联的错误,如果有关联错误则返回不同于零的错误值,否则返回零值。

函数原型

int feof ( FILE * stream );

函数检查是否发生了与stream关联的文件结束指示符,如果设置了,则返回一个不同于零的值,否则返回零值。

下面的示例程序演示了如何使用fread函数读取一个图片文件(例如,一个PNG文件),并将其内容存储到一个缓冲区中。

#include <stdio.h>
#include <stdlib.h>
int main() {
    FILE *file;
    char *filename = "example.png"; // 图片文件名
    long filesize; // 文件大小
    void *buffer; // 用于存储文件内容的缓冲区
    // 打开文件
    file = fopen(filename, "rb");
    if (file == NULL) {
        perror("Error opening file");
        return EXIT_FAILURE;
    }
// 移动到文件末尾以获取文件大小
    fseek(file, 0, SEEK_END);
filesize = ftell(file);
// 重置文件指针到文件开头
    rewind(file); 
    // 分配足够的内存来存储文件内容
    buffer = malloc(filesize);
    if (buffer == NULL) {
        perror("Memory allocation failed");
        fclose(file);
        return EXIT_FAILURE;
    }
    // 使用fread读取文件内容到缓冲区
    if (fread(buffer, 1, filesize, file) != filesize) {
        perror("Error reading file");
        free(buffer);
        fclose(file);
        return EXIT_FAILURE;
    }
    // 读取完成,关闭文件
    fclose(file);
  // 释放buffer占用的内存
    free(buffer);
    return EXIT_SUCCESS;
}

在上述示例中使用fopen函数以二进制模式("rb")打开图片文件,然后使用fseek和ftell函数来确定文件的大小,接着分配一个足够大的缓冲区来存储整个文件的内容,并使用fread函数将其读取到缓冲区中,最后关闭文件并释放缓冲区的内存。

fwrite函数

fwrite函数用于向stream指向的文件流,写入size*count字节数据,参数ptr指向待写入的二进制数据。

函数原型

size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);

参数:

ptr:指向某内存空间的指针,该内存空间中储存有待写入文件的数据块;参数ptr类型为void*型,说明ptr可以指向任何数据类型;

size:指定了每个待写入文件的数据项的字节大小,类型为size_t(unsigned int);

count:指定了待写入文件的数据项的个数,类型为size_t(unsigned int)型;

stream:指向FILE类型结构的指针。

返回值:

fwrite函数返回一个size_t类型的值,表示实际写入的数据元素数量。如果返回值小于count,则表示发生了错误。

 

下面的示例程序将一个整数数组写入到一个二进制文件中。

#include <stdio.h>
int main() {
    FILE *fp;
    int data[] = {1, 2, 3, 4, 5};
    int count = sizeof(data) / sizeof(data[0]);
    fp = fopen("data.bin", "wb");  // 以二进制写模式打开文件
    if (fp == NULL) {
        perror("Error opening file");
        return 1;
    }
    size_t elements_written = fwrite(data, sizeof(int), count, fp);
    if (elements_written < count) {
        perror("Error writing to file");
        fclose(fp);
        return 1;
    }
    fclose(fp);
    return 0;
}

fwrite函数写入的是二进制数据,不是文本数据。因此,在读取这些数据时,应该使用fread函数,并且注意数据的类型和对齐方式。

如果写入的数据类型与读取时的数据类型不匹配,或者数据的对齐方式不正确,可能会导致读取的数据与原始数据不一致。

fwrite函数不会检查文件流是否已经打开或是否已经到达文件末尾,所以在调用fwrite之前,最好先检查文件流是否有效。

代码在线纠错(通义千问 qwen-max)

支持粘贴多个代码文件,提交后由阿里云通义千问自动分析代码漏洞、语法错误、逻辑问题并给出修改建议。
您已解锁 AI 代码纠错功能,可正常使用!

评论区

登录 后发表评论
暂无评论