【C++与Linux基础】文件篇 -语言特性上的文件操作

本系列主要旨在帮助初学者学习和巩固Linux系统。也是笔者自己学习Linux的心得体会。


个人主页: 爱装代码的小瓶子
文章系列: Linux
2. C++


文章目录

  • [1. 前置介绍:](#1. 前置介绍:)
  • 2.理解Linux下的文件
    • [2-1 简单的理解(狭义)](#2-1 简单的理解(狭义))
    • [2-2 深入理解:](#2-2 深入理解:)
  • [3. C语言打开文件的模式:](#3. C语言打开文件的模式:)
  • [4. 文件操作的三巨头:`fprintf`、`fwrite`、`fread`](#4. 文件操作的三巨头:fprintffwritefread)
    • [4-1:`fwrite(数据指针, 块大小, 块数量, fp)`](#4-1:fwrite(数据指针, 块大小, 块数量, fp))
    • [4-2 `fread(数据指针, 块大小, 块数量, fp)`](#4-2 fread(数据指针, 块大小, 块数量, fp))
  • 5.引入系统层面的调用:

1. 前置介绍:

在上两篇文章中,第一篇文章:【C++与Linux 基础】进程篇 -还在怕进程控制?带你用C++手写一个简易Shell,第二篇文章:【C++与Linux基础】进程篇 - 改进Shell,完成内建命令我们已经写好了一个基本的shell,借助这个小实验我们理解了进程和进程之间的切换。利用子程序来执行外部命令。

这里我们要学习的就是Linux系统的文件篇。在这篇文章,你可以认识到:

  1. Linux下,一切都是文件(概念,这里只是讲一下)
  2. C语言中的六种打开模式
  3. C语言的打开文件的操作
  4. 引入系统接口:系统函数打开文件操作

在Linux系统编程中我们已经结束了进程篇,文件篇已经开始启航了。

2.理解Linux下的文件

在我们的日常生活中,我们经常能够看到我们操作windows平台的文件,其实Linux里面也是一样的。

2-1 简单的理解(狭义)

文件存贮在磁盘中,磁盘是永久存贮性容器,他不会因为断电而导致文件消失。因此我们可以引出以下几个点:

  1. 我们对文件的操作就是在磁盘中读取和操作。
  2. 磁盘是外设,对磁盘的输入和输出就是io操作。
  3. 盘上的文件本质是对文件的所有操作,都是对外设的输入和输出简称IO。

2-2 深入理解:

我们在之前中,我们也说过了,Linux下一切皆文件,不仅仅是磁盘里面的文件。比如我们的显示屏还要键盘或者网卡,他们的本质都是文件。

在之前的文章中,我们讲了一个概念:先描述,再组织,这是很重要的概念的,在这里我们也适用:我们也是通过结构体来描述这些文件。最后通过链表组织和运行他们。

当你打开一个空的文件夹的时候,这个文件是否占据内存呢?

答案是显而易见的,他还是占据内存的。尽管再windows平台上显示是0个字节,但是其实占据空间的。这里我们指出 文件 = 属性 + 里面的数据

接下来我们来详细讲解以下C语言风格的打开文件吧:

3. C语言打开文件的模式:

先看C语言提供了几种打开的方式吧,在学习C语言的时候,我们都说有三种大的方向:

  1. r模式:只读模式:如果文件不存在,就无法读取。
  2. w模式:写入模式:如果文件不存在,会创建一个文件。注意在写入的时候会清空之前写的内容。
  3. a模式:追加模式:如果文件不存在,仍然会创建一个文件。与w模式不同的是:他会在文件之后追击写入。

这只是三个最基础的,我们还可以在它的后面加上一个+号,最后一共是三个大方向,六个模式:

带上 + 号的核心意义是:开启"更新"(Update)模式,允许对同一个文件流既进行读取,又进行写入。

3-1来尝试w模式

我们可以来尝试用程序,来打开或者创建程序来看看吧:

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

int main()
{
    FILE* fp = fopen("log.txt","w");
    if(fp == NULL){
        //如果fp指针为空,进入这里
        perror("open error");
        exit(1);
    }   
    char* buff = "hello world";
    fprintf(fp,"%s",buff);//以指定的格式写入文件中                                                                                                                                  
    return 0;
}

我们先看这个文件夹内是没有log,txt的。

开始执行之后,我们再次打开这个文件,我们会发现,文件被创建而且,写入成功:

在这里我们是没有找到,我们没有给这个文件的路径,他是怎么知道的呢?

其实当我们运行的时候,进程里面会保存系统环境变量,其中就又有一个信息就是PWD,我们不给路径,他就默认在这个PWD里面拼接文件名,进行创建。

3-2 尝试只读模式(r)

再上一个文件夹中,我们已经创建并且写入成功了,这次我们尝试打开这个文件并读取里面的内容:

cpp 复制代码
    FILE* fp = fopen("log.txt","r");
    if(fp == NULL){
        perror("open error");
        exit(1);
    }   
    char buff[15];
    fscanf(fp,"%s",buff);
    printf("%s",buff);                                                                                                                                                              
    fclose(fp);
    return 0;

我们将文件中的内容以字符串导入buff之中,但是这里还有一个问题,我们来看运行结果:

我们可以看到,这里我们只打印了一个hello。

这是因为fscanf只能读取一个完整的句子,当他遇到一个空格的时候,他就会结束读取。

这里我们就要换其他的接口来完成这个内容了:

我们可以尝试使用fgets来完成这个内容的提取,同时,为了读取多个句子,我们使用while来完成获取里面的所有的句子

fgets一旦遇到\n就会停止读取,但是为了防止文件有多个句子,我们使用while来循环读取,直到遇到文件结尾:EOF(end of file)就会结束。

可以看到结果,完美的打印了,这一句话,并没有出现上面只打印了一个hello。

4. 文件操作的三巨头:fprintffwritefread

在上面我们已经详细提到了fprintf,这个就是像指定文件中输入,比如可以像标准输出(显示屏)中输入字符串。主要是操作字符的,和fscanf一样。(上面也提到了他的缺点。)

这里我们着重讲:fwritefread

4-1:fwrite(数据指针, 块大小, 块数量, fp)

  • 作用: 直接把内存块"复印"到文件里。
  • 区别: 如果你存数字 100:
  • fprintf 存的是字符 '1', '0', '0' (3个字节)。
  • fwrite 存的是 int 的二进制原码 (4个字节),用记事本打开是乱码。

在这里我们也可以尝试来写一下:

cpp 复制代码
    FILE* fp = fopen("log.txt","w");
    if(fp == NULL){
        perror("open error");
        exit(1);
    }   

    char* msg = "hello world\n";
    int cnt = 10; 
    while(cnt--){
        //将msg的内容传递给buff,传第一次,n是为了安全写入,告诉有多少的内存
        char buff[1024];
        snprintf(buff,sizeof(buff),"%s%d\n",msg,cnt);                                                                                                                               
        fwrite(buff,strlen(buff),1,fp);
    }   
    fclose(fp);
    return 0;

注意这里面fwrite的用法:我们strlen是计算\0之前的内容,我们是不希望\0进入我们的文件中的

这是因为:是给人看的。记事本不需要(也不喜欢)\0,它看到 \0 可能会显示成乱码方块或者这就截断显示了。内存字符串: 必须有 \0,否则 CPU 不知道字符串在哪里结束。

来看看以下结果:

可以看到这样是没有问题的。接下来,简单来写写fread。

4-2 fread(数据指针, 块大小, 块数量, fp)

把磁盘中文件写回内存中,废话不多说,我们来看一个代码:

cpp 复制代码
    FILE* fp = fopen("log.txt","r");
    if(fp == NULL){
        perror("open error");
        exit(1);
    }

    char buff[1024];
    //这里的n指的是本次读取几个字节
    int n = fread(buff,1,sizeof(buff) - 1,fp);
    if(n  > 0)  buff[n] = '\0';
    else buff[0] = '\0';
    printf("%s",buff);
    fclose(fp);

注意这里的fread的格式:必须每次读取一个字节,读取sizeof(buff) -1 个大小,必须强制留个\0一个空间,这样再内存中打印时没有问题的。

5.引入系统层面的调用:

在上文讲了,这些都是语言层面上的调用,不同的语言具有自己的调用方式,JAVA还是C++都有自己的一套函数来完成这个。

我们这里着重介绍了C语言的文件管理。后面,我们回详细讲一下系统层面的调用,还有三个标准输入和输出和错误。

感谢各位对本篇文章的支持。谢谢各位点个三连吧!



相关推荐
端平入洛18 小时前
auto有时不auto
c++
崔小汤呀1 天前
最全的docker安装笔记,包含CentOS和Ubuntu
linux·后端
何中应1 天前
vi编辑器使用
linux·后端·操作系统
何中应1 天前
Linux进程无法被kill
linux·后端·操作系统
何中应1 天前
rm-rf /命令操作介绍
linux·后端·操作系统
何中应1 天前
Linux常用命令
linux·操作系统
葛立国1 天前
从 / 和 /dev 说起:Linux 文件系统与挂载点一文理清
linux
郑州光合科技余经理2 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
feifeigo1232 天前
matlab画图工具
开发语言·matlab
dustcell.2 天前
haproxy七层代理
java·开发语言·前端