C语言有关文件的操作

news2024/12/28 3:55:29

打开文件与关闭文件

在编写代码时,我有一个习惯是“保证一一对应”。
写下代码fopen()之后,还没有写对文件进行增删查改等操作的代码,先立刻写上fclose(),避免忘记关闭FILE* fd的情况。

不关闭fd,在fopen()次数较少的情况下,系统不会出现错误,但是当打开的fd多了,会造成段错误。这类问题需要反复调用fopen()才会出现错误,如果测试次数不多的话容易忽略该问题,因此建议fopen()fclose()配套使用,避免此类问题。

fopen()打开文件后,还需要需要判断文件指针是否为NULL,这一步是必要的,否则接下来的fclose(NULL)会造成段错误。

文件的打开模式与随机访问

文件的随机访问依赖于两个函数:fseek()ftell()fseek()函数将文件看作数组,可以移动至任意字节处。

文件的打开方式限制了fseek()的功能。假设有文件test.bin,文件的原始内容如下,文件大小为5字节:
文件内容
设计如下程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{   
    // 打开文件
    // 文件原始内容为0x11 0x22 0x33 0x44 0x55,共5字节
    FILE* fd = fopen("/home/qnh/Desktop/film/test.bin", "ab");
    if(NULL == fd)
    {
        printf("FILE[%s] \n LINE[%d] \n FUN[%s]: Open file failed ! \n", __FILE__, __LINE__, __FUNCTION__);
        return 0;
    }

    // 读取文件内容并显示
    fseek(fd, 3, SEEK_SET);
    int offset = ftell(fd);
    printf("offset_after_fseek: %d \n", offset);
    int ch = getc(fd);
    printf("ch: %02x \n", ch);

    offset = ftell(fd);
    printf("offset_after_getc: %d \n", offset);

    // puts()方法向文件中写入数据
    putc(0xAA, fd);
    offset = ftell(fd);
    printf("offset_after_puts: %d \n", offset);

    // fwrite()方法向文件中写入数据
    fseek(fd, 3, SEEK_SET);
    unsigned char data[2] = {0xBB, 0xCC};
    fwrite(data, 1, 2, fd);
    offset = ftell(fd);
    printf("offset_after_fwrite: %d \n", offset);

    fclose(fd);
}

举例来说,以"ab"模式打开文件,并使用fseek()移动文件指针。先进行读操作,此时使用ftell()输出文件的当前位置,可以发现,文件指针是移动至offset的的,但是使用getc()函数,无法读取到文件在该偏移量的内容,读取到的内容是0xFF,这是合理的,因为"ab"模式是写模式,并没有读取文件内容的权限,如果使用"ab+"模式打开文件,就可以读取文件内容了。
程序输出

再测试一下写操作,可以发现,写入的0xAA以及0xBB、0xCC被追加到了文件的末尾,而不是offset处,并且此时ftell()显示文件指针移动到了文件的末尾。说明"a模式"下写入数据的情况,与fseek()函数移动的文件指针无关,数据只能追加到文件的末尾。见参考链接【1】。
写入内容的文件

文件结束符EOF

见参考链接【2】

EOF 是 End Of File 的缩写。在C语言中,它是在标准库中定义的一个宏,在数值上为int类型的-1(0xFFFFFFFF)。
在《C primer plus》的13.2.4 文件结尾章节,有如下描述:

如果getc()函数在读取一个字符时发现是文件结尾,它将返回一个特殊值EOF。所以C程序只有在读到超过文件末尾时才会发现文件的结尾。

这句话书里写的比较混乱,我第一次读产生了歧义。
首先,书中的文件结尾和文件末尾是两个不同的概念,我的理解是,文件末尾是文件的最后一个字节,而文件结尾是一个“哨兵”字符,指向文件末尾的后一个字节,也就是SEEK_END
其次,不要认为EOF是附加在文件结尾处的字符,实际上文件的结尾并不存在一个EOF字符,EOF表示一种状态。getc()函数读到超过文件末尾时,会发现文件已经结束了,此时getc()返回EOF。

书的13.5.1 fseek()和ftell()的工作原理一章,表13.3将SEEK_END和文件末尾混为一谈,与13.2.4 文件结尾超过文件末尾时才会发现文件的结尾产生了矛盾。
鉴于书中这几个章节的混乱,我建议抛弃文件末尾的概念,只留“文件结尾 = SEEK_END”这个概念,概念解释图如下:

概念图

getc()读完整个文件,此时满足(ch == EOF)时,文件指针指向了SEEK_END处,此时想要读取文件的最后一个字节,需要从SEEK_END处回退一个字节。

fseek(fd, -1, SEEK_END);

当文件指针指向SEEK_END时,ftell()表示文件开始处到SEEK_END的字节数,也可以认为是从文件开始处移动到SEEK_END所需要的次数,也可以认为是文件的大小。

获取文件内容

在工程中经常需要逐字节获取文件内容。

如果使用while()循环+判断EOF的方法,getchargetcfgets的返回值需要使用int类型变量接收,如果使用char类型变量接收,可能会导致错误。

