LV04-04-文件操作-文件属性与目录

本文主要是C语言——文件属性与目录的基本操作的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows windows11
Ubuntu Ubuntu16.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
SecureCRT Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日
开发板 正点原子 i.MX6ULL Linux阿尔法开发板
uboot NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)
linux内核 linux-4.15(NXP官方提供)
STM32开发板 正点原子战舰V3(STM32F103ZET6)
点击查看本文参考资料
参考方向 参考原文
------
点击查看相关文件下载
--- ---

一、目录访问

1. 两个结构体

1.1 DIR

DIR 结构体与 FILE 类似,只是 DIR 存放的是目录相关信息的成员,它的结构,说实话,我只找到了它在 dirent.h 头文件中的声明,详细的结构还是网上搜到的:

1
2
3
4
5
6
7
8
9
10
11
12
struct __dirstream
{
void *__fd;
char *__data;
int __entry_data;
char *__ptr;
int __entry_ptr;
size_t __allocation;
size_t __size;
__libc_lock_define (, __lock)
};
typedef struct __dirstream DIR;

1.2 dirent

dirent 结构体不仅仅指向目录,还可以指向目录中的具体文件。直接 man 看不到它的定义,使用 man readdir 时可以看到这个结构体定义:

1
2
3
4
5
6
7
8
struct dirent 
{
ino_t d_ino; /* Inode number 索引节点号*/
off_t d_off; /* Not an offset; see below */
unsigned short d_reclen; /* Length of this record */
unsigned char d_type; /* Type of file; not supported by all filesystem types */
char d_name[256]; /* Null-terminated filename 文件名,最长255字符*/
};

2. 基本函数

2.1 opendir()

在 linux 下可以使用 man 3 opendir 命令查看该函数的帮助手册。

1
2
3
4
5
6
/* 需包含的头文件 */
#include <sys/types.h>
#include <dirent.h>

/* 函数声明 */
DIR *opendir(const char *name);

【函数说明】该函数打开参数 name 指定的目录, 并返回 DIR* 形态的目录流,。

【函数参数】

  • name :需要打开的目录名称(可包括路径名)。

【返回值】返回值是 DIR * 类型,成功时返回目录流指针;出错时返回 NULL 。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
7
/* 需要包含的头文件 */
#include <sys/types.h>
#include <dirent.h>

/* 至少应该有的语句 */
DIR * dp;
dp=opendir("/home/hk");

【注意事项】 none

2.2 readdir()

在 linux 下可以使用 man 3 readdir 命令查看该函数的帮助手册。

1
2
3
4
5
/* 需包含的头文件 */
#include <dirent.h>

/* 函数声明 */
struct dirent *readdir(DIR *dirp);

【函数说明】该函数读取目录流中的内容。

【函数参数】

  • dirp :已打开目录的目录流指针变量。

【返回值】返回值是 struct dirent * 类型, struct dirent 是用来描述目录流中一个目录项的结构体类型。成功时返回目录流 dirp 中的下一个目录项(下个目录进入点);出错或到末尾时返回 NULL 。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
7
8
9
/* 需要包含的头文件 */
#include <dirent.h>

/* 至少应该有的语句 */
DIR * dp;
struct dirent *dt;
dp=opendir("/home/hk");
dt = readdir(dp));
printf("%s\n", dt->d_name);

【注意事项】 none

2.3 closedir()

在 linux 下可以使用 man 3 closedir 命令查看该函数的帮助手册。

1
2
3
4
5
6
/* 需包含的头文件 */
#include <sys/types.h>
#include <dirent.h>

/* 函数声明 */
int closedir(DIR *dirp);

【函数说明】该函数关闭已打开的目录文件。

【函数参数】

  • dirp :已打开目录的目录流指针变量。

【返回值】返回值是 int 类型。成功时返回 0 ;出错时返回 EOF 。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
/* 需要包含的头文件 */
#include <sys/types.h>
#include <dirent.h>

/* 至少应该有的语句 */
closedir(dp);

【注意事项】 none

3. 使用实例

