ftell、fread、fseek、feof 详细的使用介绍与注意细节

本文详细介绍了C语言中文件操作的相关函数,包括获取文件位置指针、读取文件数据、重定位文件指针及检测文件结束等内容,并通过具体示例说明了这些函数的使用方法及其注意事项。

1. long ftell(FILE *stream);

功能:用于得到文件位置指针当前位置相对于文件首的偏移字节数

2. size_t fread ( void *buffer, size_t size, size_t count, FILE *stream)

功能:从一个文件流中读数据,最多读取count个元素,每个元素size字节,如果调用成功返回实际读取到的元素个数,如果不成功或读到文件末尾返回 0。

3. int fseek(FILE *stream, long offset, int origin)

功能:重定位文件内部位置指针

当offset是向文件尾方向偏移的时候(即为正值),无论加上偏移后是否超出文件尾,fseek都是返回0。
就算超出文件尾,用ftell得到的值仍是加上offset后的值,而不是文件末尾;
此时用fread会失败,返回0。

当offset是向文件头方向偏移的时候(即为负值),如果加上offset后没有超出文件头,是正常偏移,文件指针指向正确的偏移地址,fseek返回值为0;当offset超出文件头时,fseek返回出错-1值;
此时用ftell得到的仍是fseek之前的值,而不是0。

    iseek = fseek(pfRead, 1000, SEEK_CUR);  //0
    itell = ftell(pfRead);    //1000
    iread = fread(&ch, sizeof(char), 1, pfRead);    //0

    iseek = fseek(pfRead, -1001, SEEK_CUR);    //-1
    itell = ftell(pfRead);    //1000
    iread = fread(&ch, sizeof(char), 1, pfRead);    //0

4. int feof(FILE *stream)

功能:检测流上的文件结束符

feof与eof的区别
“C”语言的“feof()”函数和数据库中“eof()”函数的运作是完全不同的。数据库中“eof()”函数读取当前指针的位置,“C”语言的“feof()”函数返回的是最后一次“读操作的内容”。多年来把“位置和内容”相混,从而造成了对这一概念的似是而非。
那么,位置和内容到底有何不同呢?举个简单的例子,比如有人说“你走到火车的最后一节车箱”这就是位置。而如果说“请你一直向后走,摸到铁轨结束”这就是内容。也就是说用内容来判断会“多走一节”。这就是完全依赖于“while(!feof(FP)){…}”进行文件复制时,目标文档总会比源文档“多出一些”的原因。

在stdio.h中可以看到如下定义:

#define EOF (-1)
#define _IOEOF 0x0010
#define feof(_stream) ((_stream)->_flag & _IOEOF)
int c;
while(!feof(fp))
{
    c = fgetc(fp);
    printf("%X\n", c);
}

会发现多输出了一个FF,原因就是在读完最后一个字符后,fp->flag仍然没有被置为_IOEOF,因而feof()仍然没有探测到文件结尾。直到再次调用fgetc()执行读操作,feof()才能探测到文件结尾。这样就多输出了一个-1(即FF)。
正确的写法应该是:

int c;
c = fgetc(fp);
while(!feof(fp))
{
    printf("%X\n", c);
    c = fgetc(fp);//最后一个c的值为-1,但是无妨,因为其他所有的循环操作都要放在此句话上面
}

feof()可以用EOF代替吗?不可以。fgetc返回-1时,有两种情况:读到文件结尾或是读取错误。因此我们无法确信文件已经结束,
因为可能是读取错误! 这时我们需要feof()。

用fseek无论将文件位置指针设置成文件尾还是文件尾之后的值,feof都仍为0

    //文件长度为10
    iseek = fseek(pfRead, 10, SEEK_SET);    //0
    ieof = feof(pfRead);    //0

    iseek = fseek(pfRead, 11, SEEK_SET);    //0
    ieof = feof(pfRead);    //0


一些具体的易错点测试如下:

    unsigned char a;
    FILE *pfRead;

    if (fopen_s(&pfRead, "1.txt", "rb") != 0)   //该文件中共包含10个字符!
    {
        printf("can't open file\n");
        return -1;
    }

    int iseek, iread, itell, ieof;

    iseek = fseek(pfRead, 1000, SEEK_CUR);  //返回值为0,之后的注释都表示返回值
    itell = ftell(pfRead);    //1000
    iread = fread(&a, sizeof(char), 1, pfRead);    //0
    ieof = feof(pfRead);    //0

    iseek = fseek(pfRead, -1001, SEEK_CUR);    //-1
    itell = ftell(pfRead);    //1000
    ieof = feof(pfRead);    //0
    iread = fread(&a, sizeof(char), 1, pfRead);    //0


    iseek = fseek(pfRead, 0, SEEK_SET);    //0
    while (!feof(pfRead))    //最后一个数会多输出一次
    {
        iread = fread(&a, sizeof(char), 1, pfRead);    //前10次为1,最后一次为0
        printf("%c ", a);   
    }
    itell = ftell(pfRead);    //10

    //改成下面这样就不会多输出了
    iseek = fseek(pfRead, 0, SEEK_SET);
    iread = fread(&a, sizeof(char), 1, pfRead);    //1
    while (!feof(pfRead))   
    {
        printf("%c ", a);
        iread = fread(&a, sizeof(char), 1, pfRead);    //前9次为1,最后一次为0
    }
    itell = ftell(pfRead);    //10

    iseek = fseek(pfRead, 10, SEEK_SET);    //0
    ieof = feof(pfRead);    //0
    itell = ftell(pfRead);  //10
    iread = fread(&a, sizeof(char), 1, pfRead); //0
    ieof = feof(pfRead);    //16
    itell = ftell(pfRead);  //10

    iseek = fseek(pfRead, 11, SEEK_SET);    //0
    ieof = feof(pfRead);    //0
    iread = fread(&a, sizeof(char), 1, pfRead);    //0
    ieof = feof(pfRead);    //16

    system("pause");