int main()
{   
    FILE* fd = fopen("/home/qnh/Desktop/film/test.bin", "rb+");
    if(NULL == fd)
    {
        printf("FILE[%s] \n LINE[%d] \n FUN[%s]: Open file failed ! \n", __FILE__, __LINE__, __FUNCTION__);
        return 0;
    }

    int ch = getc(fd);
    while(EOF != ch)
    {
        printf("%02x ", ch);
        ch = getc(fd);
    }

    fclose(fd);
}

使用char类型变量可能产生错误的原因在于,假设getc()读取到的字节为0xFF,则getc()返回值为0x000000FF,赋值给char类型的ch后,ch的值为0xFF,那么(EOF == ch)条件将会成立,而此时文件尚未读到结尾,while()循环会提前退出。见参考链接【3】。

或者先获取文件的大小,再使用for()循环读取文件,这种方法就不需要使用EOF了。
获取文件大小后,循环的次数就确定了,可以使用for()循环获取文件内容,此时可以使用char类型变量接收。
需要注意文件指针的位置,我常用的做法是在使用ftell()前,先将文件指针移动至SEEK_END处,使用ftell()后,再将文件指针移动至SEEK_SET处,从头开始遍历文件。

int main()
{   
    FILE* fd = fopen("/home/qnh/Desktop/film/test.bin", "rb+");
    if(NULL == fd)
    {
        printf("FILE[%s] \n LINE[%d] \n FUN[%s]: Open file failed ! \n", __FILE__, __LINE__, __FUNCTION__);
        return 0;
    }

    fseek(fd, 0, SEEK_END);
    int size = ftell(fd); // 获取文件大小
    fseek(fd, 0, SEEK_SET);
    for(int i = 0; i < size; i++)
    {
        printf("%02x ", getc(fd));
    }

    fclose(fd);
}

参考连接

【1】https://blog.csdn.net/veghlreywg/article/details/103348856
【2】https://blog.csdn.net/chenaibo/article/details/6062773
【3】https://blog.csdn.net/fengyuruhui/article/details/1682495

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/842590.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux初始命令

如果没有ip地址&#xff0c;配置&#xff1a; 查看当前时间&#xff1a; 指定格式查看时间&#xff1a; 修改时间&#xff1a; 查看时区&#xff1a; 设置时区&#xff1a; 查看当前工作目录&#xff1a; root的家目录就是根&#xff0c;普通用户家目录是home

迈瑞BeneVision N17/N15/N12协议解析

迈瑞BeneVision N17/N15/N12协议解析

pdf 怎么转换成word 文档?这几种方法不容错过

pdf 怎么转换成word 文档&#xff1f;PDF 和 Word 都是日常工作和学习中常见的文档格式&#xff0c;但是它们拥有不同的特点。PDF 可以保持文档格式的一致性&#xff0c;并且不易修改&#xff0c;而 Word 则更加灵活&#xff0c;可以随意编辑和修改。因此&#xff0c;将 PDF 转…

春秋云镜 CVE-2020-26042

春秋云镜 CVE-2020-26042 Hoosk CMS v1.8.0 存在sql注入漏洞 靶标介绍 Hoosk CMS v1.8.0 install/index.php 存在sql注入漏洞。 启动场景 漏洞利用 SQL注入POC POST /install/index.php HTTP/1.1 Host: xxxx User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; r…

无涯教程-Perl - chdir函数

描述 此功能将当前工作目录更改为EXPR,如果未指定,则更改为用户的主目录。此函数调用等效于Unix命令 cd EXPR 。 语法 以下是此函数的简单语法- chdir EXPRchdir返回值 如果失败,此函数返回0,如果成功,则返回1。 例 以下是显示其基本用法的示例代码,假设您在/user/home/…

【stm32】初识stm32—stm32环境的搭建

文章目录 &#x1f6f8;stm32资料分享&#x1f354;stm32是什么&#x1f384;具体过程&#x1f3f3;️‍&#x1f308;安装驱动&#x1f388;1&#x1f388;2 &#x1f3f3;️‍&#x1f308;建立Start文件夹 &#x1f6f8;stm32资料分享 我用夸克网盘分享了「STM32入门教程资料…

Proxmox VE lxc容器中使用samba共享文件夹遇到mount error(1): Operation not permitted 的解决

问题描述&#xff1a; 在PVE的LXC 容器中使用samba成功创建共享并使用&#xff0c; 用smbclient访问共享可以正常连接和使用&#xff0c;但是使用 mount.cifs 或者 mount.smb3时提示权限错误 mount error(1): Operation not permitted Refer to the mount.cifs(8) manual page…

【机密计算-大厂有话说】AMD

基于 VirTEE/SEV 的 SEV-SNP 平台证明 刊号 58217&#xff0c;版本 v1.2&#xff0c;发布于 2023.7 1. 介绍 VirTEE/sev 工具箱提供了一套基于 rust 语言的简单易用的 API 来访问 AMD EPYC 处理器内的安全处理器&#xff0c;这个库已经早已经支持传统的 SEV 固件&#xff0c;最…

