一、linux中的文件
在 Linux 系统中,一切都是“文件”:普通文件、驱动程序、网络通信等等。所有的操作,都是通过“文件 IO”来操作的。所以,很有必要掌握文件操作的常用接口。
二、文件IO
在 Linux上操作文件时,有两套函数:标准IO、系统调用I0。
标准I0相关函数:fopen/fread/fwrite/fseek/fflush/fclose等。
系统调用I0的相关函数:open/read/write/lseek/fsync/close。
二者区别:
①系统调用IO函数每次操作都会进入内核。
②标准IO函数引入了用户Buffer,先访问一次内核将数据存入Buffer,然后进行读写操作,不会频繁访问内核。但其底层仍然使用系统调用IO函数。
三、主函数
3.1 主函数
int main(int argc ,char **argv)
argc:参数个数
argv:参数数组
例子:
./open 1.txt
则argc=2,argv[0]=./open;argv[1]=1.txt
3.2 函数查看方法
Linux 下有 3 大帮助方法:help、man、info。
help 只能用于查看某个命令的用法,而 man 手册既可以查看命令的用法,还可以查看函数的详细介绍等等。它含有 9 大分类
四、open函数:
(1) 在Ubuntu终端上使用“man 2 open”指令可以查看open函数原型及所需要包含的头文件。
(2) 包含的头文件:#include <sys/types.h>、#include <sys/stat.h>、#include <fcntl.h>
(3) 原型:①int open(const char *pathname, int flags);
②int open(const char *pathname, int flags, mode_t mode);
相关形参解释如下:
(4) 功能:打开或创建一个文件。
4.1 open函数打开文件实验:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
int main(int argc ,char **argv)
{
if(argc!=2)
{
printf("Usage: %s <file>\n",argv[1]);
return -1;
}
int fd;
fd=open(argv[1],O_RDWR);
if(fd<0)
{
printf("无法打开 %s\n",argv[1]);
// printf("errno = %d\n", errno); //错误代码
//printf("err=%s\n",strerror(errno)); //将错误代码翻译成具体信息
perror("err"); //打印错误信息
}
else
{
printf("open success\n");
}
close(fd);
return 0;
}
errno是一个全局变量,存放上一个错误码,使用时需要包含头文件<errno.h>。
perror()函数可以打印错误信息。
在Usage: %s <file>中
<>表示文件不可省略[]表示可以省略
4.2 测试执行
gcc -o test test.c
./test 1.txt &
ps //查询当前运行的进程状态
cd /proc/4339 //proc主要用于获取系统和进程信息,4339是进程号
kill -9 4339 //杀死进程
&:程序在后台运行
ps:命令查询当前运行的进程状态
kill -9 进程号 : 用来杀死进程
4.3 open函数创建文件:
int open(const char *pathname, int flags, mode_t mode);
flags:表示打开文件所采取的操作
mode:表示创建的文件的权限
注意:打开已经存在的文件,无法修改其权限;新建文件的时候可以指定权限
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int fd;
if (argc != 2)
{
printf("Usage: %s <file>\n", argv[0]);
return -1;
}
/*
打开文件,可读可写,存在则截断为0,
不存在则创建权限为777的文件,记得umask,
所以最终权限只有775
*/
fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0777);
if (fd < 0)
{
perror("err");
}
else
{
printf("fd = %d\n", fd);
}
close(fd);
return 0;
}
1.调用open函数时,flags处可以将多个参数以|进行分隔。
2.umask能够设置文件在创建时mode的掩码。
例如,使用umask命令得到"0002"的结果,那么文件创建时模式掩码为"000 000 000 010"。
3.使用open创建文件时最终的权限结果为:"mode & ~umask"。因此,当指定创建文件权限为777时,111 111 111 & ~(111 111 101) = 111 111 101,所以创建文件的权限结果为775。
五、write函数
5.1 函数原型
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
作用:将buf 中的 count 字节数据写入指定文件描述符的文件中。
fd:指定要写入的文件描述符
buf:缓冲区,一般是一个数组,指要写入文件的对象
count:要写入的实际字节数
返回值:
成功时: 返回实际写入的字节数。
失败时: 返回 -1。
5.2 write函数写入多个字符串
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int fd;
int len;
if (argc < 3 )
{
printf("Usage: %s <file> string1 ...\n", argv[0]);
return -1;
}
fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0777);
if (fd < 0)
{
perror("err");
}
else
{
printf("fd = %d\n", fd);
}
for(int i = 2;i < argc; i++)
{
len = write(fd, argv[i], strlen(argv[i]));
if(len != strlen(argv[i]))
{
perror("write");
break;
}
write(fd , "\r\n" , 2);
}
close(fd);
return 0;
}
5.3 在现存内容中间插入新字符串(write + lseek函数)
5.3.1 函数原型
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
作用:重新定位读/写文件偏移
fd:指定要偏移的文件描述符
offset:文件偏移量
whence:开始添加偏移 offset 的位置
SEEK_SET,offset相对于文件开头进行偏移
SEEK_CUR,offset相对文件当前位置进行偏移
SEEK_END,offset相对于文件末尾进行偏移
5.3.2 代码实现
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int fd;
int len;
if (argc < 3 )
{
printf("Usage: %s <file> string1 ...\n", argv[0]);
return -1;
}
fd = open(argv[1], O_RDWR | O_CREAT, 0777);
if (fd < 0)
{
perror("err");
}
else
{
printf("fd = %d\n", fd);
}
lseek(fd,3,SEEK_SET);
write(fd,argv[2],strlen(argv[2]));
close(fd);
return 0;
}
5.3.3运行结果
插入的字符串出现在偏移量的后面,但是会覆盖原来的字符 。
六、read函数
6.1 函数原型
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
作用:从给定的文件描述符指定的文件中,读取 count 个字节的数据,存放至 buf中。
fd:指定要读写的文件描述符
buf:缓冲区,一般是一个数组,用于存放读取的内容
count:一次要读取的最大字节数
返回值:
- 正值:成功读取的字节数,可能小于请求的字节数。
- 0:表示到达文件末尾。
- -1:出现错误。
6.2 read函数读取文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int fd;
int len;
unsigned char buf[100];
if (argc !=2 )
{
printf("Usage: %s <file> \n", argv[0]);
return -1;
}
fd = open(argv[1], O_RDONLY);
if (fd < 0)
{
perror("open");
}
else
{
printf("fd = %d\n", fd);
}
while(1)
{
len = read(fd,buf,sizeof(buf)-1);
if(len == -1)
{
perror("read");
close(fd);
return -1;
}
else if(len == 0)
{
break;
}
else
{
buf[len] = '\0';
printf("%s\n",buf);
}
}
close(fd);
return 0;
}
6.3遇到的错误
len = read(fd,buf,sizeof(buf)-1);
if(len == -1)
{
perror("read");
close(fd);
return -1;
}
else
{
buf[len] = '\0';
printf("%s\n",buf);
}
1.在写sizeof(buf)-1这行代码,写成了sizeo(buf-1),相当于sizeo(-1),也就是一个指针的大小,8个字节,导致输出一直只有8个字母
2.一般使用小缓存buf,加上循环多次read,提高效率。