<think>好的,用户问的是“fread如何使用?”。我需要先回想一下fread函数的基本用法。fread是C语言标准库中的一个函数,用于从文件流中读取数据。它的原型是: size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 参数分别是:指向数据存储的指针,每个数据项的大小,数据项的数量,文件指针。返回值是成功读取的元素个数。 接下来,我需要考虑用户可能的使用场景。用户可能在处理文件读取,比如读取二进制文件或者文本文件的内容。用户可能对C语言有一定了解,但需要具体的使用示例。 需要确保回答覆盖基本用法、参数解释、返回值的意义,以及常见的注意事项,比如二进制模式打开文件、错误处理等。可能还需要给出一个完整的代码示例,这样用户可以直接参考。 用户可能想知道如何正确使用fread来读取不同类型的数据,比如整数数组或者结构体。此外,可能需要注意字节序或者文件是否以二进制模式打开,否则在Windows下可能会有换行符转换的问题。 另外,用户可能关心如何处理读取错误,比如文件是否存在,或者读取过程中是否发生错误。这时候需要提到feof和ferror函数的使用。 还要注意参数中size和nmemb的正确设置,比如读取一个数组时,size是单个元素的大小,nmemb是元素个数。如果用户需要读取整个文件内容,可能需要先获取文件大小,然后分配足够的内存,再使用fread一次性读取。 最后,给出一个完整的示例,比如读取文件内容到缓冲区中,并处理可能的错误情况,这样用户可以直接应用到自己的代码中。 </think>`fread` 是 C 标准库中用于从文件流中读取数据的函数,常用于二进制文件操作。以下是详细用法和示例: --- ### **1. 函数原型** ```c size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); ``` #### **参数说明** - `ptr`:指向存储读取数据的缓冲区的指针。 - `size`:每个数据项的字节大小(如 `sizeof(int)`)。 - `nmemb`:要读取的数据项个数。 - `stream`:文件指针(需通过 `fopen` 打开)。 #### **返回值** - 成功读取的**完整数据项个数**(非字节数),若返回值小于 `nmemb` 可能表示文件结束或错误。 --- ### **2. 基本用法** #### **(1) 读取二进制文件** ```c #include <stdio.h> int main() { FILE *file = fopen("example.bin", "rb"); // 以二进制模式打开文件 if (!file) { perror("文件打开失败"); return 1; } // 假设文件中存储的是整数数组 int buffer[100]; size_t itemsRead = fread(buffer, sizeof(int), 100, file); printf("成功读取 %zu 个整数\n", itemsRead); fclose(file); return 0; } ``` #### **(2) 读取整个文件内容到内存** ```c #include <stdio.h> #include <stdlib.h> int main() { FILE *file = fopen("example.txt", "rb"); if (!file) { perror("文件打开失败"); return 1; } // 获取文件大小 fseek(file, 0, SEEK_END); long fileSize = ftell(file); fseek(file, 0, SEEK_SET); // 分配内存并读取文件 char *buffer = (char *)malloc(fileSize + 1); if (!buffer) { fclose(file); return 1; } size_t bytesRead = fread(buffer, 1, fileSize, file); buffer[bytesRead] = '\0'; // 添加字符串结束符(如果是文本文件) printf("读取内容: %s\n", buffer); free(buffer); fclose(file); return 0; } ``` --- ### **3. 关键注意事项** - **二进制模式**:读取二进制文件时必须使用 `"rb"` 模式,避免换行符自动转换(如 Windows 下 `\r\n` 转 `\n`)。 - **错误检查**:通过 `feof` 和 `ferror` 判断是否到达文件末尾或发生错误: ```c if (feof(file)) printf("到达文件末尾\n"); if (ferror(file)) printf("读取错误\n"); ``` - **返回值处理**:`fread` 返回的是成功读取的**数据项个数**,而非字节数。例如: - 若 `fread(..., 10, 1, ...)` 返回 `1`,表示读取了 10 字节数据。 - 若 `fread(..., 4, 5, ...)` 返回 `5`,表示读取了 20 字节数据。 --- ### **4. 常见用途** - **读取结构体**: ```c typedef struct { int id; char name[20]; } Person; Person p; fread(&p, sizeof(Person), 1, file); ``` - **批量读取数组**: ```c float data[100]; fread(data, sizeof(float), 100, file); ``` --- ### **5. 完整示例:复制文件** ```c #include <stdio.h> #include <stdlib.h> #define BUFFER_SIZE 1024 int main() { FILE *src = fopen("source.bin", "rb"); FILE *dst = fopen("dest.bin", "wb"); if (!src || !dst) { perror("文件打开失败"); return 1; } char buffer[BUFFER_SIZE]; size_t bytesRead; while ((bytesRead = fread(buffer, 1, BUFFER_SIZE, src)) > 0) { fwrite(buffer, 1, bytesRead, dst); // 写入目标文件 } fclose(src); fclose(dst); return 0; } ``` --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值