代理模式:静态代理+JDK/CGLIB 动态代理

文章目录 1. 代理模式2. 静态代理3. 动态代理3.1. JDK 动态代理机制3.1.1. 介绍 3.1.2. JDK 动态代理类使用步骤3.1.3. 代码示例3.2. CGLIB 动态代理机制3.2.1. 介绍3.2.2. CGLIB 动态代理类使用步骤3.2.3. 代码示例 3.3. JDK 动态代理和 CGLIB 动态代理对比 4. 静态代理和动态…

uniapp 微信小程序 封装公共的请求js(api版本)

一、新建api文件夹 在项目目录下创建api文件夹&#xff0c;内放files跟index.js文件夹&#xff0c;files文件夹内放每个页面对应的js请求接口 1、index.js /*** api接口的统一出口*/ const api {}; const requireComponent require.context(./files, false, /\.js$/) requi…

3.2 防火墙

数据参考&#xff1a;CISP官方 目录 防火墙基础概念防火墙的典型技术防火墙企业部署防火墙的局限性 一、防火墙基础概念 防火墙基础概念&#xff1a; 防火墙&#xff08;Firewall&#xff09;一词来源于早期的欧式建筑&#xff0c;它是建筑物之间的一道矮墙&#xff0c;用…

【基础IO】文件系统 {磁盘的物理结构,存储结构,逻辑结构;CHS 和 LBA 寻址方式;磁盘分区和块组;文件inode;软硬链接}

文件系统 文件分为&#xff1a; 内存文件&#xff1a;被进程打开的文件&#xff0c;文件被加载到内存中供进程快速读写。磁盘文件&#xff1a;没有被打开的文件&#xff0c;保存在磁盘上。磁盘文件被分门别类的存储和管理&#xff0c;用于支持更好的存取。 提示&#xff1a; …

Amazon CodeWhisperer亚马逊云代码生成器idea体验使用

阿丹&#xff1a; 自从接触到微服务以来发现要写的代码越来越多了&#xff0c;之前一直面向ChatGPT来编程&#xff0c;今天找到了一个新的ai代码生成器。体验一下。安装的过程给兄弟们演示一下。 关键还是免费的。 连接如下:AI 代码生成器 - Amazon CodeWhisperer - AWS 查看…

记录第一篇被”华为开发者联盟鸿蒙专区 “收录的文章

记录第一篇被”华为开发者联盟鸿蒙专区 “社区收录的文章。 坚持写作的动力是什么&#xff1f; 是记录、分享&#xff0c;以及更好的思考 。

地理信息系统空间分析实验教程 第三版 第八章示例与练习 寻找最佳路径

寻找最佳路径 背景 随着社会经济的发展&#xff0c;公路的重要性日益提高。在一些交通欠发达的地区&#xff0c;公路 设迫在眉睫。如何根据实际地形情况设计出比较合理的公路&#xff0c;是一个值得研究的问题 目的 通过练习&#xff0c;熟悉 ArcGIS 栅格数据距离制图、表…

Docker搭建zookeeper

问题背景 前言 本文参考自&#xff1a;docker-compose快速搭建Zookeeper集群还有一种更加详细更加全面的部署方式&#xff1a;Docker之docker-compose一键部署Zookeeper集群&#xff0c;但笔者还未验证&#xff0c;先记录下来 搭建 安装docker-ce 此处不赘述 安装docker-co…

skywalking日志收集

文章目录 一、介绍二、添加依赖三、修改日志配置1. 添加链路表示traceId2. 添加链路上下文3. 异步日志 四、收集链路日志 一、介绍 在上一篇文章skywalking全链路追踪中我们介绍了在微服务项目中使用skywalking进行服务调用链路的追踪。 本文在全链路追踪的基础上&#xff0c…

小研究 - 基于 MySQL 数据库的数据安全应用设计(一)

信息系统工程领域对数据安全的要求比较高&#xff0c;MySQL 数据库管理系统普遍应用于各种信息系统应用软件的开发之中&#xff0c;而角色与权限设计不仅关乎数据库中数据保密性的性能高低&#xff0c;也关系到用户使用数据库的最低要求。在对数据库的安全性进行设计时&#xf…

vscode连接远程Linux服务器

文章目录 一、环境安装1.1 下载vscode1.2 下载vscode-sever 二、ssh链接2.1 安装Remote-SSH2.2 设置vscode ssh2.3 设置免密登录2.3.1 本地生成公私钥2.3.2 服务器端添加公钥 三、安装插件3.1 vscode安装插件3.1.1 在线安装插件3.1.2.1 下载插件3.1.2.2 安装插件 3.2 vscode-se…

SQL注入实操三(SQLilabs Less41-65)

文章目录 一、sqli-labs靶场1.轮子模式总结2.Less-41 stacked Query Intiger type blinda.注入点判断b.轮子测试c.获取数据库名称d.堆叠注入e.堆叠注入外带注入获取表名f.堆叠注入外带注入获取列名g.堆叠注入外带注入获取表内数据 3.Less-42 Stacked Query error baseda.注入点…