Linux环境编程之文件I/O(三):文件的读写
(一)
当我们打开了一个文件后,一般对文件的操作就是读写。读写函数分别是read、write。
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
参数:
fd:利用open、creat得到的文件描述符。
buf:buf是void *类型,用于表示通用指针,此处指所读取到的数据的内存缓冲。
count:需要读取的数据量。
返回值:成功执行时,返回所读取的数据的字节数,一般等于或小于所请求读取的数据字节数。若已到文件结尾则返回0,若出错则返回-1。失败返回-1时,error被设为以下的某个值。这个error一般是在内核里的驱动程序中设置的,不是应用程序设置的。
EAGAIN:打开文件时设定了O_NONBLOCK标志,并且当前没有数据可读取
EBADF:文件描述词无效,或者文件不可读
EFAULT:参数buf指向的空间不可访问
EINTR:数据读取前,操作被信号中断
EINVAL:一个或者多个参数无效
EIO:读写出错
EISDIR:参数fd索引的时目录
上面提到返回值可能小于所请求读取的字节数。原因可能是:
1、读普通文件时,在读到要求字节数之前已到达了文件尾端。
2、当从终端设备读时,通常一次最多读一行。
3、当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数。
4、当从管道或FIFO读时,如若管道包含的字节少于所需的数量,那么read将只返回实际可用的字节数。
5、当从某些面向记录的设备读时,一次最多返回一个记录。
6、当某一信号造成中断,而已经读了部分数据时。(以后会在信号部分详细讨论该情况)
(二)
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
write函数与read函数相对,是向打开的文件写数据。
参数:
fd:利用open、creat得到的文件描述符。
buf:buf是void *类型,用于表示通用指针,此处指所写入的数据的内存缓冲。
count:需要写入的数据量。
返回值:若成功则返回已写的字节数,若出错则返回-1。
注意:对于普通文件,写操作从文件的当前偏移量处开始。如果在打开该文件时,指定了O_APPEND选项,则在每次写操作之前,将文件偏移量设置在文件的当前结尾处。在一次成功写之后,该文件偏移量增加实际写的字节数。
(三)
上面write函数中的注意部分,有“文件的当前偏移量处”的描述,如果我想从文件的开始处或文件的结尾处写数据该如何操作呢?这就用到了另外一个I/O函数lseek函数。
其实,每个打开的文件都有一个与其相关联的“当前文件偏移量”。它通常是一个非负整数,用以度量从文件开始处计算的字节数。通常,读写操作都从当前文件偏移量处开始,并使偏移量增加所读写的字节数。系统默认情况下,当打开一个文件时,除非指定O_APPEND选项,否则该偏移量被设置为0。
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
参数:
filedes:利用open、creat得到的文件描述符。
offset与whence有关,whence有三个SEEK_SET、SEEK_CUR、SEEK_END。
1、若whence是SEEK_SET,则将该文件的偏移量设置为距文件开始处offset个字节。
2、若whence是SEEK_CUR,则将该文件的偏移量设置为其当前值加offset,offset可正可负。
3、若whence是SEEK_END,则将该文件的偏移量设置为文件长度加offset,offset可正可负。
返回值:若成功则返回新的文件偏移量,若出错则返回-1。
注意:
1、并不是所有的文件都能设置文件偏移量,对于管道、FIFO、网络套接字等不能设置文件偏移量。可用下列方法做判断:
off_t currpos;
currpos = lseek(fd, 0, SEEK_CUR);
若返回值为-1,则不能设置文件偏移量,并将error设置为ESPIPE。
2、因为偏移量可能是负值,所以在比较lseek的返回值时应当谨慎,不要测试它是否小于0,而要测试它是否等于-1。
if(lseek(STDIN_FILENO, 0, SEEK_CUR) == -1)
3、lseek仅将文件偏移量记录在内核中,它并不引起任何I/O操作。然后,该偏移量用于下一个读写操作。文件偏移量可以大于文件的当前长度,可在文件中构成一个空洞,文件中的空洞并不要求在磁盘上占用存储区。(四)
/*
*File Name:demo.c
*Author :libing
*Mail :libing1209@126.com
*function :read、write、lseek
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define BUFFSIZE 2048
int
main(void)
{
int fd;
char buf[BUFFSIZE] = {0, 1, 2, 3};
ssize_t nbytes;
//创建新的文件test.txt
fd = open("test.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if(fd == -1){
printf("creat test.txt failed.\n");
exit(1);
}
//写数据到文件
nbytes = write(fd, buf, BUFFSIZE);
if(nbytes == -1){
printf("read failed.\n");
}
//为打开的文件设置偏移量到文件开始处
if(lseek(fd, 0, SEEK_SET) == -1){
printf("cannot seek\n");
}
//读文件数据
nbytes = read(fd, buf, BUFFSIZE);
if(nbytes == -1){
printf("read failed.\n");
}
printf("nbytes = %d.\n", nbytes);
return 0;
}
编译测试结果:
编译程序:
gcc demo.c
运行程序:
./a.out
结果显示:
nbytes = 2048.
(五)
应用举例:
Linux中,我们经常使用cp命令复制一个文件内容到另一个文件中,如cp file1 file2。复制的命令,我们就可以用文件I/O来完成。
/*
*File Name : copy.c
*Author : libing
*Mail : libing1209@126.com
*Function : copy file1 to file2
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int
main(int argc, char *argv[])
{
int file1fd;
int file2fd;
char buf[2048];
int nbytes;
if(argc != 3){
printf("usage %s file1 file2.\n", argv[0]);
return 0;
}
//源文件,即要复制的文件file1
file1fd = open(argv[1], O_RDONLY);
if(file1fd == -1){
printf("open file1 failed.\n");
return 0;
}
//目的文件,即要复制到的文件file2
file2fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if(file2fd == -1){
printf("open file2 failed.\n");
return 0;
}
//进行文件的复制
while((nbytes = read(file1fd, buf, 2048)) > 0)
write(file2fd, buf, nbytes);
close(file1fd);
close(file2fd);
return 0;
}
编译结果测试
编译文件:
gcc copy.c
执行文件:
./a.out demo.c test.txt
显示结果:vi test.txt 可以看到其内容与demo.c内容相同。