木头骑士的Linux编程实验室(二)——基本的文件读写操作
2015-09-08 18:31
756 查看
在Linux中,文件是一个相当重要的概念,不光普通的文件需要读写,Linux几乎将所有课读写的东西都抽象成了文件,都采用文件的操作方式进行操作,包括什么设备驱动啦,目录啦,管道啦,符号链接啦,甚至网络间的数据传输,都抽象成了文件,所以,掌握了文件的操作方式,也就几乎能够操作Linux上的一切啦。
文件操作能够管理如此多的东西,所以文件操作也提供了相对比较复杂的函数接口,本章试验几个基本的函数接口,包括open、read、write、lseek和close,他们分别负责打开文件、读文件、写文件、设置文件偏移量以及关闭文件。
下面先给出这几个函数的原型:
下面对上述函数作出简单说明。
open函数用于将一个用路径字符串表示的文件打开,返回一个文件描述符。文件描述符时一个非负整数,通常调用该函数时,返回的文件描述符都是当前环境下可用的最小整数值。一般一个shell环境下都已经预先打开了3个描述符:0表示标准输入,1表示标准输出,2表示标准出错。open的第一个参数就是要打开的文件。第二个参数oflag用来说明此函数的选项,该选项有多个可选值,本章试验只介绍几个比较常用的:
O_RDONLY 只读打开。
O_WRONLY 只写打开。
O_RDWR 读写打开。
O_EXEC 只执行打开。
O_SEARCH 只搜索打开(应用于目录)。
以上五个标识必须且只能指定一个。一般来说常用的是前3个。
O_APPEND 每次写时都追加到文件的尾端。
O_CREAT 若此文件不存在,则创建它。使用此选项时,open函数需要同时说明第三个参数mode,用mode指定该新文件的访问权限。
O_TRUNC 如果此文件存在,而且为只写或读写成功打开,则将其长度截断为0。
还有其他的标志,在后面试验到更高级的文件操作时,将顺便提及,这里不做过多介绍了。当同时指定多个oflag参数时,要用或标识连接起来。
openat函数提供了一种相对路径打开文件的方法,有一下3种可能:
1.path参数指定的是绝对路径名,在这种情况下,fd参数被忽略,openat函数等同于open
2.path参数指定的是相对路径名,fd参数指出了相对路径名在文件系统中的开始地址。fd参数是通过打开相对路径名所在的目录来获取的。
3.path参数指定了相对路径名,fd参数具有特殊值AT_FDCWD。在这种情况下,路径名在当前工作目录中获取。
Linux还提供了好多以at结尾的系统调用,其参数均符合上述3种可能性,后面就不再赘述了。
close函数用于关闭一个打开的文件,在文件不使用时,要关闭文件,释放文件描述符,同时,也会释放该进程加在该文件上的所有记录锁。
lseek函数用于设置文件的偏移量,表示将文件的偏移量设置成从whence算起offset个字节的偏移量。whence取值如下:
若whence是SEEK_SET,则从文件开始处算起
若whence是SEEK_CUR,则从文件的当前偏移量算起
若whence是SEEK_END,则从文件的结尾算起
read函数从文件中读数据,存放到buf中,nbytes指出每次想要读取的长度。可能文件中没有那么多数据可读了,可以通过返回值确定实际读到的字节数。如果已经达到文件尾了,则会返回0。
write函数将buf中的数据写入文件,写入字节数为nbytes。
下面做第一个实验,已只写并且O_TRUNC的模式打开文件/tmp/a.txt,然后向其中写入字符串"Hello World!",然后关闭文件,再以只读的方式打开文件,读取文件内容输出到标准输出,然后用lseek将文件偏移量放到文件的第六个字节处,再次读取文件内容并输出到标准输出。代码如下:
运行结果为:
Hello World!
World!
这里看到,我们在第一次打开文件时指定了O_TRUNC,如果不指定这个标志,在打开文件时,文件偏移量为文件开头处,但是并不清除文件的内容,接下来的写操作就会造成覆盖之前文件内容的情况,如果写入的字符数比文件中原有的字符数要多,那么也没什么问题,反正都覆盖了嘛,但如果写入的字符数比原来文件中的字符数要少,那么文件中没被覆盖的字符数就仍然存在。
接下来在看看open函数的O_CREAT和O_APPEND标志。做下面一个实验:首先在/tmp下只写打开文件b.txt,并删除其中内容,如果文件不存在就创建这个文件,然后向其中写入一行字符串"This is the first line.\n",然后关闭文件,然后以读写,追加的方式再次打开文件,然后从终端读取输入,追加写入文件中,直到读取到了'*'星号字符为止,最后将整个文件的内容输出。代码如下:
O_APPEND标志等小于打开文件后执行lseek(fd, 0, SEEK_END);但两者仍有区别。O_APPEND标志使open和lseek两个操作成为一个原子操作,配合后面会试验的记录锁或进程互斥技术,可避免可能出现的多进程操作文件时的文件操作混乱。
O_CREAT标志表示如果没有这个文件,则创建之,此时要为open提供第三个参数,用于表示访问权限。
还有一个专门用于创建文件的系统调用——creat
返回值:若成功,返回为只写打开的文件描述符;若出错,返回-1
该函数等价于
open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
入此看来,还是open创建文件方便一些,因为可以同时指定只写之外的读写权限。
说明:本文的实验使用eclipse建立工程,已上传至github:
https://github.com/haoranzeus/LinuxProgrammingLib.git
文件操作能够管理如此多的东西,所以文件操作也提供了相对比较复杂的函数接口,本章试验几个基本的函数接口,包括open、read、write、lseek和close,他们分别负责打开文件、读文件、写文件、设置文件偏移量以及关闭文件。
下面先给出这几个函数的原型:
#include <fcntl.h> int open(const char *path, int oflag, ... /* mode_t mode */); int openat(int fd, const char *path, int oflag, ... /* mode_t mode */);返回值:成功,返回文件描述符;若出错,返回-1
#include <unistd.h> int close(int fd);返回值:若成功,返回0;若出错,返回-1
#include <unistd.h> off_t lseek(int fd, off_t offset, int whence);返回值:若成功,返回新的文件偏移量;若出错,返回-1
#include <unistd.h> ssize_t read(int fd, void *buf, size_t nbytes);返回值:读到的字节数,若已到文件尾,返回0;若出错,返回-1
#include <unistd.h> ssize_t write(int fd, const void *buf, size_t nbytes);返回值:若成功,返回已写的字节数;若出错,返回-1
下面对上述函数作出简单说明。
open函数用于将一个用路径字符串表示的文件打开,返回一个文件描述符。文件描述符时一个非负整数,通常调用该函数时,返回的文件描述符都是当前环境下可用的最小整数值。一般一个shell环境下都已经预先打开了3个描述符:0表示标准输入,1表示标准输出,2表示标准出错。open的第一个参数就是要打开的文件。第二个参数oflag用来说明此函数的选项,该选项有多个可选值,本章试验只介绍几个比较常用的:
O_RDONLY 只读打开。
O_WRONLY 只写打开。
O_RDWR 读写打开。
O_EXEC 只执行打开。
O_SEARCH 只搜索打开(应用于目录)。
以上五个标识必须且只能指定一个。一般来说常用的是前3个。
O_APPEND 每次写时都追加到文件的尾端。
O_CREAT 若此文件不存在,则创建它。使用此选项时,open函数需要同时说明第三个参数mode,用mode指定该新文件的访问权限。
O_TRUNC 如果此文件存在,而且为只写或读写成功打开,则将其长度截断为0。
还有其他的标志,在后面试验到更高级的文件操作时,将顺便提及,这里不做过多介绍了。当同时指定多个oflag参数时,要用或标识连接起来。
openat函数提供了一种相对路径打开文件的方法,有一下3种可能:
1.path参数指定的是绝对路径名,在这种情况下,fd参数被忽略,openat函数等同于open
2.path参数指定的是相对路径名,fd参数指出了相对路径名在文件系统中的开始地址。fd参数是通过打开相对路径名所在的目录来获取的。
3.path参数指定了相对路径名,fd参数具有特殊值AT_FDCWD。在这种情况下,路径名在当前工作目录中获取。
Linux还提供了好多以at结尾的系统调用,其参数均符合上述3种可能性,后面就不再赘述了。
close函数用于关闭一个打开的文件,在文件不使用时,要关闭文件,释放文件描述符,同时,也会释放该进程加在该文件上的所有记录锁。
lseek函数用于设置文件的偏移量,表示将文件的偏移量设置成从whence算起offset个字节的偏移量。whence取值如下:
若whence是SEEK_SET,则从文件开始处算起
若whence是SEEK_CUR,则从文件的当前偏移量算起
若whence是SEEK_END,则从文件的结尾算起
read函数从文件中读数据,存放到buf中,nbytes指出每次想要读取的长度。可能文件中没有那么多数据可读了,可以通过返回值确定实际读到的字节数。如果已经达到文件尾了,则会返回0。
write函数将buf中的数据写入文件,写入字节数为nbytes。
下面做第一个实验,已只写并且O_TRUNC的模式打开文件/tmp/a.txt,然后向其中写入字符串"Hello World!",然后关闭文件,再以只读的方式打开文件,读取文件内容输出到标准输出,然后用lseek将文件偏移量放到文件的第六个字节处,再次读取文件内容并输出到标准输出。代码如下:
void fileTest1(){ int fd = open("/tmp/a.txt", O_WRONLY | O_TRUNC); if(-1 == fd) { perror("open error"); return; } if (-1 == write(fd, "Hello World!", sizeof("Hello World!"))) { perror("write error"); return; } if (-1 == close(fd)) { perror("close error"); return; } fd = open("/tmp/a.txt", O_RDONLY); if (-1 == fd) { perror("open error"); return; } char tmp[20]; ssize_t readsize = read(fd, tmp, 20); if (-1 == readsize) { perror("read error"); return; } printf("%s\n", tmp); if (-1 == lseek(fd, 6, SEEK_SET)) { perror("lseek error"); return; } readsize = read(fd, tmp, 20); if (-1 == readsize) { perror("read error"); return; } printf("%s\n", tmp); }
运行结果为:
Hello World!
World!
这里看到,我们在第一次打开文件时指定了O_TRUNC,如果不指定这个标志,在打开文件时,文件偏移量为文件开头处,但是并不清除文件的内容,接下来的写操作就会造成覆盖之前文件内容的情况,如果写入的字符数比文件中原有的字符数要多,那么也没什么问题,反正都覆盖了嘛,但如果写入的字符数比原来文件中的字符数要少,那么文件中没被覆盖的字符数就仍然存在。
接下来在看看open函数的O_CREAT和O_APPEND标志。做下面一个实验:首先在/tmp下只写打开文件b.txt,并删除其中内容,如果文件不存在就创建这个文件,然后向其中写入一行字符串"This is the first line.\n",然后关闭文件,然后以读写,追加的方式再次打开文件,然后从终端读取输入,追加写入文件中,直到读取到了'*'星号字符为止,最后将整个文件的内容输出。代码如下:
void fileTest2() { int fd = open("/tmp/b.txt", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); if (-1 == fd) { perror("open error"); return; } if (-1 == write(fd, "This is the first line.\n", sizeof("This is the first line.\n") - 1)) { perror("write error"); return; } if (-1 == close(fd)) { perror("close error"); return; } fd = open("/tmp/b.txt", O_RDWR | O_APPEND); if (-1 == fd) { perror("open error"); return; } char tmp; printf("please input something, end with a '*'\n"); char str[20]; while('*' != (tmp = getchar())) { str[0] = tmp; if (-1 == write(fd, str, 1)) { perror("write error"); return; } } if (-1 == lseek(fd, 0, SEEK_SET)) { perror("lseek error"); return; } int readsize; do { readsize = read(fd, str, 20); if (-1 == readsize) { perror("read error"); return; } if (-1 == write(1, str, readsize)) { perror("write error"); return; } } while(0 != readsize); close(fd); }
O_APPEND标志等小于打开文件后执行lseek(fd, 0, SEEK_END);但两者仍有区别。O_APPEND标志使open和lseek两个操作成为一个原子操作,配合后面会试验的记录锁或进程互斥技术,可避免可能出现的多进程操作文件时的文件操作混乱。
O_CREAT标志表示如果没有这个文件,则创建之,此时要为open提供第三个参数,用于表示访问权限。
还有一个专门用于创建文件的系统调用——creat
#include <fcntl.h> int creat(const char *path, mode_t mode);
返回值:若成功,返回为只写打开的文件描述符;若出错,返回-1
该函数等价于
open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
入此看来,还是open创建文件方便一些,因为可以同时指定只写之外的读写权限。
说明:本文的实验使用eclipse建立工程,已上传至github:
https://github.com/haoranzeus/LinuxProgrammingLib.git
相关文章推荐
- Linux下VLAN功能的实现
- linux常用命令(19):find命令概览
- Linux进程管理及进程管理工具的使用
- 2015年9月5日课程作业(at、crontab)
- 编译器的工作过程
- linux 上创建不了模拟器
- CentOS 7 关闭防火墙 SELinux , FireWalld
- 在CentOS 6.3 64bit上搭建python高性能框架gevent开发环境
- Linux文件处理命令
- 网易linux笔试题
- linux patch 命令小结
- linux /bin FHS翻译
- CentOS 6.2编译安装MySQL 5.5.25
- Linux系统裁剪(3)之动态增加Linux模块
- Linux下安装maven
- CentOS7下FTP的安装配置
- CentOS 6.3 手动rpm包安装gcc、g++
- Linux 安装图形界面及远程连接
- linux用户态和内核态理解
- linux中C语言获取高精度时钟gettimeofday函数