首页 > 其他分享 >万字带你彻底搞懂 C语言| 文件IO还不懂?看这篇就够了

万字带你彻底搞懂 C语言| 文件IO还不懂?看这篇就够了

时间:2024-12-31 11:54:39浏览次数:3  
标签:文件 return 函数 int C语言 IO 搞懂 include buf

目录

一、前言(必看)

文件访问方式对比

二、系统调用接口

2.1 打开文件:open函数

示例代码一:以只读方式打开test.txt文件,文件不存在则报错

示例代码二:以只写方式打开文件test.txt, 文件不存在则创建,文件存在则清空

示例代码三:验证一个进程(现在可以理解成一个程序)能打开的最大文件数量

2.2 读取文件:read函数

示例代码一:读取test.txt 文件内容,并打印到屏幕上

示例代码二:读取文件的行数 

2.3 写入文件:write函数

示例代码一:以只写模式打开文件,如果不存在则创建,如果存在则清空内容

2.4 重定位:lseek函数

示例代码一:跳过10字节在读文件

2.5 目录操作

2.5.1 更改当前进程工作目录:chdir函数

2.5.2 打开目录:opendir

2.5.3 读取目录:readdir函数

2.5.4 关闭目录:closedir函数

2.5.5 获取文件属性1:stat 函数

2.5.6 获取文件属性2:fstat 函数

2.5.7  获取文件属性3:lstat 函数

三、练习案例:实现文件拷贝 


一、前言(必看)

文件访问方式对比

(1)标准IO与文件IO的区别

  • 标准IO:通过FILE流指针访问文件。

  • 文件IO:通过文件描述符访问文件。

(2)文件描述符

  • 定义:文件描述符是一个非负整数,作为文件的唯一标识符,类似于学号。

  • 分配规则:从0开始,顺序分配

  • 获取方式:当打开或创建文件时,内核会向进程返回一个文件描述符。

(3)进程运行时的默认文件描述符

进程运行时,会自动打开三个文件,分别对应标准输入、标准输出和标准错误输出:

文件类型标准IO(流)文件IO(文件描述符)
标准输入stdin0
标准输出stdout1
标准错误输出stderr2

(4)总结

  • 标准IO使用FILE流指针,文件IO使用文件描述符。

  • 文件描述符从0开始顺序分配,是文件的唯一标识符。

  • 进程默认打开的三个文件分别对应文件描述符0、1、2,分别表示标准输入、标准输出和标准错误输出。

二、系统调用接口

2.1 打开文件:open函数

作用

open 函数用于打开或创建一个文件,并返回文件描述符,以便后续的读写操作。

头文件

#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

函数原型

int open(const char *pathname, int flags);

int open(const char *pathname, int flags, mode_t mode);

参数

  • pathname: 要打开或创建的文件的路径名。

  • flags: 打开文件的标志,用于指定打开方式。常见的标志包括:

    • O_RDONLY:     只读打开

    • O_WRONLY:    只写打开 

    • O_RDWR:        可读写打开

    • O_CREAT:        当文件不存在时创建该文件, 此时需要第三个参数

    • O_TRUNC:       当文件存在时,清空文件内容

    • O_APPEND:     当文件存在时,追加到文件末尾

    • mode: 当使用 O_CREAT 标志时,指定新文件的权限模式。通常使用八进制数表示,如 0644

    • 如果用符号表示,0664 对应的权限为:-rw-rw-r-- (Linux基础)

    • 文件所有者: rw- (读写)

    • 所属组: rw- (读写)

    • 其他用户: r-- (只读)

返回值

  • 成功: 返回文件描述符(一个非负整数)。

  • 失败: 返回 -1,并设置 errno 以指示错误类型。

示例代码一:以只读方式打开test.txt文件,文件不存在则报错

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>  // 包含 close() 函数的头文件

int main(int argc, char *argv[]) 
{
    // 1. 以只读方式打开 test.txt 文件
    int fp = open("test.txt", O_RDONLY);  // O_RDONLY 是只读模式

    // 2. 判断是否打开成功
    if (fp < 0) {
        // 打开失败
        perror("open");  // 打印错误信息
        return -1;       // 返回错误码
    }

    // 3. 打开成功,打印文件描述符编号
    printf("fp = %d\n", fp);  // 打印文件描述符编号

    // 4. 关闭文件描述符
    close(fp);  // 关闭文件描述符,释放资源

    return 0;  // 程序正常结束
}

