2. 文件编程
2.1 什么是文件I/O
I/O是指输入/输出(Input/Output)的缩写,在Linux系统中,所有的输入和输出都是通过文件进行的,这也被称为文件I/O。 Linux文件I/O指的是在Linux操作系统中主存和外部设备(比如硬盘、U盘)进行文件输入和输出的过程,其中数据从设备到内存的过程称为输入,数据从内存到设备的过程叫输出。
2.2 Linux文件I/O编程的基本方式
- 标准I/O(stdio):标准I/O函数库提供了一系列高级的文件I/O函数,如fopen、fclose、fread、fwrite等,它们可以帮助程序员更方便地进行文件操作。标准I/O函数库还提供了缓冲区机制,可以提高文件I/O的效率。
- 文件I/O(syscall):系统调用I/O是直接使用系统调用进行文件操作,例如open、read、write、close等函数。与标准I/O函数库不同,系统调用I/O函数没有缓冲区,文件I/O的效率更高。
- 原始I/O(raw I/O):原始I/O是一种低级别的文件I/O,使用read和write函数进行数据读写,可以更加细粒度地控制文件的I/O操作。原始I/O一般用于对特殊设备进行操作,例如磁盘分区、网络设备等等。 这三种方式在Linux系统中都可以使用,选择哪一种方式主要取决于具体的应用场景和需要。标准I/O适用于大多数文件I/O操作,可以提高程序的开发效率;系统调用I/O适用于对文件进行更底层的操作,可以提高文件I/O的效率;原始I/O则适用于对特殊设备进行操作。
2.3 文件描述符
对于文件I/O来说,一切都是通过文件描述符进行的。在Linux系统中,当打开或者创建一个文件的时候,内核向进程返回一个对应的文件描述符(非负整数)。同时还规定一个进程启动的时候,0是标准输入,1是标准输出,2是标准错误。这意味着如果此时去打开一个新的文件,它的文件描述符会是3,再打开一个文件文件描述符就是4......
POSIX 定义了 STDIN_FILENO、STDOUT_FILENO 和 STDERR_FILENO 来代替 0、1、2。这三个符号常量的定义位于头文件 unistd.h。
2.4 打开或创建文件
Linux提供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 表示文件打开方式;
- mode用来规定对该文件的所有者、文件的用户组及系统中其他用户的访问权限。
- 如果函数行成功,就返回文件描述符,如果函数执行失败,就返回-1。
文件打开的方式flags可以使用下列宏 (当有多个选项时,采用“|”连接):
- O_RDONLY:打开一个只供读取的文件。
- O_WRONLY: 打开一个只供写入的文件。
- O_RDWR:打开一个可供读写的文件。
- O_APPEND:写入的所有数据将被追加到文件的末尾。
- O_CREAT:打开文件,如果文件不存在就建立文件。
- O_EXCL:如果已经置O_CREAT且文件存在,就强制open失败。
- O_TRUNC:在打开文件时,将文件的内容清空。
mode只有创建文件时才使用此参数,指定文件的访问权限。模式有:
- S_IRUSR:文件所有者的读权限位。
- S_IWUSR:文件所有者的写权限位。
- S_IXUSR: 文件所有者的执行权限位。
- S_IRWXU: S IRUSRS IWUSRS IXUSR。
- S_IRGRP:文件用户组的读权限位。
- S_IWGRP.文件用户组的写权限位。
- S_IXGRP:文件用户组的执行权限位。
- S_IRWXG: S_IRGRP|S_IWGRPI|S_IXGRP。
- S_IROTH:文件其他用户的读权限位。
- S_IWOTH:文件其他用户的写权限位。
- S_IXOTH: 文件其他用户的执行权限位。
- S_IRWXO:S_IROTH|S_IWOTH|IXOTH。
文件访问权限的计算是根据umask&~mode得出来的。
当我们登录 Linux 系统之后创建文件总有一个默认权限,设置用户创建文件的默认权限就是umask所做的工作。umask 设置文件权限通过指定建立文件时预设的权限掩码来实现,不能默认创建可执行文件,必须手工赋予执行权限,所以创建文件时默认为最大权限 666,而目录默认最大权限为777。
计算方法:
例如umask值为022:
linaro@linaro-alip:~$ umask
0022则默认文件的权限为:110 110 110 & (~000 010 010) = 110 110 110 & 111 101 101 = 110 100 100
即默认权限为:644
打开当前工作目录 luckfox 下的文件test.txt:
int fd = open("./luckfox/test.txt", O_RDWR)如果要以只读方式打开当前目录上级目录下的某个文件:
int fd = open("../luckfox .txt", O_RDWR)打开绝对路径下的文件(不存在就创建,否则以读写方式打开):
int fd = open("./luckfox/test.txt", O_CREAT| O_RDWR)创建文件:新建一个open.cpp文件,在open.cpp中输入代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
fd = open("luckfox.txt", O_CREAT | O_RDWR, 0666);
if (fd == -1)
{
printf("open is error\n");
return -1;
}
printf("fd is %d\n", fd);
return 0;
}- 如果函数执行成功,返回的文件描述符为3;否则返回-1。
2.5 读取文件的数据
Linux提供read函数从打开的文件中读取数据,该函数声明如下:
#include<unistd.h>
ssize_t read(int fd, void * buf ,size_t count)参数含义:
- fd为文件描述符;
- buf表示读出数据缓冲区地址;
- count表示读出的字节数。
函数含义:把参数fd所指的文件传送count个字节到buf指针所指的内存中。
返回值:若读取成功,则返回实际读到的字节数。若失败,返回-1。若已达到文件尾或没有可读取数据,则返回0。注意:因此读到的字节数可能小于count的值。
从文件中读取数据:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
char buf[32] = {0};
ssize_t ret;
fd = open("luckfox.txt", O_RDWR);
if (fd == -1)
{
printf("open is error\n");
return -1;
}
printf("fd is %d\n", fd);
ret = read(fd,buf,32);
if (ret == -1)
{
printf("read is error\n");
return -2;
}
printf("buf is %s", buf);
printf("ret is %ld\n", ret);
close(fd);
return 0;
}
2.6 向文件写入数据
Linux提供write函数将数据写入已打开的文件内,该函数声明如下:
#include<unistd.h>
ssize_t write(int fd, void * buf ,size_t count)参数含义:
- fd为文件描述符;
- buf表示读出数据缓冲区地址;
- count表示读出的字节数。
函数含义:把参数buf所指的缓冲区中的count个字节数数据写入fd所指的文件内。
向文件中写入数据:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
int main(int argc, char *argv[])
{
int fd;
ssize_t ret;
char buf[] = "boys and girls\n hi,children!";
char filename[] = "luckfox.txt";
fd = open(filename,O_RDWR|O_APPEND);
if (fd == -1)
{
printf("open is error\n");
return -1;
}
printf("fd is %d\n", fd);
ret = write(fd,buf,strlen(buf));
printf("write %d bytes to file %s\n",ret,filename);
close(fd);
return 0;
}
2.7 文件偏移量
在实际应用中,有的时候需要从文件中某个位置开始读写,此时需要让文件读写位置移动到新的位置。在Linux系统中可使用函数 lseek 来修改文件偏移量 ( 读写位置 ),该函数声明如下:
#include <sys/types.h>
#include<unistd.h>
off_t lsweek(int fd, off_t offset, int whence)参数含义:
- fd为文件描述符;
- offset:偏移量,以字节为单位,offset 可以为正、也可以为负,如果是正数表示往后偏移,如果是负数则表示往前偏移。
- whence:与参数offset偏移量搭配使用,该参数为当前位置的基点,可以使用一下三种值:
- SEEK_SET:相对于文件开头;
- SEEK_CUR:相对于当前的文件读写指针位置;
- SEEK_END:相对于文件末尾。
把文件位置指针设置为100:
lseek(fd,100,SEEK SET);把文件位置设置成文件末尾:
lseek(fd,0,SEEK END);确定当前的文件位置:
lseek(fd,0,SEEK CUR);第二次重新开始读取文件:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
char buf[32] = {0};
ssize_t ret;
fd = open("luckfox.txt", O_CREAT | O_RDWR, 0666);
if (fd == -1)
{
printf("open is error\n");
return -1;
}
// printf("fd is %d\n", fd);
ret = read(fd, buf, 32);
if (ret < 0)
{
printf("read is error\n");
return -2;
}
printf("buf is %s\n", buf);
printf("ret is %ld\n", ret);
lseek(fd,0,SEEK_SET);
ret = read(fd, buf, 32);
printf("ret is %ld\n", ret);
close(fd);
return 0;
}确定文件的当前位置:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
char buf[32] = {0};
ssize_t ret;
fd = open("luckfox.txt", O_CREAT | O_RDWR, 0666);
if (fd == -1)
{
printf("open is error\n");
return -1;
}
ret = read(fd, buf, 8);
if (ret < 0)
{
printf("read is error\n");
return -2;
}
printf("buf is %s\n", buf);
printf("ret is %ld\n", ret);
ret=lseek(fd,0,SEEK_CUR);
printf("ret is %ld\n", ret);
close(fd);
return 0;
}查看文件大小(字节数):
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int fd;
char buf[32] = {0};
ssize_t ret;
fd = open("luckfox.txt", O_CREAT | O_RDWR, 0666);
if (fd == -1)
{
printf("open is error\n");
return -1;
}
ret = read(fd, buf, 2);
if (ret < 0)
{
printf("read is error\n");
return -2;
}
printf("buf is %s\n", buf);
printf("ret is %ld\n", ret);
ret=lseek(fd,0,SEEK_END);
printf("ret is %ld\n", ret);
close(fd);
return 0;
}