今天我们接着前一篇的内容来谈一谈IO的目录操作:跟前面的文件操作一样,还是通过相对应的函数来对目录文件进行操作,下面进行相关函数的详细介绍。
一、目录操作
1、打开目录 -- opendir()
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
参数:
name:目录流指针
返回值:
成功返回目录流指针,失败返回NULL
2、读取目录 -- readdir()
这个函数要注意的是: readdir一次只能随机读取一个目录项
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
结构体:
struct dirent {
char d_name[256]; /* Null-terminated filename */ //文件名
};
3、关闭目录 -- closedir()
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
参数:
dirp:关闭目录流指针
返回值:
成功返回0,失败返回-1
4、获取文件属性 -- lstat()
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
参数:
pathname:文件名
statbuf:存放文件信息的结构体指针
返回值:
成功返回0,失败返回-1;
struct stat
{
mode_t st_mode; //文件类型和权限
nlink_t st_nlink; //文件硬链接数
uid_t st_uid; //用户ID
gid_t st_gid; //用户组ID
off_t st_size; //文件大小
struct timespec st_mtim; //最后修改文件时间
#define st_mtime st_mtim.tv_sec
}
好了,相关知识点就到这里,学完上面的内容,我们可以一个小练习来巩固所学
练习:用函数完成 ls -l 命令的制作
首先我们的知道ls -l命令的内容如下图:
下面是需要用到的一些函数:
用到的相关函数:
获取文件类型:
S_IFMT 0170000 bit mask for the file type bit field
S_IFSOCK 0140000 socket
S_IFLNK 0120000 symbolic link
S_IFREG 0100000 regular file
S_IFBLK 0060000 block device
S_IFDIR 0040000 directory
S_IFCHR 0020000 character device
S_IFIFO 0010000 FIFO
如果 st_mode & S_IFMT等于对应文件类型宏,则文件类型宏对应类型就为文件类型
获取文件权限:
st_mode & 对应权限宏 == 宏本身 表示具有该权限
S_IRUSR 00400 owner has read permission
S_IWUSR 00200 owner has write permission
S_IXUSR 00100 owner has execute permission
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
localtime--得到时间戳
#include <time.h>
struct tm *localtime(const time_t *timep);
结构体:
struct tm {
int tm_sec; /* Seconds (0-60) */
int tm_min; /* Minutes (0-59) */
int tm_hour; /* Hours (0-23) */
int tm_mday; /* Day of the month (1-31) */
int tm_mon; /* Month (0-11) */
int tm_year; /* Year - 1900 */
int tm_wday; /* Day of the week (0-6, Sunday = 0) */
int tm_yday; /* Day in the year (0-365, 1 Jan = 0) */
int tm_isdst; /* Daylight saving time */
};
getgrgid -- 得到用户组名的函数
#include <sys/types.h>
#include <grp.h>
struct group *getgrgid(gid_t gid);
struct group
{
char *gr_name; /* group name */
};
getpwuid -- 得到用户名的函数
#include <sys/types.h>
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);
struct passwd
{
char *pw_name; /* username */
};
代码实现:
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include <grp.h>
#include <pwd.h>
int main(int argc, char *argv[])
{
int ret = 0;
int i = 0;
int x = 1;
struct stat info; //创建存放文件信息的结构体
struct tm *t = NULL; //存放时间信息的结构体
struct group *gp = NULL; //存放用户组信息的结构体
struct passwd *usr = NULL; //存放用户信息的结构体
DIR *dir = opendir("."); //打开目录得到目录流指针
if(NULL == dir)
{
perror("opendir");
return -1;
}
struct dirent *buf = NULL;
while( (buf = readdir(dir)) != NULL) //循环遍历目录得到每一个目录项
{
if(buf->d_name[0] != '.') //不显示隐藏文件
{
ret = lstat(buf->d_name, &info); //通过文件名得到文件信息
if(ret < 0)
{
perror("lstat");
return -1;
}
switch(info.st_mode & S_IFMT)
{
case S_IFREG:
printf("-"); break;
case S_IFDIR:
printf("d"); break;
}
for(i = 8; i >= 0; i--)
{
if(info.st_mode & (x<<i)) //这里通过观察规律,可以用位移来做,代码比较简洁
{
switch(i%3)
{
case 2:
printf("r"); break;
case 1:
printf("w"); break;
case 0:
printf("x"); break;
}
}
else
{
printf("-");
}
}
printf(" %ld ", info.st_nlink); //打印硬链接数
usr = getpwuid(info.st_uid);
printf("%s ", usr->pw_name); //打印用户名
gp = getgrgid(info.st_gid);
printf("%s ", gp->gr_name); //打印用户组名
printf("%-8ld ", info.st_size); //打印文件大小
t = localtime(&info.st_mtime);
printf("%d月 %d %02d:%02d ", t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min); //打印最后一次修改文件时间
printf("%s\n", buf->d_name); //打印文件名
}
}
closedir(dir);
return 0;
}
运行结果:
二、库的制作
库的本质:二进制文件
库的类型:静态库和动态库(共享库)
静态库和动态库的区别在于加载程序的时机不同
1、静态库
在程序编译阶段(链接阶段),将库的代码加以复制拷贝的方式加载到源程序中
特点:
1、占资源空间,文件体积大
2、运行时,不需要静态库存在
3、可移植性强
4、优化升级时,需要重新编译源文件
静态库制作流程:
1、先预留接口函数
2、实现所有函数功能
3、生成所有的.c文件的目标文件(以.o结尾的文件)
4、编译生成静态库
ar crs
静态库文件的命名规范:
以lib开头,以.a结尾,中间为库名
libhello.a --> libhello.a==库文件名
hello==库名
链接静态库:
gcc man.c -L. -lhello
2、动态库
在程序编译阶段,只是将需要用到的函数做一个记录,最后在程序运行阶段再加载需要用到的函数
特点:
1、不占资源空间,编译之后可执行文件比较小
2、运行时需要动态库存在
3、可移植性差
4、优化升级比较方便,不需要重新编译源文件
动态库的制作流程:
1、先预留接口函数
2、实现所有函数功能
3、将所有的.c文件生成对应的目标文件
gcc -c -fPIC hello.c -o hello.o
-fPIC --表示生成位置无关代码
4、编译生成动态库
gcc -shared -o libhello.so hello.o
动态库文件的命名规范:
以lib开头,以.a结尾,中间为库名
libhello.a --> libhello.a==库文件名
hello==库名
链接动态库:
gcc man.c -L. -lhello