这里文件描述符 fp = 3的原因是因为前面已经说过,程序运行时,会默认打开三个文件描述符,分别是0,1,2,对应输入、输出、错误,而文件描述符是顺序分配的,所以这里的文件描述符是 3,再创建一个就是 4(没有关闭任何一个文件描述符的前提下)

示例代码二:以只写方式打开文件test.txt, 文件不存在则创建,文件存在则清空

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>  // 包含 close() 函数的头文件

int main() {
    // 1. 以只写方式打开文件,如果文件不存在则创建,如果存在则清空内容
    int fp = open("test1.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664);

    // 2. 判断是否打开成功
    if (fp < 0) {
        perror("open");  // 打印错误信息
        return -1;       // 返回错误码
    }

    // 3. 打开成功,打印文件描述符
    printf("fp = %d\n", fp);

    // 4. 关闭文件
    close(fp);

    return 0;  // 程序正常结束
}

特别注意:当使用 O_CREAT 标志时,open函数的第三个参数就需要填写上了,指定创建文件的权限,一般为0664,| 符号表示,三个都要 ,与的意思。

示例代码三:验证一个进程(现在可以理解成一个程序)能打开的最大文件数量

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main() {

    int file_nums = 3;  // 初始文件数量(默认打开的三个文件:0, 1, 2)
    int fp;    // 文件描述符

    // 1. 不断尝试打开文件
    while (1) {
        fp = open("test.txt", O_RDONLY);  // 以只读方式打开文件

        if (fp < 0) {
            // 打开失败
            perror("open");  // 打印错误信息
            break;           // 退出循环
        }

        // 2. 打开成功
        file_nums++;  // 增加打开的文件数量
    }        
    // 3. 打印当前打开的文件数量
    printf("file nums = %d\n", file_nums);

    return 0;
}

得出结论::一个进程最多同时打开1024个文件

2.2 读取文件:read函数

作用

read 函数用于从文件描述符对应的文件中读取数据。

头文件

#include <unistd.h>

函数原型

ssize_t read(int fd, void *buf, size_t count);

参数

  • fd: 文件描述符,通常由 open 函数返回。

  • buf: 用于存储读取数据的缓冲区地址。

  • count: 要读取的字节数。

返回值

  • 成功: 返回实际读取的字节数。如果返回 0,表示已到达文件末尾(EOF)。

  • 失败: 返回 -1,并设置 errno 以指示错误类型。

示例代码一:读取test.txt 文件内容,并打印到屏幕上

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char *argv[]) 
{
    // 检查命令行参数
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
        return -1;
    }

    // 1. 打开文件
    int fp = open(argv[1], O_RDONLY);  // 以只读方式打开文件
    if (fp < 0) {
        perror("open");  // 打印错误信息
        return -1;
    }
    
    // 2. 读取文件所有内容并显示到屏幕上
    char buf[100];
    while (1) {
        // 每次读之前先清空 buf,防止遗留数据
        memset(buf, 0, sizeof(buf));  // 全部用 \0 填充

        int ret = read(fp, buf, sizeof(buf) - 1);  // 读取数据
        if (ret < 0) {
            // 读取失败
            perror("read");
            break;
        } 
		else if (ret == 0) 
		{
            // 读取到文件末尾了,文件为空也会这样
            printf("read file end\n");
            break;
        }

        // 打印读取的内容
        printf("%s", buf);
    }

    // 3. 关闭文件
    close(fp);

    return 0;
}

为什么是 sizeof(buf)-1 : 因为字符串是以 \0 结尾,代码中定义的缓冲区 buf 的大小为 100 字节。如果使用 sizeof(buf)(即 100)作为 read 的读取长度,read 可能会将缓冲区完全填满,没有空间放置 \0。因此,通过 sizeof(buf) - 1,确保最多只读取 99 个字节,留出最后一个字节用于存储 \0(会自动添加)。 

示例代码二:读取文件的行数 

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[]) 
{
    // 检查命令行参数
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <filename>\n", argv[0]); 
        return -1;
    }

    // 1. 打开文件
    int fp = open(argv[1], O_RDONLY);  // 以只读模式打开文件
    if (fp < 0) {
        perror("open");  // 打印错误信息
        return -1;
    }

    // 2. 逐字符读取文件内容并统计行数
    char ch;
    int ret;
    int line_count = 0;  // 行数计数器
    int empty = 1;  // 标记文件是否为空(1 表示为空)

    while ((ret = read(fp, &ch, 1)) > 0) 
	{
        empty = 0;  // 文件不为空
        if (ch == '\n') {  // 如果遇到换行符,行数加 1
            line_count++;
        }
    }

    // 3. 处理文件末尾可能缺少换行符的情况
    if (empty) {
        printf("文件为空\n");  // 文件为空
    } else if (ch != '\n') {
        line_count++;  // 文件末尾没有换行符,但有内容
    }

    // 4. 关闭文件
    close(fp);

    // 5. 输出结果
    printf("文件的行数: %d\n", line_count);

    return 0;
}

