【hello Linux】基础IO

news2024/11/18 14:40:14

目录

1.C语言文件操作

1. 打开文件:

2. 文件操作

3. 关闭文件

2. C语言中的流操作

3. 系统文件IO

1. 接口介绍

2. 写文件

3. 读文件

4. 文件描述符fd

0 & 1 & 2 文件描述符: 

文件描述符原理:

文件描述符的分配规则:

5. 重定向

6. 使用dup2系统调用

7. stdout和stderr的区别

stdout 重定向:

如何达到 stdout 和 stderr都重定向?


Linux🌷 

1.C语言文件操作

在进行文件操作前,我们都要先打开对应文件,文件操作后,我们要关闭对应文件。

C语言中进行文件操作要包含头文件:

#include <stdio.h>

1. 打开文件:

FILE *fopen( const char *filename, const char *mode );

 filename  :文件的路径 = 路径 + 文件名;

 mode  :打开方式:"r" 、"w"、"a" 等;

返回值:打开成功则返回一个指向打开文件的指针,打开失败则返回NULL;

打开方式详解: 

 "r"只读方式打开,若文件不存在则打开失败;

 "w"只写方式打开,若文件存在则清空文件,若文件不存在则创建新文件;

  "a"追加方式打开,若文件存在则在文件末尾追加要写入的内容,若文件不存在则创建新文件;

 2. 文件操作

写文件

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

将string的内容写到stream

示例: 

#include <stdio.h>    
    
int main()    
{    
  FILE* fp = fopen("./log.txt","w");  //以写的方式打开文件    
  if(fp == NULL)        //打开文件失败    
  {    
    perror("fopen");    
    return 1;    
  }    
    
  int cnt=10;                         //写文件    
  const char* msg = "hello linux!\n";    
  while(cnt--)    
  {    
    fputs(msg,fp);    
  }    
    
  fclose(fp);         //关闭文件    
    
  return 0;                                                                                                                                                        
}    

结果演示:

读文件

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

将stream执行的内容读到string中,每次最多都=读n个字节;

示例:

  #include <stdio.h>    
      
  int main()    
  {    
    FILE* fp = fopen("./log.txt","r");    
    if(fp==NULL)    
    {    
      perror(fopen);    
      return 1;    
    }    
      
    char buffer[64];    
    while(fgets(buffer, sizeof(buffer),fp))    
    {    
      printf("%s",buffer);    
    }    
      
    if(!feof(fp))    
    {    
      printf("fgets quit not normal!\n");    
    }    
    else    
    {    
      printf("fgets quit normal!\n");    
    }    
      
    fclose(fp);    
    return 0;                                                                                                                                                                                                   
  }    

结果演示:

 追加文件

示例:

  #include <stdio.h>    
      
  int main()    
  {    
    FILE* fp = fopen("./data.txt","a");    
    if(fp==NULL)    
    {    
      perror(fopen);    
      return 1;    
    }    
      
    const char* msg = "hello linux!\n";    
    int cnt = 3;    
    while(cnt--)    
    {    
      fputs(msg,fp);    
    }    
    fclose(fp);    
    return 0;                                                                                                                                                                                                   
  }    

结果演示:

 3. 关闭文件

int fclose( FILE *stream );

stream 为打开文件的文件标识符;


在上述中,我们对文件的操作,最终都是访问硬件的:显示器、键盘、磁盘;

OS作为硬件的管理者,它不相信任何用户,提供了一些系统调用接口供用户使用;

C语言中的fgets、fputs便是在系统调用接口上进行了二次封装所得到的,这些函数调用比系统调用

要易于用户使用;


 2. C语言中的流操作

将内容输出到显示器上,我们能想到什么办法?

我们发现不光使用printf可以达到此功能,使用stdout也可以达到此功能;

在Linux学习中,我们知道一切皆文件,其实对于键盘、显示器等,我们都可以把他们当作文件来

对待,它们有读写函数,对应读写功能,经过驱动层的封装,我们可以像对平常文件文件一样,使

用读写函数对键盘、显示器进行读写操作;

在C语言程序中,会默认打开三个输入输出流:stdin、stdout、stderr

stdin:标准输入流,对应的设备为键盘:从键盘读入字符,将内容保存在键盘对应的文件中;

