Linux文件I/O

Linux文件I/O

文件结构

inode: 记录存储文件的各属性

block: 存储文件内容

在这里插入图片描述

创建目录

  1. 系统分配一个inode和至少一个block
  2. inode记录该目录属性,指向block
  3. block记录该目录下相关联的文件或目录的inode编号和名字

创建文件

  1. 系统分配至少一个inode和与文件大小相对应数量的一个block
  2. inode记录该目录属性,指向block

如果一个目录中的文件数太多,以至于1个block容纳不下这么多文件时,Linux的文件系统会为该目录分配更多的block

读取文件流程

  1. 读取目录或文件
  2. 例读取/home下的test.c
  3. 首先根目录的inode编号固定为0
  4. 通过根目录的inode编号找到其inode结构体,通过inode结构体找到其block
  5. 目录的block内容为该目录下的文件inode号与文件名字的表格
  6. 根据文件test.c名字在目录的block找到test.c对应的inode编号,通过该编号就可以找到test.c的内容,进而完成文件内容读取

文件的基本操作

概念补充

文件描述符

一个运行中的程序被称为一个进程,他有一些与之对应的文件描述符,文件描述符是一些小的,正整数数值的数,通过他们可以访问打开的文件和设备

  • 文件描述符是打开文件进程与文件之间的连接
  • 文件描述符是一个正整数的值
  • 同时打开几个文件,描述符不同
  • 一个文件打开多次,描述符也不同
文件权限

在这里插入图片描述

系统调用

open系统调用
  • 建立一条到文件或设备的访问路径
  • 打开或创建文件
  • 执行失败返回 -1

Linux下open()函数打开文件,通过定义 int fd = open()函数,

获取文件描述符,背后关系,从文件指针数组中获得尚未分配的指针,fd即为对应的数组下标,打开文件是通过fd来打开文件,对应数组下标一般是从3开始,前三个依次为stdin,stdout,stderr.

此时,再打开新的文件,获取新的文件描述符,就会扫描该数组,找到未使用的,使用它

接口代码
#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--
实例代码
//参考下方汇总
write系统调用
  • 把缓冲区前n个字节写入与文件描述符filedes相关联的文件
  • 返回值是实际写出的字节数
接口代码
#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系统调用
  • 从与文件描述符fildes相关联的文件里读入nbytes个字节的数据,并把他们放到buffer数据区中
  • 返回值是实际写入的字节数
  • 执行失败返回 -1
接口代码
#include <unistd.h>

ssize_t read(int filedes,void* buffer,size_t nbytes);
参数解释
  • filedes: 之前opencreate调用返回的文件描述符
  • 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系统调用
  • 终止一个文件描述符与文件的关联
  • 文件描述符可以被重新使用
  • 一个运行的程序一次性能打开的文件数量是有限的
  • 执行成功返回 0
接口代码
#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);
}

文件状态信息

stat结构体成员

  • st_mode 文件权限和文件类型信息
    • 掩码:
      • S_IFMT 文件类型
      • S_IRWXU 所属者的读/写/执行权限
      • S_IRWXG 所属组的读/写/执行权限
      • S_IRWXO 其他用户的读/写/执行权限
      • S_ISBLK 测试是否是特殊的块设备文件
      • S_ISCHR 测试是否是特殊的字符设备文件
      • S_ISDIR 测试是否是目录
      • S_ISFIFO 测试是否是FIFO设备
      • S_ISREG 测试是否是普通文件
      • S_ISLNK 测试是否是符号链接文件
  • st_ino 与该文件关联的inode
  • st_dev 保存文件的设备
  • st_uid 文件属性的UID号
  • st_gid 文件属性的GID号
  • st_atime 文件上一次被访问的时间
  • st_ctime 文件的权限、所有者、组或内容上一次改变的时间
  • st_mtime 文件的内容上一次被修改的时间
  • st_nlink 该文件上硬连接的个数
fstat系统调用
  • 返回一个打开文件描述符关联着的文件状态信息,这些信息被写到

    stat结构里

接口代码
#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函数
  • 删除链接
    • 对应链接数减一,如果为0,则删除
  • 是删除该文件所在目录的一条目录数据项,即删除目录文件中的一个文件名及其inode号
接口代码
#include <unistd.h>

int unlink(const char *pathname);
link函数
  • 创建链接(硬链接)
  • ln a b : a,b共享同一个inode,存储信息,可以看作同一个文件的两个名字
接口代码
#include <unistd.h>

int link(const char *path1, const char *path2);
symlink函数
  • 创建符号链接(软链接)
  • ln -s a b : b相当于a的快捷方式,这是两个文件
接口代码
#include <unistd.h>

int symlink(const char *path1, const char *path2);

目录操作

opendir函数
  • 打开目录
  • 返回指向目录流的指针DIR
接口代码
#include <sys/types.h>
#include <dirent.h>

DIR* opendir(const char* dirname);
readdir函数
  • 返回一个指向结构的指针
  • dirent结构体
struct dirent
{
    long d_ino;// 索引节点inode号
    off_t d_off;// 在目录文件中的偏移
    unsigned short d_reclen;// 文件名长
    unsigned char d_type;// 文件类型
    char d_name [NAME_MAX+1];// 文件名
}
接口代码
#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函数
  • 设置目录流dirp的目录指针
接口代码
#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);
综合实例

求文件目录下的所有文件

知识点:

  • “.”: 表示当前目录
  • “…”: 表示父目录
  • 遍历时应当排除

strcmp(char* str1,char* str2): 比较两个字符串是否相等

strcpy(char* str1,char* str2): 将str2赋值给str1

strcat(char* str1,char* str2): 将str2拼接到str1后

// 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);
参数
  1. argc: 命令行参数的个数,通常由 main 函数传递给 getopt
  2. argv: 命令行参数的数组,通常由 main 函数传递给 getopt
  3. optstring: 一个字符串,定义了有效选项及其参数的格式。每个字符代表一个选项:
    • 如果字符后面跟有冒号(如 a:),则表示这个选项需要一个参数。
    • 如果字符后面没有冒号(如 b),则表示这个选项不需要参数。
    • 如果 optstring 中包含 ?getopt 会在遇到无效选项时返回 ?
返回值
  • 选项字符: getopt 会返回当前处理的选项字符。如果选项需要一个参数,optarg 会指向这个参数的字符串。
  • -1: 当没有更多选项可以处理时,getopt 返回 -1
全局变量
  • optarg: 指向当前选项的参数。如果当前选项需要一个参数(如 -o value 中的 value),optarg 指向该参数的字符串。如果选项不需要参数,则 optargNULL
  • 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++):
    • 遍历并打印所有剩余的命令行参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值