文章目录
前言
继前文linux系统中文件IO常用的函数(总结)感兴趣可以了解一下。
一、文件IO常用函数练习
继前文,我们来做一个综合小练习,使用文件IO实现文件的拷贝
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define PRINT_ERR(msg) \
do \
{ \
printf("%s %s %d\n",__FILE__,__func__,__LINE__); \
perror(msg); \
return -1; \
} while (0)
int main(int argc, const char* argv[])
{
int sfd,dfd,ret;
char buf[128]={0};
//1.判断传参是否正确./a.out srcfile destfile
if(argc!=3)
{
fprintf(stderr, "input error,try again\n");
fprintf(stderr, "usage:./a.out srcfile destfile\n");
return -1;
}
//2.打开源文件和目标文件
if((sfd=open(argv[1],O_RDONLY))==-1)
{
PRINT_ERR("open srcfile error");
}
if((dfd=open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0666))==-1)
{
PRINT_ERR("fopen destfile error");
}
//3.循环读取拷贝
while((ret=read(sfd,buf,sizeof(buf)))!=0)
{
write(dfd,buf,ret);
}
//4.关闭文件
close(dfd);
close(sfd);
return 0;
}
结果展示
二、stat及相关函数的使用
2.1 stat/getpwuid/getgrgid函数的介绍
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
功能:获取文件的属性信息
参数:
@pathname:文件路径及名字
@statbuf: 属性信息结构体地址
struct stat
{
dev_t st_dev; //文件的设备ID
//dev_t是设备号的类型:内核识别驱动的编号。文件中相当于磁盘的设备号
ino_t st_ino; //inode号,文件系统识别文件的唯一标号
mode_t st_mode; //文件的类型和权限
S_IFMT 0170000 //文件类型的掩码位
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 //管道文件
如何判断文件类型
if((st.st_mode) & 0170000 == 0100000 )
{
printf("这是普通文件\n");
}
如何获取当前文件的权限(8进制表示)
文件的权限 = (st.st_mode) & 0777
nlink_t st_nlink; //硬链接数,文件别名的个数
uid_t st_uid; //用户的id
gid_t st_gid; //组的id
dev_t st_rdev; //如果是字符或者块设备文件,这里表示的是它的设备号。普通文件没有意义
// /dev/input/mouse0 13,32 ===>设备号 == 13 <<20 |32
// 13主设备号,占高12位 (那一类设备)
// 32次设备号,占低20位 (同类中的哪一个设备)
off_t st_size; //文件大小,单位是字节
blksize_t st_blksize; //块的大小,一般是512字节,块是磁盘扇区的倍数,扇区是512字节
blkcnt_t st_blocks; //文件大小,单位是块
struct timespec st_atim; //最后一次访问这个文件的时间
struct timespec st_mtim; //最后一次修改文件的时间
struct timespec st_ctim; //最后一次状态改变的时间(chmod)
};
返回值:成功返回0,失败返回-1置位错误码
注: 这个函数不能用来获取软连接文件的属性信息,如果想获取软连接文件的属性信息,使用lstat
完成
#include <pwd.h>
struct passwd *getpwuid(uid_t uid);
功能:根据uid获取passwd结构体信息
参数:
@uid:用户的id
返回值:成功返回passwd结构体指针,失败返回NULL
struct passwd {
char *pw_name; //用户名
char *pw_passwd; //用户密码
uid_t pw_uid; //uid
gid_t pw_gid; //gid
char *pw_gecos; //用户的信息
char *pw_dir; //用户的家目录
char *pw_shell; //使用的命令行解析器
};
#include <grp.h>
struct group *getgrgid(gid_t gid);
功能:根据gid获取group结构体信息
参数:
@gid:组的id
返回值:成功返回group结构体指针,失败返回NULL
struct group {
char *gr_name; //组名
char *gr_passwd; //组的密码
gid_t gr_gid; //gid
char **gr_mem; //组内成员
};
2.2 代码示例
使用上述函数统计文件信息。
#include <stdio.h>
#include <grp.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define PRINT_ERR(msg) \
do \
{ \
printf("%s %s %d\n",__FILE__,__func__,__LINE__); \
perror(msg); \
return -1; \
} while (0)
int main(int argc,const char * argv[])
{
struct stat st;
if(stat("./io.c",&st))
PRINT_ERR("get file stat error");
printf("ino = %ld,type = %#o,mode=%#o,hardlink=%ld,uid=%s,gid=%s,size=%ld\n",
st.st_ino,st.st_mode & 0170000,st.st_mode&0777,st.st_nlink,
getpwuid(st.st_uid)->pw_name, //根据uid获取用户名
getgrgid(st.st_gid)->gr_name, //根据gid获取组名
st.st_size);
return 0;
}
结果展示:
三、获取系统时间的相关函数
3.1 time/localtime函数介绍
#include <time.h>
time_t time(time_t *tloc);
功能:获取秒钟数
参数:
@tloc:写为NULL
返回值:成功返回秒钟数,失败返回-1,置位错误码
struct tm *localtime(const time_t *timep);
功能:根据秒钟数转换为tm结构体
参数:
@timep:秒钟数
返回值:成功返回tm的结构体指针,失败返回NULL
struct tm {
int tm_sec; //秒
int tm_min; //分钟
int tm_hour; //小时
int tm_mday; //天
int tm_mon; //月+1
int tm_year; //年 +1900
int tm_wday; //周几 (0-6, Sunday = 0)
int tm_yday; //一年中的第几天
int tm_isdst; //夏令时,在中国已被废弃
};
3.2 代码示例
获取系统时间
#include <stdio.h>
#include <time.h>
#define PRINT_ERR(msg) \
do \
{ \
printf("%s %s %d\n",__FILE__,__func__,__LINE__); \
perror(msg); \
return -1; \
} while (0)
int main(int argc,const char * argv[])
{
time_t ts;
struct tm *tm;
if((ts = time(NULL))==-1)
PRINT_ERR("get time error");
if((tm = localtime(&ts))==NULL)
PRINT_ERR("change time error");
printf("%d-%02d-%02d %02d:%02d:%02d week=%d\n",
tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday,
tm->tm_hour,tm->tm_min,tm->tm_sec,tm->tm_wday);
return 0;
}
结果展示:
四、综合练习
要求:输入任意文件,输出文件的类型及文件的属性信息
#include <stdio.h>
#include <grp.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#define PRINT_ERR(msg) \
do \
{ \
printf("%s %s %d\n", __FILE__, __func__, __LINE__); \
perror(msg); \
return -1; \
} while (0)
int main(int argc, const char *argv[])
{
struct stat st;
struct tm *tm;
if (argc != 2)
{
fprintf(stderr, "input error,try again\n");
fprintf(stderr, "usage:./a.out filename\n");
return -1;
}
if (lstat(argv[1], &st))
PRINT_ERR("get file stat error");
switch (st.st_mode & 0170000)
{
case S_IFSOCK:
printf("这是套接字文件\n");
break;
case S_IFBLK:
printf("这是块设备文件\n");
break;
case S_IFLNK:
printf("这是软连接文件\n");
break;
case S_IFDIR:
printf("这是目录文件\n");
break;
case S_IFCHR:
printf("这是字符设备文件\n");
break;
case S_IFIFO:
printf("这是管道文件\n");
break;
case S_IFREG:
printf("这是普通文件\n");
break;
}
printf("ino = %ld,mode=%#o,hardlink=%ld,uid=%s,gid=%s,size=%ld\n",
st.st_ino, st.st_mode & 0777, st.st_nlink,
getpwuid(st.st_uid)->pw_name, //根据uid获取用户名
getgrgid(st.st_gid)->gr_name,
st.st_size);
//转换出来时间
if ((tm = localtime(&st.st_atim.tv_sec)) == NULL)
PRINT_ERR("change time error");
printf("%d-%02d-%02d %02d:%02d:%02d week=%d\n",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec, tm->tm_wday);
return 0;
}
结果展示:
五、目录操作
5.1 opendir函数
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
功能:打开目录
参数:
@name:目录的字符串
返回值:成功返回目录的指针,
失败返回NULL置位错误码
5.2 readdir函数
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
功能:读取目录下的内容
参数:
@dirp:目录的指针
返回值:成功返回dirent结构体指针
失败返回NULL,置位错误码
struct dirent
{
ino_t d_ino; //读取到的文件的inode号
off_t d_off; //不用理会
unsigned short d_reclen; //这个结构体的大小
unsigned char d_type; //文件的类型
DT_BLK This is a block device.
DT_CHR This is a character device.
DT_DIR This is a directory.
DT_FIFO This is a named pipe (FIFO).
DT_LNK This is a symbolic link.
DT_REG This is a regular file.
DT_SOCK This is a UNIX domain socket.
DT_UNKNOWN The file type could not be determined.
char d_name[256]; //文件名
};
5.3 closedir函数
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
功能:关闭目录
参数:
@dirp:目录指针
返回值:成功返回0,失败返回-1置位错误码
5.4 目录操作示例
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#define PRINT_ERR(msg) \
do \
{ \
printf("%s %s %d\n", __FILE__, __func__, __LINE__); \
perror(msg); \
return -1; \
} while (0)
int main(int argc, const char *argv[])
{
DIR *dir;
struct dirent *dt;
if ((dir = opendir("../work")) == NULL)
{
PRINT_ERR("opendir error");
}
while((dt = readdir(dir)) != NULL)
{
switch (dt->d_type)
{
case DT_BLK:
printf("这是块设备文件\n");
break;
case DT_CHR:
printf("这是字符设备文件\n");
break;
case DT_DIR:
printf("这是目录文件\n");
break;
case DT_FIFO:
printf("这是管道文件\n");
break;
case DT_LNK:
printf("这是软连接文件\n");
break;
case DT_REG:
printf("这是普通文件\n");
break;
case DT_SOCK:
printf("这是套接字文件\n");
break;
case DT_UNKNOWN:
printf("未知的文件类型\n");
break;
}
printf("d_name:%s,d_ino:%ld,size:%d\n", dt->d_name, dt->d_ino, dt->d_reclen);
}
closedir(dir);
return 0;
}
结果展示:
注:这里的size是结构体的大小。
5.5 总结
opendir
打开一个目录后得到一个DIR类型的指针给readdir
使用。readdir
函数调用一次就会返回一个struct dirent
类型的指针,这个指针指向一个结构体变量,这个结构体变量里面记录了一个目录项(所谓目录项就是目录中的一个子文件)。readdir
调用一次只能读出一个目录项,要想读出目录中所有的目录项必须多次调用readdir
函数。readdir
函数内部会记住哪个目录项已经被读过了哪个还没读,所以多次调用后不会重复返回已经返回过的目录项。当readdir
函数返回NULL
时就表示目录中所有的目录项已经读完了。- 最后使用
closedir
关闭目录。