stdout:标准输出流,对应的设备为显示器:从显示器文件读出数据,自然我们便可以在显示器上

看到字符;

stderr:对应设备为显示器;

经过查看cpluplus我们也发现这三个流的类型都是文件类型:

 当我们的c语言程序运行起来的时候,该进程为bash的子进程,bash默认打开stdin、stdout、

stderr三个流,该进程继承父进程也打开这三个流,我们的printf、scanf等函数才可以向显示器打

印数据、从键盘读取数据。

3. 系统文件IO

上述C语言对文件操作的函数是在系统调用接口上的封装;

下来我们使用系统提供的接口完成上述对文件的读写操作:

1. 接口介绍

open
我们可以使用man手册进行查询:man open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

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

//pathname: 要打开文件的:文件名及路径

//flags: 打开方式
//打开文件时,可以传入多个参数选项,用下面的一个或者多个常量进行“或”运算,构成flags。

//参数:
//    O_RDONLY: 只读打开
//    O_WRONLY: 只写打开
//    O_RDWR : 读,写打开
//这三个常量,必须指定一个且只能指定一个
//    O_CREAT : 若文件不存在,则创建它。需要使用mode选项,来指明新文件的访问权限
//    O_APPEND: 追加写

//mode:权限
//如果文件存在也可以不用此参数;
//若文件不存在便要使用此参数给创建的文件赋予权限,否则文件权限是乱的;

//返回值:
//    成功:新打开的文件描述符
//    失败:-1

上述flags参数的选项是可以进行或运算的,那些选项都是只有一个比特位为1的数据,且不重复;

2. 写文件

写文件:写到哪、从哪写、一次写多少字节; 

#include <stdio.h>    
#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <unistd.h>    
#include <string.h>    
    
int main()    
{    
  int fd = open("myfile.txt", O_WRONLY|O_CREAT, 0644);    
  if(fd<0)      //打开失败    
  {    
    perror("open");    
    return 1;    
  }    
    
  int count = 5;    
  const char* msg = "hello linux!\n";    
  int len = strlen(msg);    
    
  while(count--)    
  {    
    write(fd,msg,len);    
  }    
  close(fd);    
  return 0;                                                                                                                                                        
}    

 3. 读文件

读文件:从哪读、读到哪、一次最多读多少字节;

#include <stdio.h>    
#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <unistd.h>    
#include <string.h>    
    
int main()    
{    
  int fd = open("myfile.txt",O_RDONLY);    
  if(fd<0)    
  {    
    perror("open");    
    return 1;    
  }    
    
  const char* msg = "hello linux!\n";    
  char buf[1024];    
  while(1)    
  {    
    ssize_t s = read(fd,buf,strlen(msg));    
    if(s>0)    
      printf("%s",buf);    
    else    
      break;    
  }    
  close(fd);    
  return 0;                                                                                                                                                        
}    

4. 文件描述符fd

 在我们使用系统调用接口open打开一个文件时,open系统调用接口的返回值便称为文件描述符;

文件描述符的取值:0、1、2、3....正整数;

0 & 1 & 2 文件描述符:

Linux 进程默认情况下会 打开三个文件描述符:分别是标准输入 0 , 标准输出 1 , 标准错误 2;
0,1,2 对应的物理设备一般是:键盘,显示器,显示器
所以输入输出还可以采用如下方式:
  #include <stdio.h>    
  #include <sys/types.h>    
  #include <sys/stat.h>    
  #include <fcntl.h>    
  #include <string.h>    
      
  int main()    
  {    
    char buf[1024];    
    ssize_t s = read(0,buf,1024);    
    if(s>0)    
    {    
      buf[s]='\0';    
      write(1,buf,strlen(buf));    
      write(2,buf,strlen(buf));    
    }    
    return 0;                                                                                                                                                      
  }    

 文件描述符原理:

文件描述符就是从 0 开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构
来描述目标文件。于是就有了fifile 结构体。表示一个已经打开的文件对象。而进程执行 open 系统调
用,所以必须让进程和文件关联起来。每个进程都有一个指针*fifiles, 指向一张表 fifiles_struct, 该表
最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文
件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件

文件描述符的分配规则:

我们通常打开一个文件时,被分配的文件描述符为3,因为0、1、2被占用;

