Linux文件IO操作
来源:微信公众号「编程学习基地」
[toc]
文件操作
在进行 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.clrwxrwxrwx 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操作
- Linux下C编程入门笔记——文件IO操作(一)
- Linux下C编程-----IO/文件操作/内存映射 实现简单记录存储(3)
- 嵌入式Linux文件IO,目录操作opendir(),readdir(),closedir()文件权限chmode(),fchmode(),文件属性stat()/lstat()/fstat()文件类型
- Linux编程——文件 IO操作
- linux 文件操作,IO多路复用,IPC 进程间通信
- Linux下C编程入门笔记——文件IO操作(二)
- Linux下用文件IO的方式操作GPIO(/sys/class/gpio)
- linux下的文件IO缓冲区,及其相关操作
- Linux下用文件IO的方式操作GPIO(/sys/class/gpio)
- Linux下用文件IO的方式操作GPIO(/sys/class/gpio)
- linux 文件状态标识和IO操作方式
- Linux操作GPIO(文件IO方式)
- Linux下用文件IO的方式操作GPIO(/sys/class/gpio)
- linux文件IO操作
- Linux下用文件IO的方式操作GPIO
- Linux系统编程:标准IO操作--索引对应路径下的文件
- Linux下用文件IO的方式操作GPIO(/sys/class/gpio)
- linux的文件io操作(转)