Linux文件IO常用函数
1. 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,若文件不存在则可创建
参数:
pathname:文件路径名
flags:打开文件的方式,必选项:O_RDONLY、O_WRONLY、O_RDWR
mode:新建文件时指定文件的权限,此参数仅在文件不存在时生效
返回值:
成功:打开文件的文件描述符
失败:-1
*/
flags详细说明:
必选项:
取值 | 含义 |
O_RDONLY | 以只读的方式打开 |
O_WRONLY | 以只写的方式打开 |
O_RDWR | 以可读、可写的方式打开 |
可选项:(和必选项按位或)
取值 | 含义 |
O_CREAT | 若文件不存在则创建,此选项需要指定mode参数说明文件权限 |
O_EXCL | 若同时指定了O_CREAT,且文件已存在,则出错 |
O_TRUNC | 若文件存在,则清空文件内容 |
O_APPEND | 以追加的方式写文件 |
O_NONBLOCK | 对于设备文件,以非阻塞方式打开 |
mode权限:
取值(八进制) | 含义 |
00700 | 文件所有者:读、写、执行 |
00400 | 文件所有者:读 |
00200 | 文件所有者:写 |
00100 | 文件所有者:执行 |
00070 | 文件所有者同组:读、写、执行 |
00040 | 文件所有者同组:读 |
00020 | 文件所有者同组:写 |
00010 | 文件所有者同组:执行 |
00007 | 其他组:读、写、执行 |
00004 | 其他组:读 |
00002 | 其他组:写 |
00001 | 其他组:执行 |
2. close函数
函数原型:
#include<unistd.h>
int close(int fd);
/*
功能:
关闭文件fd
参数:
fd:文件描述符,open的返回值
返回值:
成功:0
失败:-1,并设置errno
*/
open和close使用示例:
void test() {
int fd = -1;
// 以只读的方式打开一个文件,若文件不存在则报错
fd = open("txt", O_RDONLY);
// 以只写的方式打开一个文件,若文件不存在则新建;文件存在则直接打开,同时mode参数无效。
fd = open("txt", O_WRONLY | O_CREAT, 0644);
// 以只写的方式打开一个文件,若文件不存在则新建;文件存在则报错。
fd = open("txt", O_WRONLY | O_CREAT | O_EXCL, 0644);
// 以只写的方式打开一个文件,若文件不存在则新建;文件存在则清空文件内容。
fd = open("txt", O_WRONLY | O_TRUNC |O_CREAT, 0644);
// 以只写和追加的方式打开一个文件,若文件不存在则报错。
fd = open("txt", O_WRONLY | O_APPEND);
if (-1 == fd) {
perror("open");
return 1;
}
// 文件描述符 0 1 2 已被使用,会分配一个最小的空闲文件描述符
printf("fd = %d\n", fd);
close(fd);
}
3. write函数
函数原型:
#include<unistd.h>
ssize_t write(int fd, const void* buf, size_t count);
/*
功能:
将buf中count个字节的数据写到文件fd
参数:
fd:写入的目标文件描述符
buf:写入数据的首地址
count:写入数据的字节数
返回值:
成功:实际写入数据的字节数
失败:-1
*/
write使用示例:
void test() {
int fd = -1;
int ret = -1;
char* str = "Hello";
// 1. 只写的方式打开一个文件
fd = open("txt", O_WRONLY | O_CREAT, 0644);
if (-1 == fd) {
perror("open");
return 1;
}
printf("fd = %d\n", fd);
// 2. 写文件
ret = write(fd, str, strlen(str));
if (-1 == ret) {
perror("write");
return 1;
}
printf("实际写入:%d个字节.\n", ret);
// 3. 关闭文件
close(fd);
}
4. read函数
函数原型:
#include<unistd.h>
ssize_t read(int fd, void* buf, size_t count);
/*
功能:
从文件fd将count个字节的数据读入buf
参数:
fd:被读取文件的文件描述符
buf:内存缓冲区首地址
count:读取的字节数
返回值:
成功:实际读取的字节个数
失败:-1
*/
read使用示例:
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#define SIZE 128
int test() {
int fd = -1;
int ret = -1;
char buf[SIZE];
// 1. 以只读的方式打开一个文件
fd = open("txt", O_RDONLY);
if (-1 == fd) {
perror("open");
return 1;
}
printf("fd = %d\n", fd);
// 读文件
memset(buf, 0, SIZE); // 数组清零
// 返回实际读取的字节数,这里读取了5个字节
ret = read(fd, buf, SIZE);
if (-1 == ret) {
perror("read");
return 1;
}
printf("read len: %d. %s\n", ret, buf); // read len: 5. Hello
}
非阻塞read使用示例:
(注意:非阻塞是文件属性,而不是read函数属性)
int test() {
/*
以非阻塞、读的方式打开当前终端。 /dev/tty当前终端设备
*/
int fd = open("/dev/tty", O_RDONLY | O_NONBLOCK);
char buf[10];
int n;
n = read(fd, buf, sizeof(buf));
if (n < 0) {
// EAGAIN:若非阻塞,但是没有数据可读。此时全局变量errno被设置为EAGAIN。
if (errno != EAGAIN) {
perror("read /dev/tty");
return -1;
}
printf("没有数据.\n");
}
return 0;
}
5. lseek
(1)所有打开的文件都有一个当前文件偏移量(current file offset, cfo),cfo通常为非负整数,表示文件起始位置到当前位置的偏移字节数。
(2)读写操作通常开始于cfo,并使cfo增大,增量为读写的字节数。除非以O_APPEND的方式打开文件,其余方式打开文件cfo被初始化为0.
函数原型:
#include<sys/types.h>
#include<unistd.h>
off_t lseek(int fd, off_t offset, int whence);
/*
功能:
从文件fd的whence位置开始偏移offset个字节
参数:
fd:文件描述符
offset:从whence位置开始的偏移量。
正数:从whence位置向右移动; 负数:从whence位置向左移动。
若向左偏移超过了文件起始位置,则出错返回;
若向右偏移超过了文件末尾位置,则再次写入时将增大文件大小。
whence:
SEEK_SET:从文件起始位置偏移offset个字节
SEEK_CUR:从文件当前位置偏移offset个字节
SEEK_END:从文件末尾位置偏移offset个字节
返回值:
成功:返回新的偏移量
失败:-1
*/
lseek使用示例:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#define SIZE 128
int test() {
int fd = -1;
int ret = -1;
char buf[SIZE];
// 1. 打开文件
fd = open("txt", O_RDWR | O_CREAT, 0644);
if (-1 == fd) {
perror("open");
return 1;
}
printf("fd = %d\n", fd);
// 2. lseek操作
write(fd, "ABCD", 4); // 此时txt文件为4字节
// 从文件起始位置偏移10个字节。
// 若只偏移,后续没有从偏移的新位置的写操作,则文件大小不变,仍为4字节
ret = lseek(fd, 10, SEEK_SET);
if (-1 == ret) {
perror("lseek");
return 1;
}
// 从偏移位置(距文件起始位置10字节)写入5字节,此时文件大小15字节
// 此时txt中数据为:ABCD^@^@^@^@^@^@12345
// ^@表示ascii为0的字符
ret = write(fd, "12345", 5);
if (-1 == ret) {
perror("write");
return 1;
}
memset(buf, 0, SIZE);
// 无法读取,因为文件偏移位置在末尾
ret = read(fd, buf, SIZE);
if (-1 == ret) {
perror("read");
return 1;
}
printf("read buf: %s\n", buf); // 无数据
// 将文件偏移到起始位置
ret = lseek(fd, 0, SEEK_SET);
if (-1 == ret) {
perror("lseek");
return 1;
}
// 可读出文件中的数据:ABCD, 因为遇到ascii为0的字符就认为文件结束
ret = read(fd, buf, SIZE);
if (-1 == ret) {
perror("read");
return 1;
}
printf("read buf: %s\n", buf); // ABCD
// 3. 关闭文件
close(fd);
}