Linux文件I/O
文件结构
创建目录
- 系统分配一个inode和至少一个block
- 该inode记录该目录属性,指向block
- 该block记录该目录下相关联的文件或目录的inode编号和名字
创建文件
- 系统分配至少一个inode和与文件大小相对应数量的一个block
- 该inode记录该目录属性,指向block
读取文件流程
- 读取目录或文件
- 例读取
/home
下的test.c
- 首先根目录的inode编号固定为0
- 通过根目录的inode编号找到其inode结构体,通过inode结构体找到其block
- 目录的block内容为该目录下的文件inode号与文件名字的表格
- 根据文件
test.c
名字在目录的block找到test.c
对应的inode编号,通过该编号就可以找到test.c
的内容,进而完成文件内容读取
文件的基本操作
概念补充
文件描述符
文件权限
系统调用
open系统调用
接口代码
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int open(const char* pathname,int flags,[mode_t mode]);
参数解释
- pathname: 字符指针,指向文件路径及文件名
- flags: 整数形参,定义以何种方式访问文件
- O_RDONLY: 只读打开文件
- O_WRONLY: 只写打开文件
- O_RDWR: 读写打开文件
- O_CREATE: 按
mode
中给出的访问方式创建文件 - …
- mode: 可选参数,只有
flags为O_CREAT
才生效,表示给文件赋予何种权限- 常用数字代表如:0644 ==>
-rw-r--r--
- 常用数字代表如:0644 ==>
实例代码
//参考下方汇总
write系统调用
接口代码
#include <unistd.h>
ssize_t write(int fileds,const void* buffer,size_t n);
参数解释
- fileds: 文件描述符
- buffer: 缓冲区
- n: 从缓冲区写入到文件的字节数
实例代码
#include <unistd.h>
#include <stdlib.h>
int main()
{
if((write(1,"Here is come data\n",18)) != 18)
write(2,"A write error has occurred on file description 1\n",46);
exit(0);
}
read系统调用
接口代码
#include <unistd.h>
ssize_t read(int filedes,void* buffer,size_t nbytes);
参数解释
- filedes: 之前open或create调用返回的文件描述符
- buffer: 指向数组或结构的指针
- nbytes: 从文件中读取的字节数
实例代码
#include <unistd.h>
#include <stdlib.h>
int main()
{
char buffer[128];
int nread;
nread = read(0,buffer,128);
if(nread == -1)
write(2,"A read error has occurred\n",26);
if((write(1,buffer,nread)) != read)
write(2,"A write error has occurred\n",27);
exit(0);
}
create系统调用
接口代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int creat(const char* pathname,mode_t mode);
参数解释
- pathname: 文件路径名
- mode: 为赋予创建文件的访问权限
实例代码
//参考下方汇总
close系统调用
接口代码
#include <unistd.h>
int close(int filedes);
参数解释
- filedes: 文件描述符
实例代码
//参考下方汇总
综合应用
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
int main()
{
int in,out;
// 源文件路径
char* inPath = "test.txt";
// 目标文件路径
char* outPath = "out.txt";
// 定义缓冲区
char buffer[1024] ={0};
// 以只读方式打开文件
in = open(inPath,O_RDONLY);
// 以rw-r--r--打开文件
out = open(outPath,0644);
// 输出对应文件描述符
printf("in = %d,out = %d\n",in,out);
if(in == -1) exit(1);
// 如果目标文件不存在,就创建
if(out == -1) out = creat(outPath,0644);
// 每次成功读取字节数
ssize_t nread;
while((nread = read(in,buffer,sizeof(buffer))) > 0)
{
// 将缓冲区字节数写入目标文件
write(out,buffer,nread);
printf("nread = %ld\n",nread);
printf("%s\n",buffer);
}
close(in);
close(out);
exit(0);
}
文件状态信息
fstat系统调用
接口代码
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int fstat(int fildes, struct stat* buf);
参数解释
- filedes: 文件描述符
- stat: 文件状态信息结构体
stat系统调用
接口代码
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int stat(const char* path, struct stat* buf);
lstat系统调用
接口代码
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
int lstat(const char* path, struct stat* buf);
lseek系统调用
接口代码
#include <unistd.h>
#include <sys/types.h>
off_t lseek(int fileds, off_t offset,int start_flag);
参数解释
- filedes: 参数为文件描述符
- offset: 参数为表示新位置相对起始位置的字节数
- start_flag:
- offset从文件的起始文件开始算,通常值为0
- offset相对文件读写的当前位置而言,通常值为1
- offset相对文件尾而言,通常值为2
perror函数
接口代码
#include <stdio.h>
void perror(const char *s);
参数解释
- 如果s不为空,错误信息会先增加字符串s的内容,再打印错误信息
实例代码
#include <stdio.h>
int sample
{
int fd;
fd = open("file",O_RDONLY);
if(fd == -1)
{
perror("Cannot open file");
return;
}
}
//运行结果
Cannot open file: No such file or directory
Cannot open file: Interrupted system call
chmod系统调用
接口代码
**#include <sys/stat.h>**
// 修改文件或目录的访问权限
int chmod(const char *path, mode_t mode);
// 改变文件或目录的所有者或组
int chown(const char *path, uid_t owner, gid_t group);
参数解释
- path: 指定被修改权限的文件
- mode: 修改的权限设置
- owner: 用户id
- group: 组id
实例代码
#include <unistd.h>
#include <sys/stat.h>
int main()
{
chmod("abc",04764);
chmod("bcd",S_ISUID|S_IRWXU|S_IRGRP|S_IWGRP|S_IROTH);
chmod("abc",1000,1000);
return 0;
}
文件链接
unlike函数
接口代码
#include <unistd.h>
int unlink(const char *pathname);
link函数
接口代码
#include <unistd.h>
int link(const char *path1, const char *path2);
symlink函数
接口代码
#include <unistd.h>
int symlink(const char *path1, const char *path2);
目录操作
opendir函数
接口代码
#include <sys/types.h>
#include <dirent.h>
DIR* opendir(const char* dirname);
readdir函数
接口代码
#include <sys/types.h>
#include <dirent.h>
struct dirent* readdir(DIR* drip);
参数解释
- drip: 目录流指针
实例代码
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
int main()
{
DIR* dir_ptr;
struct dirent* dirrntp;
// 打开目录
if((dir_ptr == opendir("/home")) == NULL)
perror("can not open /home");
// 循环读出该目录下每一项
while((direntp == readdir(dir_ptr)) != NULL)
printf("%s\n",direntp->d_name);
close(dir_ptr);
return 0;
}
telldir函数
接口代码
#include <sys/types.h>
#include <dirent.h>
long int telldir(DIR* dirp);
参数解释
- drip: 目录流指针
实例代码
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
int main()
{
DIR* dir_ptr;
struct dirent* dirrntp;
int dir_loc;
// 打开目录
if((dir_ptr == opendir("/home")) == NULL)
perror("can not open /home");
// 循环读出该目录下每一项
while((direntp == readdir(dir_ptr)) != NULL)
{
printf("%s\n",direntp->d_name);
// 获取当前文件流位置
dir_loc = telldir(dir_ptr);
printf("%d\n",dir_loc);
}
close(dir_ptr);
return 0;
}
seekdir函数
接口代码
#include <sys/types.h>
#include <dirent.h>
void seekdir(DIR* dirp,long int loc);
参数解释
- drip: 目录流指针
- loc: 用来设置指针位置,通过telldir调用获得
closedir函数
接口代码
#incldue <sys/types.h>
#include <dirent.h>
int closedir(DIR* dirp);
综合实例
// Linux下输入文件
$ gcc -o findDir findDir.c
$ ./findDir ../
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <locale.h>
#include <stdint.h>
#include <string.h>
#define LINES 200
#define NAMELEN 1024
char fileDir[LINES][NAMELEN];
int front = 0;// 队头指针
int rear = 0;// 队尾指针
char* out = "./ans.txt";// 输出文件
FILE* fq;
void insert(char* str)
{
strcpy(fileDir[rear++],str);
if(rear % LINES == 0)rear = 0;
}
char* pop()
{
char* str = fileDir[front];
front++;
if(front % LINES == 0)front = 0;
return str;
}
int empty()
{
return rear == front ? 1 : 0;
}
void findDir(char* fileNamePath)
{
DIR* dir;
struct dirent* dirDetails;
struct stat buff;
if((dir = opendir(fileNamePath)) == NULL)
{
printf("%s文件目录不存在\n",fileNamePath);
return;
}
// 队列开始,循环判断每一项
while((dirDetails = readdir(dir)) != NULL)
{
char* str1 = ".";
char* str2 = "..";
int ren[2];
ren[0] = strcmp(dirDetails->d_name,str1);
ren[1] = strcmp(dirDetails->d_name,str2);
// 过滤当前目录.和父目录..
if(ren[0] == 0 || ren[1] == 0 )continue;
// 过滤隐藏文件
if(dirDetails->d_name[0] == '.')continue;
char fullPath[1024] = {0};
strcpy(fullPath,fileNamePath);
int len = strlen(fullPath);
// 这一步???
if(fullPath[len-1] != '/')
{
fullPath[len++] = '/';
fullPath[len] = 0;
}
// 拼接得到完整目录文件
strcat(fullPath,dirDetails->d_name);
if(stat(fullPath,&buff) == -1)
{
printf("00000000000000,fullPath = %s\n",fullPath);
continue;
}
else
{
if(S_ISDIR(buff.st_mode))
{
strcat(fullPath,"/");// 构建成目录
insert(fullPath);// 加入队列
}
else fprintf(fq,"%s\n",fullPath);// 输出文件内容
}
}
closedir(dir);// 关闭目录流
if(!empty())
{
char* dirPath = pop();
findDir(dirPath);
}
}
int main(int argc,char* argv[])
{
fq = fopen(out,"w");
if(argc != 2)
{
printf("error卒\n");
exit(-1);
}
findDir(argv[1]);
fclose(fq);
printf("结束\n");
exit(0);
}
文件入口getopt函数
函数原型
#include <unistd.h>
int getopt(int argc, char * const argv[], const char *optstring);
参数
argc
: 命令行参数的个数,通常由main
函数传递给getopt
。argv
: 命令行参数的数组,通常由main
函数传递给getopt
。optstring
: 一个字符串,定义了有效选项及其参数的格式。每个字符代表一个选项:- 如果字符后面跟有冒号(如
a:
),则表示这个选项需要一个参数。 - 如果字符后面没有冒号(如
b
),则表示这个选项不需要参数。 - 如果
optstring
中包含?
,getopt
会在遇到无效选项时返回?
。
- 如果字符后面跟有冒号(如
返回值
- 选项字符:
getopt
会返回当前处理的选项字符。如果选项需要一个参数,optarg
会指向这个参数的字符串。 -1
: 当没有更多选项可以处理时,getopt
返回-1
。
全局变量
optarg
: 指向当前选项的参数。如果当前选项需要一个参数(如-o value
中的value
),optarg
指向该参数的字符串。如果选项不需要参数,则optarg
为NULL
。optind
: 在解析选项后,指向argv
中下一个未处理的参数的位置。它可以用来访问剩余的命令行参数。opterr
: 用于控制getopt
是否输出错误消息。如果opterr
被设置为 0,getopt
将不会输出错误消息。默认情况下,它的值是 1。optopt
: 当遇到无效的选项时,optopt
被设置为无效选项字符。它可以用于自定义错误消息。
示例代码
下面是一个使用 getopt
的简单示例:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int opt;
while ((opt = getopt(argc, argv, "a:b::c")) != -1) {
switch (opt) {
case 'a':
printf("Option -a with value %s\n", optarg);
break;
case 'b':
printf("Option -b with value %s\n", optarg ? optarg : "none");
break;
case 'c':
printf("Option -c\n");
break;
case '?':
if (optopt == 'b') {
printf("Option -%c requires an argument.\n", optopt);
} else {
printf("Unknown option `-%c'.\n", optopt);
}
return 1;
default:
abort();
}
}
// Print remaining arguments
for (int i = optind; i < argc; i++) {
printf("Remaining argument: %s\n", argv[i]);
}
return 0;
}
解释
opt = getopt(argc, argv, "a:b::c")
:- 处理选项
-a
,该选项需要一个参数。 - 处理选项
-b
,该选项可以有一个可选参数(冒号后面有两个冒号表示可选)。 - 处理选项
-c
,该选项不需要参数。
- 处理选项
switch (opt)
:case 'a':
处理-a
选项及其参数,打印选项和参数。case 'b':
处理-b
选项及其参数,如果参数缺失,使用默认值"none"
。case 'c':
处理-c
选项,不需要参数。case '?':
处理无效选项或缺少参数的情况。
for (int i = optind; i < argc; i++)
:- 遍历并打印所有剩余的命令行参数。