#include <stdio.h>    
#include <sys/types.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
#include <unistd.h>    
#include <string.h>    
    
int main()    
{    
  int fd1 = open("mm1",O_WRONLY|O_CREAT, 0644);    
  int fd2 = open("mm2",O_WRONLY|O_CREAT, 0644);    
  int fd3 = open("mm3",O_WRONLY|O_CREAT, 0644);    
    
  printf("fd1:%d\n",fd1);    
  printf("fd2:%d\n",fd2);    
  printf("fd3:%d\n",fd3);    
    
  close(fd1);    
  close(fd2);    
  close(fd3);                                                                                                                                                      
}    

经过证实:确实是从3开始分配;

#include <stdio.h>    
#include <sys/types.h>    
#include <unistd.h>    
#include <sys/stat.h>    
#include <fcntl.h>    
                                                                                                                                                                   
int main()    
{    
  close(0);    
  int fd = open("aa",O_WRONLY|O_CREAT,0664);    
  printf("fd:%d\n",fd);    
  return 0;    
}  

 我们将0号标识符关闭,发现新创建的文件的文件标识符为0;

总结:
文件描述符的分配规则:在 fifiles_struct 数组当中,找到当前没有被使用的最小的一个下标,作为
新的文件描述符。

5. 重定向

  #include <stdio.h>    
  #include <sys/types.h>    
  #include <sys/stat.h>    
  #include <fcntl.h>    
  #include <stdlib.h>    
      
  int main()    
  {    
    close(1);    
    int fd = open("stdout",O_WRONLY|O_CREAT,0644);    
    if(fd<0)    
    {    
      perror("fopen");    
      return 1;    
    }    
    printf("fd:%d\n",fd);    
    fflush(stdout);                                                                                                                                                
    close(fd);    
    return 0;    
  }    

在上述代码中,我们将1号端口关闭,新创建的文件的文件标识符被赋予1;

1号原本是标准输出的,对应着显示器,现在对应这新创建的文件;

因此原本应该输出到显示器的内容,写入到了文件中;

这便是输出重定向;

 图解表示:

 6. 使用dup2系统调用

使用方法:

#include <unistd.h>

int dup2(int oldfd, int newfd);

将老的文件标识符所指的内容拷贝至新的文件标识符;

也就是说老的文件标识符和新的文件标识符所指的内容都是老的文件标识符所指的内容;

#include <stdio.h>    
#include <unistd.h>    
#include <fcntl.h>    
    
int main()    
{    
  int fd = open("./log", O_CREAT|O_RDWR);    
  if(fd<0)    
  {    
    perror("open");    
    return 1;    
  }    
  close(1);    
  dup2(fd,1);    
    
  int i=0;    
  for(;i<2;i++)                                                                                                                                                                                                 
  {    
    char buf[1024] = {0};    
    ssize_t read_size = read(0, buf, sizeof(buf)-1);    
    if(read_size < 0)    
    {    
       perror("read");    
       break;    
    }    
    printf("%s",buf);    
    fflush(stdout);    
  }    
  return 0;    
}    

上述是一个从标准输入流中读取两行数据,原本输出到标准输出流中,但是进行了dup2系统调

用,使原本输出到标准输出流中的数据,输出到log文件中。

 可见:使用此函数可以完成重定向功能。

7. stdout和stderr的区别

stdout 重定向:

先看一个代码:

#include <stdio.h>    
#include <string.h>    
#include <unistd.h>    
    
int main()    
{    
  const char* msg1 = "hello 标准输出\n";    
  write(1,msg1,strlen(msg1));    
    
  const char* msg2 = "hello 标准错误\n";    
  write(2,msg2,strlen(msg2));    
    
  return 0;                                                                                                                                                           
}

将msg1和msg2分别输出到标准输出流和标准错误流中;

经过查看确实如此;

但当我们将程序执行的结果重定向至log文件中,发现标准错误的还是显示到显示器上,标准输出的才是重定向至log文件中;

这是我们便发现其实 > 是输出重定向,只重定向 stdout 的内容;

 如何达到 stdout 和 stderr都重定向?

此语句的意思是将 1 指向的内容拷贝至 2 ;

具体点来说:

原本 1 指向 stdout ,2 指向 stderr,现在将 1 先指向 log 文件,最后将 1 所指的内容拷贝至 2 ,也就是说 1 和 2 最后都指向 log 文件;

