头文件
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
opendir()
该函数的作用是打开目录,将返回值传递给后续的操作函数,如readdir()
函数原型:DIR * opendir(const char * name);
该函接受一个字符串,即目录名,如果读取成功,则返回DIR类型的指针,否则返回NULL。
常见的错误代码如下:
错误码 | 原因 |
---|---|
EACCESS | 权限不足 |
EMFILE | 已达到进程可同时打开的文件数上限。 |
ENFILE | 已达到系统可同时打开的文件数上限。 |
ENOTDIR | 参数name非真正的目录 |
ENOENT | 参数name 指定的目录不存在,或是参数name 为一空字符串。 |
ENOMEM | 核心内存不足。 |
DIR是一个文件句柄,其定义如下:
struct __dirstream
{
int fd; /* File descriptor. */
__libc_lock_define (, lock) /* Mutex lock for this structure. */
size_t allocation; /* Space allocated for the block. */
size_t size; /* Total valid data in the block. */
size_t offset; /* Current offset into the block. */
off_t filepos; /* Position of next entry to read. */
/* Directory block. */
char data[0] __attribute__ ((aligned (__alignof__ (void*))));
};
typedef struct __dirstream DIR; // 声明在<dirent.h>中
readdir()
DIR结构体类似于FILE,是一个内部结构,这个句柄就是等一下要传给readdir()去遍历该目录,该函数的原型如下:
struct dirent *readdir(DIR *dir)
它接收一个DIR类型的指针,有错误发生或读取到目录文件尾则返回NULL,成功则返回下个目录进入点,为struct dirent* 类型,其定义如下:
struct dirent
{
long d_ino; /* inode number 索引节点号 */
off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
unsigned short d_reclen; /* length of this d_name 文件名长 */
unsigned char d_type; /* the type of d_name 文件类型 */
char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}
其中d_type表明该文件的类型,预定义的宏及含义如下:
宏 | 解释 |
---|---|
DT_BLK(6) | 这是一个block device |
DT_CHR(2) | 这是一个character device |
DT_DIR(4) | 这是一个目录 |
DT_FIFO(1) | 这是一个命名管道(FIFO) |
DT_LNK(10) | 这是一个符号链接 |
DT_REG(8) | 这是一个普通的文件 |
DT_SOCK(12) | 这是一个UNIX domain socket |
DT_UNKNOWN(0) | 文件类型不确定 |
错误码errno
定义<errno.h>中的一个整型变量,当系统调用或者一些库函数出错,设置该变量以表明(最近)发生错误的原因,这里列举几种常用的错误码及解释:
宏 | 解释 |
---|---|
EPERM(1) | 操作不被允许 |
ENOENT | 文件或目录不存在 |
ESRCH | 进程不存在 |
EINTR | 系统调用中断 |
EIO | 输入或输出错误 |
… | … |
linux中通过如下命令可以查看所有的错误码
sudo apt install moreutils
errno -l
部分输出如下:
stat()
从上述定义也能够看出来,dirent结构体存储的关于文件的信息很少,所以dirent同样也是起着一个索引的作用,如果想获得类似ls -l那种效果的文件信息,必须要靠stat函数了。
函数原型如下:
int stat(const char *file_name, struct stat *buf)
通过readdir()函数读取到的文件名存储在结构体dirent的d_name成员中,stat()的作用就是获取文件名为d_name的文件的详细信息,存储在stat结构体中,调用成功返回0,否则-1, errno指示了错误原因。以下为stat结构体的定义:
struct stat{
mode_t st_mode; //文件访问权限
ino_t st_ino; //索引节点号
dev_t st_dev; //文件使用的设备号
dev_t st_rdev; //设备文件的设备号
nlink_t st_nlink; //文件的硬连接数
uid_t st_uid; //所有者用户识别号
gid_t st_gid; //组识别号
off_t st_size; //以字节为单位的文件容量
time_t st_atime; //最后一次访问该文件的时间
time_t st_mtime; //最后一次修改该文件的时间
time_t st_ctime; //最后一次改变该文件状态的时间
blksize_t st_blksize; //包含该文件的磁盘块的大小
blkcnt_t st_blocks; //该文件所占的磁盘块
};
closedir()
当遍历完目录后,就调用closedir()函数关闭它:
int closedir(DIR *dir);
关闭成功则返回0,失败返回-1,errno指示了错误原因。
代码示例
遍历文件目录,输出每个文件大小
# test.cpp
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<dirent.h>
#include<sys/stat.h>
void travel_dir(const char* path){
DIR *d;
struct dirent* file = new dirent();
struct stat* st = new struct stat();
if(!(d = opendir(path))){
printf("erroe dirs %s!!!\n", path);
return;
}
while((file = readdir(d)) != NULL){
// 跳过隐藏文件, 当前目录(.),或上一级目录
if(strncmp(file->d_name, ".", 1) == 0)
continue;
//如果是普通的文件
if(file->d_type == 8){
printf("%s\n", file->d_name);
char* file_path = (char *)malloc(strlen(path) + strlen(file->d_name) + 2);
memset(file_path, '\0', sizeof(file_path));
strcat(file_path, path);
strcat(file_path, "/");
strcat(file_path, file->d_name);
printf("%s\n", file_path);
int num = stat(file_path, st);
printf("%d\n", num);
if(num == 0)
printf("文件大小: %dByte\n", st->st_size);
else{
perror("stat");
}
}
//如果是文件夹
if(file->d_type == 4){
char* dir_path = (char *)malloc(strlen(path) + strlen(file->d_name) + 2);
memset(dir_path, '\0', sizeof(dir_path));
strcat(dir_path, path);
strcat(dir_path, "/");
strcat(dir_path, file->d_name);
printf("%s\n", dir_path);
travel_dir(dir_path);
}
}
closedir(d);
return;
}
int main(int argc, char * argv[]){
travel_dir(argv[1]);
return 0;
}
执行
g++ test.cpp -o a.out
./a.out /home/lw/DIR
输出如下: