Linux文件IO操作
来源:微信公众号「编程学习基地」
文件操作
在进行 Linux 文件操作之前,我们先简单了解一下 Linux 文件系统
Linux文件类型
Linux中文件类型只有以下这几种:
符号 | 文件类型 |
---|---|
- | 普通文件 |
d | 目录文件,d是directory的简写 |
l | 软连接文件,亦称符号链接文件,s是soft或者symbolic的简写 |
b | 块文件,是设备文件的一种(还有另一种),b是block的简写 |
c | 字符文件,也是设备文件的一种(这就是第二种),c是character的文件 |
s | 套接字文件,这种文件类型用于进程间的网络通信 |
p | 管道文件,这种文件类型用于进程间的通信 |
怎么判断文件类型?
$ ls -l
total 8
drwxrwxr-x 2 ubuntu ubuntu 4096 Oct 25 15:30 dir
prw-rw-r-- 1 ubuntu ubuntu 0 Oct 25 15:30 FIFOTEST
-rw-rw-r-- 1 ubuntu ubuntu 2 Oct 25 15:25 main.c
lrwxrwxrwx 1 ubuntu ubuntu 6 Oct 25 15:28 main.c-temp -> main.c
srwxrwxr-x 1 ubuntu ubuntu 0 Oct 25 15:24 test.sock
ls -l 第一个字母代表文件类型
Linux文件权限
文件权限是文件的访问控制权限,那些用户和组群可以访问文件以及可以执行什么操作
查看文件权限
文件类型后面紧跟着的就是文件权限
简单介绍下文件权限,如下图所示:
因为Linux是一个多用户登录的操作系统,所以文件权限跟用户相关。
修改文件权限
1.以二进制的形式修改文件权限
什么是二进制形式?以main.c
的权限为例
-rw-rw-r-- 1 ubuntu ubuntu 2 Oct 25 15:25 main.c
文件的权限为rw-rw-r--
,对应的二进制为664
,如何计算呢,看下表
可读 | 可写 | 可执行 | |
---|---|---|---|
字符表示 | r | w | x |
数字表示 | 4 | 2 | 1 |
所有者的权限为rw-
,对应着4+2+0,也就是最终的权限6,以此类推,用户组的权限为6,其他用户的权限为4.
修改文件权限需要用到chmod
命令,如下所示
$ ls -l
-rw-rw-r-- 1 ubuntu ubuntu 2 Oct 25 15:25 main.c
$ chmod 666 main.c
$ ls -l
-rw-rw-rw- 1 ubuntu ubuntu 2 Oct 25 15:25 main.c
二进制的计算不要算错了
2.以加减赋值的方式修改文件权限
还是用到chmod
命令,直接上手
$ ls -l
-rw-rw-rw- 1 ubuntu ubuntu 2 Oct 25 15:25 main.c
$ chmod o-w main.c
$ ls -l
-rw-rw-r-- 1 ubuntu ubuntu 2 Oct 25 15:25 main.c
文件所有者 user | 文件所属组用户 group | 其他用户 other |
---|---|---|
u | g | o |
+
和-
分别表示增加和去掉相应权限
简单的了解了Linux下的文件操作之后就开始进入代码编程阶段
Linux error
获取系统调用时的错误描述
Linux下的文件操作属于系统调用,Linux中系统调用的错误都存储于 errno
中,例如文件不存在,errno置 2,即宏定义 ENOENT
,对应的错误描述为 No such file or directory
。
打印系统调用时的错误描述需要用到 strerror
,定义如下
#include <string.h>
char *strerror(int errnum);
查看系统中所有的 errno
所代表的含义,可以采用如下的代码:
/* Function: obtain the errno string
* char *strerror(int errno)
*/
#include <stdio.h>
#include <string.h> //for strerror()
//#include <errno.h>
int main()
{
int tmp = 0;
for(tmp = 0; tmp <=256; tmp++)
{
printf("errno: %2d\t%s\n",tmp,strerror(tmp));
}
return 0;
}
可以自己手动运行下,看下输出效果
打印错误信息
之前谈到Linux系统调用的错误都存储于 errno
中,errno
定义如下
#include <errno.h>
int errno;
除了 strerror
可以输出错误描述外,perror
也可以,而且更加方便
打印系统错误消息 perror
,函数原型及头文件定义如下
#include <stdio.h>
void perror(const char *s);
使用示例:
/**
* @brief 文件不存在打开失败时打印错误描述
*/
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h> //strerror
#include <errno.h> //errno
int main() {
int fd = open("test.txt", O_RDWR);
if(fd == -1) {
perror("open");
//printf("open:%s\n",strerror(errno));
}
close(fd);
return 0;
}
当文件 test.txt
不存在时打印如下
./main
open: No such file or directory
系统IO函数
UNIX环境下的C 对二进制流文件的读写有两种体系:
- fopen,fread,fwrite ;
- open, read, write;
fopen 系列是标准的C库函数;open系列是 POSIX 定义的,是UNIX系统里的system call。
文件操作不外乎 open
,close
,read
,write
,lseek
,从打开文件开始介绍
open/close
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:对文件的操作权限设置还有其他的设置
O_RDONLY, O_WRONLY, O_RDWR 这三个设置是互斥的
- mode:八进制数,表示创建出的新的文件的操作权限,例如:0775
close
定义如下
#include <unistd.h>
int close(int fd);
打开文件
通过open
打开一个存在的文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main() {
int fd = open("test.txt", O_RDONLY);// 打开一个文件
if(fd == -1) {
perror("open");
}
// 读写操作
close(fd); // 关闭
return 0;
}
如果文件存在,打开文件;文件不存在,打开失败,错误描述为No such file or directory
。
创建文件
通过open
创建一个新的文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main() {
// 创建一个新的文件
int fd = open("test.txt", O_RDWR | O_CREAT, 0777);
if(fd == -1) {
perror("open");
}
// 关闭
close(fd);
return 0;
}
这里有个东西需要注意一下,先看输出结果
$ ls -l test.txt
-rwxrwxr-x 1 dengzr dengzr 0 Oct 27 19:50 test.txt
创建文件的同时赋予文件权限,在上面的Linux文件权限中已经介绍过了文件权限
创建文件时赋值的权限为777
,但是创建的文件权限为775
,这是我们需要注意的地方。
在linux系统中,我们创建一个新的文件或者目录的时候,这些新的文件或目录都会有默认的访问权限。默认的访问权限通过命令umask
查看。
$ umask
0002
用户创建文件的最终权限为mode & ~umask
。
例如Demo中创建的文件权限mode
= 0777
,所以最终权限为 0775
777 -> 111111111
~002 -> 111111101 &
775 -> 111111101
修改默认访问权限
更改umask值,可以通过命令 umask mode
的方式来更改umask
值,比如我要把umask
值改为000,则使用命令 umask 000 即可。
$ umask
0002
$ umask 000
$ umask
0000
删除test.txt
重新运行程序创建
$ ./create
$ ls -l test.txt
-rwxrwxrwx 1 dengzr dengzr 0 Oct 27 20:06 test.txt
ps:这种方式并不能永久改变umask
值,只是改变了当前会话的umask
值,打开一个新的shell
输入umask
命令,可以看到umask
值仍是默认的002
。要想永久改变umask
值,则可以修改文件/etc/bashrc
,在文件中添加一行 umask 000
。
read/write
文件I/O最基本的两个函数就是read和write,在《unix/linux编程实践教程》也叫做unbuffered I/O。
这里的ubuffered,是指的是针对于read和write本身来说,他们是没有缓存机制(应用层无缓冲)。
read定义如下
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
参数:
- fd:文件描述符
- buf:保存读取数据的缓冲区
- count:读取数据的大小
返回值:
- 成功:
>0: 返回实际的读取到的字节数
=0:文件已经读取完了
- 失败:-1 ,并且设置errno
简单应用一下,示例Demo
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h> //errno
int main() {
int fd = open("test.txt", O_RDWR);
if(fd == -1) {
perror("open");
}
char buff[1024];
memset(buff,'\0',1024);
int readLen = read(fd,buff,1024);
printf("readLen:%d,data:%s",readLen,buff);
close(fd);
return 0;
}
Demo读取 test.txt
里面的数据并显示
$ ./main
readLen:5,data:text
write定义如下
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
参数:
- fd:文件描述符
- buf:待写入数据块
- count:写入数据的大小
返回值:
- 成功:实际写入的字节数
- 失败:返回-1,并设置errno
同样简单应用一下,Demo如下
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h> //errno
int main() {
int fd = open("test.txt", O_WRONLY | O_CREAT ,0775);
if(fd == -1) {
perror("open");
}
char buf[256] = "text";
int Len = write(fd,buf,strlen(buf));
printf("readLen:%d,data:%s\n",Len,buf);
// close(fd);
return 0;
}
在Demo中注释掉close,数据写入成功
$ ./main
readLen:4,data:text
$ cat test.txt
text
对比fwrite等标准的C库函数,write是没有缓冲的,不需要fflush
或者close
将缓冲数据写入到磁盘中但是程序open后一定要close,这是一个良好的编程习惯。
ps:其实write是有缓冲的,在用户看不到的系统层,我们可以理解为没有缓冲
lseek
作用:对文件文件指针进行文件偏移操作
lseek定义如下
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
参数:
- fd:文件描述符
- offset:偏移量
- whence:
SEEK_SET :设置文件指针的偏移量
SEEK_CUR :设置偏移量:当前位置 + 第二个参数offset的值
SEEK_END:设置偏移量:文件大小 + 第二个参数offset的值
返回值:返回文件指针的位置
lseek和标准C库函数fseek没有什么区别,几个作用简单了解一下
1.移动文件指针到文件头
lseek(fd, 0, SEEK_SET);
2.获取当前文件指针的位置
lseek(fd, 0, SEEK_CUR);
3.获取文件长度
lseek(fd, 0, SEEK_END);
示例Demo如下
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main() {
int fd = open("test.txt", O_RDWR);
if(fd == -1) {
perror("open");
return -1;
}
// 获取文件的长度
int len = lseek(fd, 0, SEEK_END);
printf("file len:%d\n",len);
// 关闭文件
close(fd);
return 0;
}
./main
file len:4
linux下的标准输入/输出/错误
在文件IO操作里面一直讲到文件描述符,那我就不得不提一下linux中的标准输入/输出/错误
在C语言的学习过程中我们经常看到的stdin,stdout和stderr,这3个是被称为终端(Terminal)的标准输入(standard input),标准输出( standard out)和标准错误输出(standard error),这对应的是标准C库中的fopen/fwrite/fread。
但是在在Linux下,操作系统一级提供的文件API都是以文件描述符来表示文件,对应的的标准输入,标准输出和标准错误输出是0,1,2,宏定义为STDIN_FILENO、STDOUT_FILENO 、STDERR_FILENO ,这对应系统API接口库中的open/read/write。
标准输入(standard input)
在c语言中表现为调用scanf函数接受用户输入内容,即从终端设备输入内容。也可以用fscanf指明stdin接收内容或者用read接受,对应的标准输入的文件标识符为0或者STDIN_FILENO。
#include<stdio.h>
#include<string.h>
int main()
{
char buf[1024];
//C语言下标准输入
scanf("%s",buf);
//UNIX下标准输入 stdin
fscanf(stdin,"%s",buf);
//操作系统级 STDIN_FILENO
read(0,buf,strlen(buf));
return 0;
}
ps:注意read不可以和stdin搭配使用
标准输出(standard out)
在c语言中表现为调用printf函数将内容输出到终端上。使用fprintf指明stdout也可以把内容输出到终端上或者wirte输出到终端,对应的标准输出的文件标识符为1或者STDOUT_FILENO 。
#include<stdio.h>
#include<string.h>
#include <unistd.h>
int main()
{
char buf[1024];
//C语言下标准输入 输出
scanf("%s",buf);
printf("buf:%s\n",buf);
//UNIX下标准输入 stdin
fscanf(stdin,"%s",buf);
fprintf(stdout,"buf:%s\n",buf);
//操作系统级 STDIN_FILENO
read(STDIN_FILENO,buf,strlen(buf));
write(STDOUT_FILENO,buf,strlen(buf));
return 0;
}
标准错误输出(standard error)
标准错误和标准输出一样都是输出到终端上, 标准C库对应的标准错误为stderr,系统API接口库对应的标准错误输出的文件标识符为2或者STDERR_FILENO。
#include<stdio.h>
#include<string.h>
int main()
{
char buf[1024]="error";
fprintf(stderr,"%s\n",buf);
write(2,buf,strlen(buf));
return 0;
}
既然有标准输出,为什么要有标准错误呢?既生瑜何生亮?
一个简单的Demo让你了解一下,诸葛的牛逼
#include <stdio.h>
int main()
{
fprintf(stdout, "stdout");
fprintf(stderr, "stderr");
return 0;
}
猜一下是先输出stdout还是stderr,按照正常思维是先输出stdout,再输出stderr。
但是stderr属于诸葛流,喜欢抢占先机,所以先输出stderr,再输出stdout。
~咳咳,扯远了,实际上stdout是块设备,stderr不是。对于块设备,只有当下面几种情况下才会被输入:遇到回车;缓冲区满;flush被调用。而stderr因为没有缓冲所以直接输出。
谈一下stdin和STDIN_FILENO区别
以前我一直没搞明白,以为stdin等于0,其实stdin类型为 FILE*;STDIN_FILENO类型为 int,不能相提并论,其次stdin属于标准I/O,高级的输入输出函数,对应的fread、fwrite、fclose等;STDIN_FILENO属于系统API接口库,对应的read,write,close。
上面都是我零碎的知识点总结一下备忘。
Linux文件IO操作的更多相关文章
- linux文件IO操作篇 (一) 非缓冲文件
文件IO操作分为 2 种 非缓冲文件IO 和 缓冲文件IO 它们的接口区别是 非缓冲 open() close() read() write() 缓冲 fopen() fclose() fread() ...
- Linux文件IO操作函数概述
文件概述 Linux中,一切皆文件.文件为操作系统服务和设备提供了一个简单而一致的接口.这意味着程序完全可以像使用文件那样使用磁盘文件.串行口.打印机和其他设备. 也就是说,大多数情况下,你只需要使用 ...
- linux文件IO操作篇 (二) 缓冲文件
2. 缓冲文件操作 //规模较大 实时性低的文件 //当数据长度快要超过缓冲区的范围时,或者时间周期达到时,数据才被送往指定位置 //需要使用FILE * 作为文件标识符 //stdin 标准输入 / ...
- 转:Linux 文件IO理解
源地址http://blog.csdn.net/lonelyrains/article/details/6604851 linux文件IO操作有两套大类的操作方式:不带缓存的文件IO操作,带缓存的文件 ...
- Linux学习记录--文件IO操作相关系统编程
文件IO操作相关系统编程 这里主要说两套IO操作接口,各自是: POSIX标准 read|write接口.函数定义在#include<unistd.h> ISO C标准 fread|fwr ...
- 树莓派学习笔记——使用文件IO操作GPIO SysFs方式
0 前言 本文描写叙述假设通过文件IO sysfs方式控制树莓派 GPIO端口.通过sysfs方式控制GPIO,先訪问/sys/class/gpio文件夹,向export文件写入GPIO编号, ...
- imx6用文件io操作gpio
具体请参考: http://blog.csdn.net/u014213012/article/details/53140781 这里要注意的是: 要让linux支持文件io方式操作gpio,首先驱动必 ...
- 2.Linux文件IO编程
2.1Linux文件IO概述 2.1.0POSIX规范 POSIX:(Portable Operating System Interface)可移植操作系统接口规范. 由IEEE制定,是为了提高UNI ...
- 9.2 Go 文件IO操作
9.2 Go 文件IO操作 1.1.1. bufio包 带有缓冲区的IO读写操作,用于读写文件,以及读取键盘输入 func main() { //NewReader返回一个结构体对象指针 reader ...
随机推荐
- C# 获得当前方法 和 方法调用链 的 方法
一个获得方法名的方法,depth表示调用此方法的回溯深度. 比如,A方法调用B方法,B方法调用GetCurrentMethodFullName(2),那么得到的结果是A方法的全名(namespace+ ...
- Python爬无止境,获得王者荣耀全部高清皮肤
作为一名热爱王者两年的程序员,早就想爬取所有英雄皮肤的高清照片,在设个幻灯片放映,真香,这次只用16行代码就能实现,对于新手拿这个作为实战练手项目既简单又容易上手,快来尝试下. 百度"王者荣 ...
- PHP设计模式之状态模式
状态模式从字面上其实并不是很好理解.这里的状态是什么意思呢?保存状态?那不就是备忘录模式了.其实,这里的状态是类的状态,通过改变类的某个状态,让这个类感觉像是换了一个类一样.说起来有点拗口吧,先学习概 ...
- Docker系列(22)- DockerFile指令说明并构建自己的centos
DockerFile常用指令 实战测试 DockerHub中99%镜像都是从这个基础镜像过来的FROM scratch,然后配置需要的软件和配置来进行构建 #创建一个自己的centos # 1.编写D ...
- PHP 流行的框架
Aura Laravel Symphony Yii Zend php components Packagist 最好的组件: Awesome PHP https://www.yiiframework. ...
- RabbitMQ3.9.7在CentOS7中的安装搭建
1.概述 RabbitMQ 是目前很流行的消息中间件之一,可靠性非常好,能简单的实现高可用.负载均衡. 今天我们先来聊一下 RabbitMQ 3.9.7 版本在 CentOS7 中的安装. 2.安装R ...
- P3307-[SDOI2013]项链【Burnside引理,莫比乌斯反演,特征方程】
正题 题目链接:https://www.luogu.com.cn/problem/P3307 题目大意 \(n\)个珠子的一个环形项链,每个珠子有三个\(1\sim k\)的整数. 两个珠子不同当且仅 ...
- es相关监控指标梳理
###################ElasticSearch监控指标梳理########################### #author:lugh1 # #date:2021-09-26 # ...
- Phalcon如何创建多模块并能进行访问 《Phalcon入坑指南系列 四》
本系列目录 一.Phalcon在Windows上安装 <Phalcon入坑指南系列 一> 二.Phalcon入坑必须知道的功能<Phalcon入坑指南系列 二> 三.Phalc ...
- WPF进阶技巧和实战03-控件(5-列表、树、网格01)
列表控件 ItemsControl为列表项控件定义了基本功能,下图是ItemsControl的继承关系: 在继承自ItemsControl类的层次结构中,还显示了项封装器(MenuItem.TreeV ...