坚持打卡!😃

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

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

相关文章

企业数字化转型如何做?看过来

一、什么是数字化转型&#xff1f; 企业数字化转型旨在以数字化技术为基础&#xff0c;建立一个与物理世界对应的数字世界。在数字化转型过程中&#xff0c;数据是核心&#xff0c;人工智能是手段&#xff0c;云化服务是形式&#xff0c;企业的组织制度、流程优化与重构及人才…

ROS学习——艰辛的环境安装之路一ROS安装Kinetic版本

ROS-Kinetic Kinetic版本的ROS是用在Ubuntu16.04版本下的&#xff0c;先确认Ubuntu版本 Kinetic安装的官方文档&#xff08;可以按照原文来&#xff09; http://wiki.ros.org/kinetic/Installation/Ubuntu 1.配置Ubuntu的软件仓库 在安装 Ubuntu 的过程中如果你系统选了中文…

电子表格 VS 数据网格,你的React应用程序应该怎么选?

电子表格和数据网格&#xff1a;乍一看他们似乎是可互换的组件&#xff0c;由于两者都是用表格显示来格式化大量数据&#xff0c;因此很难知道应该为React应用程序选择哪个选项。 尽管它们的外观相似&#xff0c;但提供了非常不同的功能——选择错误的一个将对用户体验产生负面…

vite面试题

为什么说vite比webpack更快 和webpack对比&#xff0c;为什么 vite 的冷启动、热启动、热更新都会快&#xff1f;这就要说说二者的区别。 使用 webpack 时&#xff0c;从 yarn start 命令启动&#xff0c;到最后页面展示&#xff0c;需要经历的过程&#xff1a; 以 entry 配置…

如何制作订单工序流转报表

使用外部数据分析工具&#xff0c;比如百度Sugar&#xff0c;连接草料二维码官方数据库&#xff0c;即可制作自定义可视化报表。本文会具体介绍订单工序流转报表的制作流程。 一、案例效果 订单工序流转报表可以帮助管理者快速查询各订单的最新进展、是否超期&#xff0c;关注…

SAP Web IDE本地环境搭建

SAP Web IDE本地环境搭建 1、JDK的安装及配置环境变量 可通过官网自行下载JDK&#xff1a; Java Downloads | Oracle 直接傻瓜式安装至默认路径“C:\Program Files\Java\jdk-17.0.2”&#xff0c;然后配置环境变量“JAVA_HOME”、“CLASSPATH”和“PATH”。 JAVA_HOME&…

MySQL的基础学习

文章目录 一、MySQL NULL 值处理二、MySQL 正则表达式三、MySQL 事务四、MySQL ALTER命令总结 一、MySQL NULL 值处理 MySQL 使用 SQL SELECT 命令及 WHERE 子句来读取数据表中的数据,但是当提供的查询条件字段为 NULL 时&#xff0c;该命令可能就无法正常工作。 为了处理这种…

HTTP常用请求头和响应头有哪些?

一、什么是http 超文本传输协议&#xff08; Hypertext Transfer Protocol &#xff0c;HTTP&#xff09;的请求和响应消息中&#xff0c;协议头部分的那些组件。用来准确描述正在获取的资源、服务器或者客户端的行为&#xff0c;定义了HTTP事务中的具体操作参数。 二、什么是…

webGL前端数字孪生技术方案

本篇内容本是为公司内部分享会准备的大纲&#xff0c;发出来给想做webGL的前端做个参考。 课程介绍&#xff1a; 基于webGL技术&#xff0c;实现数字孪生应用。讲解从模型绘制、webGL框架、数据通讯等方案的技术选型。初步了解图形学的基础内容&#xff0c;熟悉webGL的开发流程…

瀚高股份吕新杰:创新开源双驱动 躬耕国产数据库

近年来&#xff0c;国际形势不断变幻&#xff0c;也给人们带来巨大警示&#xff1a;关键核心技术是买不来、讨不来的&#xff0c;中国科技企业需寻找研发自强之路。 瀚高基础软件股份有限公司&#xff08;简称瀚高股份&#xff09;专注数据库十八年&#xff0c;始终以“振兴民…

【技巧分享】Nacos未经授权的登录漏洞-任意密码直接登录后台

