1. 文件IO和标准IO 对比
标准IO | 文件IO | |
概念 | C库定义的一组用于输入输出的函数 | 系统中定义的一组用于输入输出的函数 |
特点 | 1)有缓冲机制 2)围绕流进行操作,FILE * 3)只能操作普通文件 4)默认打开了三个流:stdout/stdin/stderr | 1)无缓冲机制 2)围绕文件描述符操作,非负整数 3)默认打开了三个文件描述符:0/1/2 4)可以操作除目录文件 外的任意类型文件 |
函数 | 1)打开:fopen 2)关闭:fclose 3)读写:fgetc fputc fgets fputs fread fwrite 4)定位:rewind fseek ftell | 1)打开:open 2)关闭:close 3)读写:read write 4)定位:lseek |
linux下默认两者最多:能打开多少个文件:1024(最多打开1024个文件描述符)-3(默认打开的文件描述符)=1021
2. 目录操作
围绕目录流进行操作
opendir readdir closedir
DIR *opendir(const char *name);
功能:获得目录流
参数:要打开的目录
返回值:
成功:目录流
失败:NULL
----------------------------------------------
int closedir(DIR *dirp);
功能:关闭目录
参数:dirp:目录流
----------------------------------------------
struct dirent *readdir(DIR *dirp);
功能:读目录
参数:要读的目录流
返回值:
成功:读到的信息(会每次读后更新偏移量)
失败:NULL,设置errno号
**返回值为结构体,该结构体成员为描述该目录下的文件信息**
struct dirent {
ino_t d_ino; /* 索引节点号*/
off_t d_off; /* 在目录文件中的偏移*/
unsigned short d_reclen; /* 文件名长度*/
unsigned char d_type; /* 文件类型 */
char d_name[256]; /* 文件名 */
};
实现:ls功能
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main(int argc, char *argv[])
{
// 打开目录流
DIR *dir = opendir("."); //.就是当前目录
if (NULL == dir)
{
perror("dir open err");
return -1;
}
puts("dir open err");
// 存储 目录文件信息 的结构体
struct dirent *d = NULL;
// 每次读,都会更新 偏移量
// 失败返回null(返回值为指针类型的函数,很多都如此)
while ((d = readdir(dir)) != NULL)
{
// 不打印 隐藏文件
if (d->d_name[0] != '.')
{
printf("name:%s\n", d->d_name);
printf("name_len:%u\n", d->d_reclen);
printf("file type:%u\n", d->d_type);
printf("d_ino:%lu\n", d->d_ino);
printf("off_t:%ld\n", d->d_off);
puts("------");
}
}
// 关闭目录流
closedir(dir);
// 置空 保存目录文件 的结构体指针
d = NULL;
}
3. 文件属性获取
stat getpwuid getgugid ctime
3.1. stat 获取文件属性
int stat(const char *path, struct stat *buf);
功能:获取文件属性
参数:
path:文件路径名
buf: 保存文件属性信息的结构体
返回值:
成功: 0
失败:-1
// 各类属性,对应各自的方法 获取信息到 变量(如结构体)
struct stat {
dev_t st_dev; /* 设备包含文件ID */
ino_t st_ino; /* inode号 */
mode_t st_mode; /* 文件类型和权限 */
nlink_t st_nlink; /* 硬链接数 */
uid_t st_uid; /* 用户ID */
gid_t st_gid; /* 组ID */
off_t st_size; /* 大小 */
dev_t st_rdev; /* 设备ID */
time_t st_atime; /* 最后访问时间 */
time_t st_mtime; /* 最后修改时间 */
time_t st_ctime; /* 最后状态改变时间 */
};
3.1.1. 判断文件类型
3.1.2. 判断文件权限
简化写法 解释:
通过权限掩码,和对应位 相与,得到二进制进行判断
3.2. 获取 文件对应的:用户id 组id
struct passwd *getpwuid(uid_t uid);
功能:通过用户 id 获取用户属性
参数:uid:用户 id
返回值:
成功:返回 保存用户相关属性的 结构体的地址
失败:NULL
struct passwd
{
char *pw_name; /* 用户名 */
char *pw_passwd; /* 密码 */
uid_t pw_uid; /* user ID */
gid_t pw_gid; /* group ID */
char *pw_gecos; /* 真实名 */
char *pw_dir; /* 主目录 */
char *pw_shell; /* 使用的shell */
};
struct group *getgrgid(gid_t gid);
功能:通过组 id 获取组属性
参数:gid:组 id
返回值:
成功:返回保存组相关属性结构体的地址
失败:NULL
struct group
{
char *gr_name; /* 组名 */
char *gr_passwd; /* 组密码 */
gid_t gr_gid; /* 组ID */
char **gr_mem; /* 组成员账号 */
};
3.3. 获取 文件时间戳
char *ctime(const time_t *timep);
功能:将时间秒数转化为字符串表示
参数:timep :时间
返回值:
成功返回字符串首地址
失败:NULL
3.4. 总体代码
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
void checkFileType(struct stat *st)
{
#if 0 //第一种写法
switch (st->st_mode & __S_IFMT)
{
case __S_IFBLK:
putchar('b');
break;
case __S_IFCHR:
putchar('c');
break;
case __S_IFDIR:
putchar('d');
break;
case __S_IFREG:
putchar('-');
break;
case __S_IFLNK:
putchar('l');
break;
case __S_IFSOCK:
putchar('s');
break;
case __S_IFIFO:
putchar('p');
break;
}
#else //第二种写法
if (S_ISBLK(st->st_mode))
putchar('b');
else if (S_ISCHR(st->st_mode))
putchar('c');
else if (S_ISDIR(st->st_mode))
putchar('d');
else if (S_ISREG(st->st_mode))
putchar('-');
else if (S_ISLNK(st->st_mode))
putchar('l');
else if (S_ISSOCK(st->st_mode))
putchar('s');
else if (S_ISFIFO(st->st_mode))
putchar('p');
#endif
}
void checkFilePermission(struct stat *st)
{
#if 0 //写法1
char buf[3] = "rwx";
for (int i = 0; i < 3; i++)
st->st_mode &(0400) >> i ? printf("%c", buf[i % 3]) : putchar('-');
#else //写法2
// 拥有者:挨个判断:读、写、可执行,不然就是无权限
st->st_mode &S_IRUSR ? putchar('r') : putchar('-');
st->st_mode &S_IWUSR ? putchar('w') : putchar('-');
st->st_mode &S_IXUSR ? putchar('x') : putchar('-');
// 组群用户者:挨个判断:读、写、可执行,不然就是无权限
st->st_mode &S_IRGRP ? putchar('r') : putchar('-');
st->st_mode &S_IWGRP ? putchar('w') : putchar('-');
st->st_mode &S_IXGRP ? putchar('x') : putchar('-');
// 其他用户看到后:挨个判断:读、写、可执行,不然就是无权限
st->st_mode &S_IROTH ? putchar('r') : putchar('-');
st->st_mode &S_IWOTH ? putchar('w') : putchar('-');
st->st_mode &S_IXOTH ? putchar('x') : putchar('-');
#endif
}
int main(int argc, char *argv[])
{
/* 先获取 目标文件的 信息结构体 */
struct stat stat_buf; //结构体变量
if (stat(argv[1], &stat_buf)) //获取成功返回0,不然非0
{
perror("get file information err");
return -1;
}
/* 文件类型的判断 */
// 两种写法
checkFileType(&stat_buf);
/* 文件权限的判断 */
// 两种写法
checkFilePermission(&stat_buf);
/* 该文件,硬链接的个数 */
printf(" %d", stat_buf.st_nlink);
/* 用户名 */
printf(" %s", getpwuid(stat_buf.st_uid)->pw_name);
/* 组名 */
printf(" %s", getgrgid(stat_buf.st_gid)->gr_name);
/* 文件 内存大小 */
printf(" %ld", stat_buf.st_size);
/* 最后修改时间 */
// ctime:将时间秒数 转化为 字符串显示
// +4 字符串首地址 后移
// .12 右对其 12字符宽度
printf(" %.12s", 4 + ctime(&stat_buf.st_mtime));
/* 文件 名字 */
printf(" %s\n", argv[1]);
}
4. 静态库 动态库
头文件:#include <> " "
源文件:xx.c
库文件:通俗来讲就是将用户写好的程序打包成一个整体,当其他模块或用户使用时,只需要有这个库文件就可以了,不需要源代码文件。也可以理解为一组预先编译好的方法的集合。
4.1. 概念
库:就是把一些常用函数的目标文件打包在一起,提供相应函数的接口,便于程序员使用;本质上来说库是一种可执行代码的二进制形式
4.2. 库的分类
静态库和动态库,本质区别是代码被载入时刻不同。
1) 静态库在程序编译时会被连接到目标代码中。
优点:
程序运行时将不再需要该静态库;
运行时无需加载库,运行速度更快
缺点:
静态库中的代码复制到了程序中,因此体积较大;
静态库升级后,程序需要重新编译链接
2) 动态库是在程序运行时才被载入代码中。
优点:
程序在执行时加载动态库,代码体积小;
程序升级更简单;
不同应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。
缺点:
运行时还需要动态库的存在,移植性较差
4.3. 静态库的制作
- 将源文件编译生成目标文件
gcc -c xxx.c -o xxx.o
- 创建静态库,用ar命令,将许多.o文件生成.a文件
ar crs libxxx.a xxx.o
lib是库的前缀,xxx是库名 .a 是后缀
- 测试静态库的使用
gcc xxx.c -L指定库的路径 -l指定库名
./a.out
4.4. 动态库的制作
- gcc创建动态库
gcc -fPIC -c xxx.c -o xxx.o
-fPIC 创建与地址无关的编译程序
gcc -shared -o libxxx.so xxx.o
lib是库的前缀,xxx库名 .so动态库的后缀
- 测试动态库的使用
gcc xxx.c -L指定库的路径 -l指定库名
./a.out
4.4.1. 加载动态库的方法
可以正常编译通过,但是运行时报错./a.out: error while loading shared libraries: libmyadd.so: cannot open shared object file: No such file or directory
原因:
当加载动态库时,系统会默认从/lib或/usr/lib路径下查找库文件
解决方法(有三种):
(1)把库拷贝到/usr/lib和/lib目录下。(此方法编译时不需要指定库的路径)
(2)在LD_LIBRARY_PATH环境变量中加上库所在路径。
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
(终端关闭,环境变量就没在了)
(3) 添加/etc/ld.so.conf.d/*.conf文件。把库所在的路径加到文件末尾,并执行ldconfig刷新
sudo vi /etc/ld.so.conf.d/*.conf
添加动态库存在的路径,如:
/home/hq/work/teach/23061/day3/shared
刷新 sudo ldconfig