代码稍微复杂一点点,如果看不懂,评论区告诉我

标签:文件,return,函数,int,C语言,IO,搞懂,include,buf
From: https://blog.csdn.net/weixin_67914777/article/details/144833845

相关文章

  • Educational Codeforces Round 165
    EducationalCodeforcesRound165Problem-A-Codeforces答案只会是2或3,分类一下就好了#include<bits/stdc++.h>usingnamespacestd;constintN=2e5+10;intn;inta[N];voidsolve(){ cin>>n; for(inti=1;i<=n;i++) { scanf("%d",&a[i]......
  • c语言 - 如何安全返回局部变量的地址
    c语言返回局部变量的地址在C语言中,返回局部变量的地址是不安全的行为,因为一旦函数执行完毕,局部变量的内存将被释放,返回的地址将指向未定义的内存区域,这将导致不可预知的行为。以下是一个返回局部变量引用的例子,这是错误的做法:#include<stdio.h>int*getVarAddr()......
  • DVWA靶场Command Injection(命令注入) 漏洞low(低),medium(中等),high(高)所有级别通关
    命令注入命令注入漏洞是一种安全漏洞,攻击者可以通过向应用程序输入恶意命令,诱使系统执行这些命令,从而达到未授权访问、数据篡改、系统控制等目的。该漏洞通常出现在应用程序未对用户输入进行充分验证和清理时常见管道符:;前面的执行完执行后面的|上一条命令的输出,作......
  • vue 权限 permission.js
    importstorefrom'@/store'/***字符权限校验*@param{Array}value校验值*@returns{Boolean}*/exportfunctioncheckPermi(value){if(value&&valueinstanceofArray&&value.length>0){constpermissions=store.ge......
  • LLaVA-OneVision: Easy Visual Task Transfer论文阅读笔记
    Motivation&AbsLLaVA-OneVision是一种整合数据、模型和视觉表征的开源多模态模型,首次在单图像、多图像和视频三大计算机视觉场景中实现性能突破。其设计支持跨模态/场景的强迁移学习,尤其通过图像任务迁移展现了强大的视频理解和跨场景能力。MethodNetworkArchitectureLLM......
  • C++项目链接C语言动态库
     有C++项目B,有C语言动态链接库A,需要在B程序中链接A库。 我们知道C++运行环境可以直接运行C语言程序,但因为C++编译时对方法名的解析不同,所以要在C++项目中运行C语言程序,关键问题是需要告诉C++编译器,按照C语言的规范来编译指定的C代码。上面所述的“指定的C代码”,包括C++项目中......
  • [Wi-Fi]QCA9377CT_Security_WPA2Personal_STA_RSNCapabilitiesVerification-OptionalB
     QCA9377 FeaturesList 1x1802.11ac+Bluetooth5inasingleSoCSupportsBluetooth5,BluetoothlowenergyandisbackwardcompatiblewithBluetooth2.xSingleregulated3.3VsupplyoperationIntegratedRFFrontEnd,singleendeddesignOffloadi......
  • iOS 覆盖率检测原理与增量代码测试覆盖率工具实现11
     背景对苹果开发者而言,由于平台审核周期较长,客户端代码导致的线上问题影响时间往往比较久。如果在开发、测试阶段能够提前暴露问题,就有助于避免线上事故的发生。代码覆盖率检测正是帮助开发、测试同学提前发现问题,保证代码质量的好帮手。对于开发者而言,代码覆盖率可以反馈两......
  • iOS 覆盖率检测原理与增量代码测试覆盖率工具实现10
     背景对苹果开发者而言,由于平台审核周期较长,客户端代码导致的线上问题影响时间往往比较久。如果在开发、测试阶段能够提前暴露问题,就有助于避免线上事故的发生。代码覆盖率检测正是帮助开发、测试同学提前发现问题,保证代码质量的好帮手。对于开发者而言,代码覆盖率可以反馈两......
  • Virtualbox - EFI system partition size
    ThesizeofanEFISystemPartition(ESP)typicallydependsontheoperatingsystemandtherequirementsofthesystem,buttherearesomegeneralguidelines:CommonEFIPartitionSizes:MinimumSize:TheEFIspecificationrequiresaminimumsizeof100......