点击查看实例
test.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/** =====================================================
* Copyright © hk. 2022-2022. All rights reserved.
* File name: 01open_close_Dir.c
* Author : fanhua
* Description: 文件IO——目录打开关闭
* ======================================================
*/
/* 头文件 */
#include <stdio.h>
#include <dirent.h>

int main(int argc,char*argv[])
{
/* 定义一个目录结构体指针变量 */
DIR * dp;
/* 定义一个目录属性结构体变量 */
struct dirent *dt;
/* 1. 打开文件 */
dp=opendir("/home/hk");
if(dp < 0)
{
perror("opendir");
return -1;
}
else
{
perror("opendir");
}
/* 2. 打印目录名称 */
while((dt = readdir(dp)) != NULL)
{
printf("%s\n", dt->d_name);
}
closedir(dp);
}

在终端执行以下命令编译程序:

1
2
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

二、文件访问

1. 文件权限

Linux/Unix 的文件调用权限分为三级 : 文件所有者( Owner )、用户组( Group )、其它用户( Other Users )。只有文件所有者和超级用户可以修改文件或目录的权限。

image-20220511092127123
file type - 代表文件
d 代表目录
Owner
Group
Other Users
r read(可读)
w write(可写)
x execute(可执行)
- 无权限

例如:

image-20220511092326302

对于文件的权限还可以使用三位 8 进制数表示:

image-20220511092515505

2. 基本函数

2.1 chmod()

在 linux 下可以使用 man 2 chmod命令查看该函数的帮助手册。

1
2
3
4
5
/* 需包含的头文件 */
#include <sys/stat.h>

/* 函数声明 */
int chmod(const char *pathname, mode_t mode);

【函数说明】该函数用于修改文件权限。

【函数参数】

  • pathname :需要修改权限的文件的文件名(可以包含路径)。
  • mode :要修改的权限(一般为四位十六进制数,后三位代表要修改的权限)。

【返回值】返回值是 int 类型,成功时返回 0 ;出错时返回 EOF 。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
7
8
9
/* 需要包含的头文件 */
#include <sys/stat.h>

/* 至少应该有的语句 */
int fd;
char buff[32]={0};
int ret;
fd = open("02read_write.txt",O_RDWR | O_CREAT | O_APPEND, 0666);
ret = read(fd, buff, 32);

【注意事项】

(1)在虚拟机中使用 Linux 时,修改共享文件夹 /mnt/hgfs/file_name 中的文件权限可能会无效,因为这里边的文件同时也是 Windows 下的文件。

(2)可以在终端使用: ll file_name 命令来单独查看某个文件的权限 。

2.2 fchmod()

在 linux 下可以使用 man 2 fchmod命令查看该函数的帮助手册。

1
2
3
4
5
/* 需包含的头文件 */
#include <sys/stat.h>

/* 函数声明 */
int fchmod(int fd, mode_t mode);

【函数说明】该函数修改文件描述符 fd 指向文件的文件权限。

【函数参数】

  • fd :文件描述符。
  • mode :要修改的权限(一般为四位十六进制数,后三位代表要修改的权限)。

【返回值】 返回值是 int 类型,成功时返回 0 ;出错时返回 EOF 。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
/* 需要包含的头文件 */
#include

/* 至少应该有的语句 */

【注意事项】 none

3. 使用实例

点击查看实例
test.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/** =====================================================
* Copyright © hk. 2022-2022. All rights reserved.
* File name: 02file_permissions.c
* Author : fanhua
* Description: 文件IO——文件权限修改
* ======================================================
*/
/* 头文件 */
#include <stdio.h>
/* chmod 函数 */
#include <sys/stat.h>

void test_chmod();

int main(int argc,char*argv[])
{
test_chmod();
return 0;
}

/**
* @Function: int chmod(const char *path, mode_t mode);
* @Description: 成功时返回0;出错时返回EOF
*/
void test_chmod()
{
int ret;
ret = chmod("/home/hk/3Test/test.vim",0444);
if(ret < 0)
{
perror("chmod");
return;
}
}

在终端执行以下命令编译程序:

1
2
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序

三、文件属性

1. stat 结构体

我们可以在linux内核源码的 include/uapi/asm-generic/stat.h中找到这个结构体。 这个结构体成员是文件的各种信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct stat {
unsigned long st_dev; /* Device. */
unsigned long st_ino; /* File serial number. */
unsigned int st_mode; /* File mode. */
unsigned int st_nlink; /* Link count. */
unsigned int st_uid; /* User ID of the file's owner. */
unsigned int st_gid; /* Group ID of the file's group. */
unsigned long st_rdev; /* Device number, if device. */
unsigned long __pad1;
long st_size; /* Size of file, in bytes. */
int st_blksize; /* Optimal block size for I/O. */
int __pad2;
long st_blocks; /* Number 512-byte blocks allocated. */
long st_atime; /* Time of last access. */
unsigned long st_atime_nsec;
long st_mtime; /* Time of last modification. */
unsigned long st_mtime_nsec;
long st_ctime; /* Time of last status change. */
unsigned long st_ctime_nsec;
unsigned int __unused4;
unsigned int __unused5;
};

其中主要就是st_mode这个成员,它包含了文件的类型和存取的权限。 st_mode 中有 9 位是代表文件的权限的,我们可以将 1 左移再与 st_mode 做 & 运算,即可判断每位为 1 或者为 0 。

image-20220511103048184
点击查看 st_mode 文件权限部分
S_ISUID04000set-user-ID bit
S_ISGID02000set-group-ID bit
S_ISVTX01000sticky bit
S_IRWXU00700owner has read, write, and execute permission
S_IRUSR00400owner has read permission
S_IWUSR00200owner has write permission
S_IXUSR00100owner has execute permission
S_IRWXG00070group has read, write, and execute permission
S_IRGRP00040group has read permission
S_IWGRP00020group has write permission
S_IXGRP00010group has execute permission
S_IRWXO00007others (not in group) have read, write, and execute permission
S_IROTH00004others have read permission
S_IWOTH00002others have write permission
S_IXOTH00001others have execute permission

系统提供了一些宏,可以用这些宏来识别 st_mode 的值,以判定文件的类型和权限。我们可以直接通过宏的返回值判断文件的类型,也可以使用 st_mode & S_IFMT 的值来判断文件类型,例如,

1
2
3
4
5
stat(pathname, &sb);
if ((sb.st_mode & S_IFMT) == S_IFREG)
{
/* Handle regular file */
}
点击查看判断 st_mode 文件类型的宏
S_IFMT0170000文件类型位字段的位掩码
S_IFSOCK0140000S_ISSOCK(st_mode) 是否SOCKET文件,是返回1,否则返回0
S_IFLNK 0120000S_ISLNK(st_mode) 是否链接文件,是返回1,否则返回0
S_IFREG 0100000S_ISREG(st_mode) 是否常规文件,是返回1,否则返回0
S_IFBLK 0060000S_ISBLK(st_mode) 是否块设备,是返回1,否则返回0
S_IFDIR 0040000S_ISDIR(st_mode) 是否目录,是返回1,否则返回0
S_IFCHR 0020000S_ISCHR(st_mode) 是否字符设备,是返回1,否则返回0
S_IFIFO 0010000S_ISFIFO(st_mode) 是否FIFO文件,是返回1,否则返回0

2. 基本函数

2.1 stat()

在 linux 下可以使用 man 2 stat 命令查看该函数的帮助手册。

1
2
3
4
5
6
7
/* 需包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

/* 函数声明 */
int stat(const char *pathname, struct stat *statbuf);

【函数说明】该函数用于获取文件信息。

【函数参数】

  • pathname :文件名(可以包含路径)。
  • statbuf : struct stat 类型的结构体,用于存放文件属性信息。

【返回值】返回值是 int 类型,成功时返回 0 ;出错时返回 EOF 。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
7
8
9
/* 需要包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

/* 至少应该有的语句 */
struct stat file_attr;
int ret;
ret = stat("03file_attributes.c", &file_attr);

【注意事项】 如果 pathname 是符号链接,那么 stat 获取的是目标文件的属性。