文章目录 前言一、nacos简介二、漏洞复现总结 前言 碰到个很有意思的系统&#xff0c;Nacos。通过查找资料&#xff0c;发现Nacos < 2.1.0 版本都存在这个漏洞。 漏洞描述&#xff1a;Nacos中发现影响Nacos < 2.1.0的问题&#xff0c;Nacos用户使用默认JWT密钥导致未授…

好用还免费的10个Figma插件推荐

Figma是一款广受设计师喜爱的工具&#xff0c;可用于创建和协作用户界面、图形和原型。它广泛应用的原因之一是因为有许多插件可供选择&#xff0c;可以增强其功能。在本文中&#xff0c;我们将探讨10个设计师必备的Figma插件。如果你无法使用Figma 可以使用即时设计——即时设…

OpenAI最新官方ChatGPT聊天插件接口《智能聊天插件引言》全网最详细中英文实用指南和教程,助你零基础快速轻松掌握全新技术(一)(附源码)

Chat Plugins Limited Alpha 聊天插件 前言IntroductionPlugin flow 插件流其它资料下载 Learn how to build a plugin that allows ChatGPT to intelligently call your API. 了解如何构建允许ChatGPT智能调用API的插件。 前言 在现代的软件开发环境中&#xff0c;使用第三方…

Doris使用总结

场景 Apache Doris是由百度贡献的开源MPP分析型数据库产品&#xff0c;亚秒级查询响应时间&#xff0c;支持实时数据分析&#xff1b;分布式架构简洁&#xff0c;易于运维&#xff0c;可以支持10PB以上的超大数据集&#xff1b;可以满足多种数据分析需求&#xff0c;例如固定历…

Maven(六):Maven的使用——继承与聚合

Maven&#xff08;六&#xff09;&#xff1a;Maven的使用——继承与聚合 前言一、实验九&#xff1a;继承1、概念2、作用3、举例4、操作4.1 创建父工程4.2 创建模块工程4.3 查看被添加新内容的父工程 pom.xml4.4 解读子工程的pom.xml4.5 在父工程中配置依赖的统一管理4.6 子工…

电脑端(PC)按键精灵——5.找色/找图命令

电脑端(PC)按键精灵——5.找色/找图命令 注&#xff1a;说了键盘、鼠标、其他、控制命令还有安装内容&#xff0c;现在说下颜色/图形命令&#xff0c;这一节相当重要 按键精灵小白入门详细教程&#xff1a; 电脑端(PC)按键精灵—小白入门 详细教程 命令介绍 1.GetPixelCol…

人大金仓携手深信服打造更高效、更融合一体化平台联合解决方案

近日&#xff0c;人大金仓数据库与深信服超融合平台完成深度优化&#xff0c;表现出性能更优、运行更稳定、数据更安全。双方联合打造更高效、更融合的一体化平台联合解决方案&#xff0c;能更好地满足行业信创升级需求。 随着新一轮信创政策陆续出台&#xff0c;信创产业从党政…

基于 SpringBoot + Vue 实现的可视化拖拽编辑的大屏项目

今天给小伙伴们分享一个基于 SpringBoot Vue 实现的可视化拖拽编辑的大屏项目&#xff1b; 一、简介 这个是一个开源的一个BI平台&#xff0c;酷炫大屏展示&#xff0c;能随时随地掌控业务动态&#xff0c;让每个决策都有数据支撑。 多数据源支持&#xff0c;内置mysql、el…

攻防世界-Reversing-x64Elf-100

Reversing-x64Elf-100 18最佳Writeup由 yuchouxuan 提供 收藏 反馈 难度&#xff1a;1 方向&#xff1a;Reverse 题解数&#xff1a;15 解出人数&#xff1a;2460 题目来源: 题目描述: 暂无 note:undefined8 FUN_004006fd(long param_1){int local_2c;char *local_28 …

设置环境变量

文章目录 window设置linux设置python设置 window设置 命令行设置 set 临时设置setx 永久设置 # 打开一个cmd命令行 set # 查看所有环境变量 set FLASK_APPsuperset # 临时设置&#xff0c;当前窗口有效 set FLASK_APP%FLASK_APP%;777 # # 查看 echo %FLASK_APP%# 永久设置…