2.2 lstat()

在 linux 下可以使用 man 2 lstat 命令查看该函数的帮助手册。

1
2
3
4
5
6
7
/* 需包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

/* 函数声明 */
int lstat(const char *pathname, struct stat *statbuf);

【函数说明】该函数用于获取文件信息。

【函数参数】

  • pathname :文件名(可以包含路径)。
  • statbuf : struct stat 类型的结构体,用于存放文件属性信息。

【返回值】返回值是 int 类型,成功时返回 0 ;出错时返回 EOF 。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
7
8
9
/* 需要包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

/* 至少应该有的语句 */
struct stat file_attr;
int ret;
ret = lstat("03file_attributes.c", &file_attr);

【注意事项】 如果 pathname 是符号链接,那么 lstat 获取的是链接文件的属性。

2.3 fstat()

在 linux 下可以使用 man 2 fstat 命令查看该函数的帮助手册。

1
2
3
4
5
6
7
/* 需包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

/* 函数声明 */
int fstat(int fd, struct stat *statbuf);

【函数说明】该函数可以由文件描述符取得文件状态。

【函数参数】

  • fd :已经打开文件的文件描述符。
  • statbuf : struct stat 类型的结构体,用于存放文件属性信息。

【返回值】返回值是 int 类型,成功时返回 0 ;出错时返回 EOF 。

【使用格式】一般情况下基本使用格式如下:

1
2
3
4
5
6
7
8
9
10
11
/* 需要包含的头文件 */
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

/* 至少应该有的语句 */
struct stat file_attr;
int ret;
int fd;
fd = open( "03file_attributes.c", O_RDONLY);
ret = fstat(fd, &file_attr);

【注意事项】 none

3. 使用实例

点击查看实例
test.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/** =====================================================
* Copyright © hk. 2022-2022. All rights reserved.
* File name: 03file_attributes.c
* Author : fanhua
* Description: 文件IO——文件属性获取
* ======================================================
*/
/* 头文件 */
#include <stdio.h>
/* rstat lstat fstat函数函数 */
#include <sys/stat.h>
/* time */
#include <unistd.h>
#include <time.h>
void test_stat();

int main(int argc,char*argv[])
{
test_stat();
return 0;
}

/**
* @Function: int stat(const char *path, struct stat *buf);
* int lstat(const char *path, struct stat *buf);
* @Description: 如果path是符号链接stat获取的是目标文件的属性;
* 而lstat获取的是链接文件的属性
*/
void test_stat()
{
/* 定义一个文件属性结构体变量 */
struct stat file_attr;
int ret;
int i;
struct tm *t;
/* 1. 获取文件属性保存到结构体变量中 */
ret = stat("03file_attributes.c", &file_attr);
/* 2. 判断是否获取成功 */
if(ret < 0)
{
perror("stat");
return;
}
/* 3. 打印文件存取类型 */
if(S_ISREG(file_attr.st_mode))
{
printf("-");
}
if(S_ISDIR(file_attr.st_mode))
{
printf("d");
}
if(S_ISCHR(file_attr.st_mode))
{
printf("c");
}
if(S_ISBLK(file_attr.st_mode))
{
printf("b");
}
if(S_ISFIFO(file_attr.st_mode))
{
printf("p");
}
if(S_ISSOCK(file_attr.st_mode))
{
printf("s");
}
/* 4. 打印文件权限 */
for(i = 8; i >= 0; i--)
{
if(file_attr.st_mode & (1 << i))
{
switch(i % 3)
{
case 0:
printf("x");
break;
case 1:
printf("w");
break;
case 2:
printf("r");
break;
}
}
else
{
printf("-");
}
}
/* 5. 文件字节数(文件大小) */
printf(" %d",(int)file_attr.st_size);
/* 6. 打印文件修改时间 */
t = localtime(&file_attr.st_ctime);
printf(" %d-%d-%d %d:%d",t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min);
printf(" 03file_attributes.c\n");
}

在终端执行以下命令编译程序:

1
2
gcc test.c -Wall # 生成可执行文件 a.out 
./a.out # 执行可执行程序