视频地址:华清远见—https://www.bilibili.com/video/BV1dz411B7vj?p=1
文件基础一组相关数据的有序集合
文件类型常规文件: r ----- 二进制文件;ASCII码文件
目录文件: d
字符设备文件: c
块设备文件: b
管道文件: p
套接字文件: s
符号链接文件: l
不同的操作系统,所支持的文件类型不一样;
标准IO 1. 概念C库中定义好的一些用于输入和输出的函数;主流操作系统上都实现了C库;
标准IO通过缓冲机制减少系统调用,实现了更高的效率;
1.1 缓冲机制:没有缓冲机制的情况下,应用程序每次读写文件都需要通过系统调用来完成;(应用程序指定读取多少数据,系统调用就读取多少数据)
应用程序内部开辟一个缓冲区buf(4k),(一次性读取4k的数据返回应用程序);缓冲区有数据,直接从缓冲区读取,避免重复的系统调用。(写入数据的时候也是先往缓冲区中写数据,缓冲区满了才会去写实际的文件)
1.2 系统调用:代码直接操作硬件:单片机;
有操作系统的情况下,应用程序访问硬件,必须通过操作操作系统提供的系统调用接口来操作硬件;
不同的操作系统,系统调用接口不兼容。
2. 流(FILE结构体) 2.1 定义标准IO中用一个结构体类型来存放打开的文件的相关信息;(系统头文件中定义好的)
标准IO中所有的操作都是围绕FILE来进行。(一个FILE就代表一个打开的文件)
2.2 流(stream)FILE又被称为流;
文本流/二进制流
Windows系统中:(对换行符的处理不同)
二进制流:换行符 — ‘n’ 文本流:换行符 — ‘r’‘n’//回车和换行
Linux系统中不作区分,都看作二进制流;
3. 流的缓冲类型 3.1 全缓冲当流的缓冲区无数据(读)或无空间(写)时才执行实际I/O操作;(默认缓冲类型)
3.2 行缓冲当在输入和输出时遇到换行符‘n’时,进行I/O操作;(进行实际的文件操作,读或写)
当流和一个终端关联时,默认为行缓冲。(典型:标准输入和标准输出流。如:打印调试信息时,如果不加换行符,打印的信息仅仅会写在标准输出流的缓冲区中,并没有写在实际的终端上)
3.3 无缓冲数据直接写入文件,流不进行缓冲。(对流进行操作的时候都是访问实际的文件;如:打印错误信息时,希望错误信息直接打印在终端上。标准错误流默认为无缓冲)
在linux下运行一个程序时,系统会自动打开三个流,(标准输入stdin,标准输出stdout,标准错误流stderr)可以在程序中直接使用。4. 流的打开与关闭 4.1 流的打开 4.1.1 fopen
FILE *fopen(const char *path,const char *mode); //第一个参数:指定打开文件的路径(通过字符串或者命令行参数把==文件名传递进来==); //第二个参数:流的打开方式 //成功时返回流指针;出错时返回NULL。(fopen的返回值通常要保存下来进行错误处理)4.1.2 流的打开方式
mode参数:
‘r’或‘rb’ — 文件以只读方式打开,文件必须存在。
r表示以文本流的方式打开一个文件;rb表示以二进制的方式打开文件。//linux不区分文本流和二进制流
‘r +’或‘r + b’ — 文件以读写方式打开,文件必须存在。
‘w’或‘wb’ — 文件以只写方式打开,文件必须存在则文件长度清0(内容清空);若文件不存在则创建。
‘w + ’或‘w + b’ — 文件以读写方式打开,文件必须存在则文件长度清0(内容清空);若文件不存在则创建。
//文件原来内容已经被清空,只能读取新写入的内容。
‘a’或‘ab’ — 文件以只写方式打开,若文件不存在则创建;向文件写入的数据会被追加到文件末尾。
‘a + ’或‘a + b’ — 文件以读写方式打开,若文件不存在则创建;向文件写入的数据会被追加到文件末尾。
//可以读取到文件原来的内容。
4.1.3 示例#include4.1.4 新建文件权限int main(int argc,char *argv[]) { FILE *fp;//定于一个流指针fp if((fp = fopen("text.txt","r +")) == NULL)//调用fopen(文件名,打开方式),如果只有文件名无路径,默认为当前路径; //判断fp的值是否存在,若不存在打印错误信息; { printf("fopen errorn");//标准输出流是行缓冲,加'n'可以立即显示在终端上; return -1; } ... return 0; }
fopen()创建的文件默认访问权限是0666(rw-rw-rw-) //0表示8进制数,后面分别表示用户、同组用户和其他用户的权限。
在linux中,umask的设定会影响新建文件的访问权限,其规则为(0666 &~ umask)
umask值:root用户默认是0022,普通用户默认是 0002;
// root用户先位反再进行与操作之后文件权限为0644;
用户可以通过umask函数来设置权限掩码,只会影响到当前程序
umask 002//权限改为002 umask//查看umask值
掩码设置为0时,对文件的权限无影响
4.2 处理错误信息 4.2.1代码实现extern int errno; // errno用来存放错误号的整型变量; void perror(const char *s); // perror先输出字符串s,再打印错误号对应的错误信息; char *strerror(int errno); // 根据错误号返回对应的错误信息;(返回值是字符串首地址,存放的是错误信息)4.2.2 示例
1.perror打印错误信息
#includeint main(int argc, char *argv[]) { FILE *fp; if((fp = fopen("text.txt","r +")) == NULL){ perror("fopen"); return -1; } ... }
2.strerror(erron)打印错误信息
#include4.3 流的关闭 4.3.1 fclose#include //声明strerror函数 #include //声明errno变量 int main(int argc, char *argv[]) { FILE *fp; if((fp = fopen("text.txt","r +")) == NULL){ printf("fopen:%sn",strerror(errno)); return -1; } ... }
int fclose(FILE *stream);//参数是之前打开的流指针 //调用成功返回0,失败返回EOF,并设置errno;4.3.2 注意事项
流关闭时自动强制刷新缓冲区的数据并释放缓冲区;
当一个程序正常终止时,所有打开的流都会被关闭;(要养成手动关闭流的习惯)
流一旦关闭后就不能执行任何操作。
if(fp != NULL) fclose(fp);//流关闭之前需要判断是否为NULL4.4思考
如何测试程序中能够打开的文件或流的最大个数
#include#include #include int main(int argc, char *argv[]) { int count = 0; FILE *fp; while(1) { if((fp = fopen("text.txt","a")) == NULL){ perror("fopen"); break; } count++; } printf("count = %dn",count); if(fp != NULL) fclose(fp); return 0; } *** fopen:Too many open files count = 1021 段错误 (核心已转储)//段错误原因---fp有为空的情况,可用fclose(stdout)关闭;或者先判断是否为空指针
1021 + stdin + stdout + stderr = 1024
5.流的读写方式 5.1按照字符读写fgetc()/fputc() 一次读/写一个字 //文本文件和二进制文件都可以但效率低5.1.1按字符输入
#include5.1.2示例:统计一个文件的大小int fgetc(FILE *stream);//参数为流指针,用来指定从哪个流中读取字符 int getc(FILE *stream);//等同fgetc int getchar(void);//没有参数,只能从标准输入流中读取一个字符并返回 getchar() == fgetc(stdin) //成功时返回读取的字符;若到文件末尾或出错时返回EOF(-1)
FILE *fp;
int ch,count = 0;
if(fp = fopen("text.txt","r")) == NULL){//依次从文件中读取一个字符;
perror("fopen");
return -1;
}
while((ch = fgetc(fp)) != EOF){//如果读取的数据不是-1,就计数;
count++;
}
printf("total %d bytesn",count);
5.1.3按字符输出
#include5.1.4示例:向一个流输入26个字母int fputc(int c,FILE *stream);//(要输出的字符,指定写入的流) int putc(int c,FILE *stream);//等同fputc int putchar(int c);//默认向标准输出流写入一个字符; //成功时返回读取的字符;若到文件末尾或出错时返回EOF(-1)
FILE *fp;
int ch;
if((fp = fopen("text.txt","w")) == NULL){
perror("fopen");
return -1;
}
for(ch = 'a';ch <='z';ch++){
fputc(ch,fp);//把ch中的字符写入到fp指定的流中
}
5.1.5示例:文件的复制
//通过命令行参数传递源文件和目标文件名
FILE *fps, *fpd;
int ch;
if (argc < 3)//判断命令行参数个数
{
printf("Usage : %S n",argv[0]);//告诉用户正确格式
return -1;
}
if ((fps = fopen(argv[1],"r")) == NULL)//打开源文件并判断是否有效
{
perror("fopen src file");
return -1;
}
if ((fpd = fopen(argv[2],"w")) == NULL)//打开目标文件并判断是否有效
{
perror("fopen dst file");
return -1;
}
while((ch = fgetc(fps)) != EOF))//从源文件读取内容并写入目标文件
{
fputc(ch, fpd);
}
fclose(fps);
fclose(fpd);
5.2按行进行读写
fgets()/fputs() 一次读/写一行 //只能读写文本文件5.2.1按行读取
#includechar *gets(char *s);//不建议使用,未指定缓冲区大小,会使文件缓冲区溢出; char *fgets(char *s, int size, FILE *stream); //从流指针中读取一行字符串,保存至s所指向的缓冲区中(size指定缓冲区的大小) //成功时返回缓冲区首地址,到文件末尾或出错时返回NULL; //遇到换行符或者size-1个有效字符时返回,都会在后面追加' '表示字符串结束;
#define N 6
char buf[N];//定义字符数组保存读到的字符串
fgets(buf,N,stdin);//stdin从标准输入读取
printf("%s",buf);//abcd换行 --- abcd'n'' '//abcdef --- abcde' '
//下次再调用fgets,缓冲区剩余内容会重新读到buf
5.2.2按行写入
#includeint puts(const char *s);//把存放在s缓存区的字符串写入到标准输出流 int fputs(const char *s,FILE *stream);//向指定的流中写入缓冲区s中的字符串 //成功时返回字符串长度,出错时返回EOF //puts将缓冲区s中的字符串输出到stdout,并追加'n'
注意
仅仅是将缓冲区中的字符串写入流,无论缓冲区中有无换行符都无影响;
FILE *fp;
char buf[]="hello world";
if((fp = fopen(argv[1],"a")) == NULL){//将字符串一追加的形式写入命令行参数
perror("fopen");
return -1;
}
fputs(buf,fp);
5.2.3示例:统计文本文件行数
#define N 64
FILE *fp;
int count = 0;
char buf[N]={0};
if(argc != 2){
printf("%s 文本文件名n",argv[0]);
return -1;
}
if((fp = fopen(argv[1],"r")) == NULL){
perror("fopen");
return -1;
}
while((fgets(buf,N,fp))!=NULL){//一直读取到文件末尾返回NULL
if(buf[strlen(buf)-1] == 'n')
{
count++;
}
}
printf("%dn",count);
换行符也算一个字符,并且字符串以’ ’结束,所以倒数第二位是’n’,详见按行读取5.2.1
5.3 按照指定对象去读写fread()/fwrite() 每次读/写若干对象,而每个对象具有相同的长度 //指定对象可以指定字符对象,整型对象,结构体对象等,以这个对象为基本单位去读/写流 //文本文件和二进制文件都可以 #include5.3.1 fread/fwritesize_t fread(void *ptr, size_t size,size_t n,FILE *fp); //缓冲区首地址//指定读取的每个对象所占的字节//指定从流中读取多少个对象//指定从哪个流中读取数据 size_t fwrite(const void *ptr, size_t size,size_t n,FILE *fp); //将存放在缓冲区ptr中的n个size大小的对象写入fp流指针指定的对象中 //成功时返回实际读写的对象个数,出错时返回EOF(-1);读到文档末尾返回0
既可以读写文本文件,也可以读写数据文件
int s[10];
if(fread(s,sizeof(int), 10, fp)< 0){
perror("fread");
return -1;
}
//将结构体写入流
struct student{
int no;
char name[8];
float score;
}s[]={{1,"zhang",97},{2,"wang",95}};
fwrite(s, sizeof(struct student), 2, fp);
5.3.2 示例:文件的复制
//通过命令行参数传递源文件和目标文件名
#define N 64
FILE *fps, *fpd;
char buf[N];
int n;
if (argc < 3)//判断命令行参数个数
{
printf("Usage : %S n",argv[0]);//告诉用户正确格式
return -1;
}
if ((fps = fopen(argv[1],"r")) == NULL)//打开源文件并判断是否有效
{
perror("fopen src file");
return -1;
}
if ((fpd = fopen(argv[2],"w")) == NULL)//打开目标文件并判断是否有效
{
perror("fopen dst file");
return -1;
}
while((n = fread(buf,1,N,fps)) > 0)//通过fread返回值判断是否读到文件末尾
{
fwrite(buf, 1, n, fpd);//将缓冲区中保存的数据写入fpd
}
fclose(fps);
fclose(fpd);
6. 流的刷新与定位
6.1 流的刷新
6.1.1 流自动刷新缓冲区
1.当流的缓冲区满或者有换行符时,系统会自动刷新流的缓冲区,将内容写入实际的文件中。
对于全缓冲,当缓冲区满时才会自动刷新;行缓冲满足一个条件即可自动刷新。
2.当流关闭的时候,如果流中存有数据,系统会将内容写入实际的文件并刷新流。
3.通过fflush函数刷新流
6.1.2 fflush函数#include6.1.3 fflush示例int fflush(FILE *fp); //成功时返回0;出错时返回EOF(-1); //使用此函数时,会强制将流中缓冲区的内容写入实际的文件中; //Linux下只能刷新输出缓冲区。输入缓冲区的内容只能通过fgetc或者其他函数读走
#include6.2 流的定位 6.2.1 函数定义int main() { FILE *fp; if((fp = fopen("text.txt","w")) == NULL) { perror("fopen"); return -1; } fputc('a',fp); fflush(fp);//如果不刷新缓冲区,由于12行程序会一直运行,全缓冲不会刷新,'a'就会一直在缓冲区中,而不会存入文件中; while(1); return 0; }
当打开一个流的时候,其内部有一个当前读写位置;读写位置会自动后移;
#includelong ftell(FILE *stream); //返回值为长整型,返回指定流的当前读写位置;流打开时其读写位置为0 long fseek(FILE *stream,long offset, int whence); //用来设定一个流的当前读写位置 //(流指针,偏移量(可正可负),基准点) ---whence参数SEEK_SET/SEEK_CUR/SEEK_END //正数表示基准点之后,负数表示基准点之前; //不允许偏移到初始位置以前;允许超出当前文件的长度; void rewind(FILE *stream); //直接将流定位到起始位置
成功时返回流的读写位置,出错时返回EOF;
6.2.2 示例:文件末尾追加FILE *fp = fopen("text.txt","r+");
fseek(fp,0,SEEK_END);
fputc('t',fp);
6.2.3 示例:获取文件长度
FILE *fp;
if((fp = fopen("text.txt","r+")) == NULL){
perror("fopen");
return -1;
}
fseek(fp,0,SEEK_END);//将文件读写位置定位到文件末尾
printf("length is %dn",ftell(fp));//获取当前读写位置的值
6.3 判断流是否出错和结束
#include7. 流的格式化输出 7.1 sprintf/fprintfint ferror(FILE *stream); //判断流是否出错:出错时返回1;没有出错时返回0,表示逻辑假。 int feof(FILE *stream); //测试流的读写位置是否已经到了末尾 //已经到末尾返回1,表示逻辑真;否则返回0。
#include7.2 示例:以指定年月日分别写入文件和缓冲区int fprintf(FILE *stream, const char *fmt, ...); //流指针设置为stdout此函数同printf //将格式化后的字符串输出到指定的流中 参数:(流指针, 格式符...) int sprintf(char *s,const char *fmt, ...); //以指定的格式把一个字符串输出到缓冲区中 参数:(缓冲区首地址, 格式符...)//一般用字符数组存放字符串
int year, month, data;
FILE *fp;
char buf[64];
year = 2014; month = 10; date = 26;
fp = fopen("text.txt", "a+");
fprintf(fp,"%d-%d-%dn", year, month, date);
sprintf(buf,"%d-%d-%dn", year, month, date);
7.3 示例
time()用来获取系统时间(秒数)
//1970年到现在的秒数
localtime()将系统时间转换成本地时间
FILE *fp;
int line = 0;
char buf[64];
time_t t;
struct tm *tp;
if((fp = fopen("text.txt", "a+")) == NULL)
{
perror("fopen");
return -1;
}
while(fgets(buf, 64, fp) != NULL)
{
if(buf[strlen(buf)-1] == 'n') line++;
}
while(1)
{
time(&t);
tp = localtime(&t);
fprintf(fp, "%02d, %d-%02d-%02d %02d:%02d:%02dn", ++line, tp->tm_year+1900, tp->tm_mon+1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec);
fflush(fp);//默认打开的普通文件是全缓冲,用强制刷新确保字符串立刻写到文件中
sleep(1);
}
文件IO
1. 概念
1.1 文件IO
遵循POSIX规范,也是一组函数,无缓冲,每次读写都会执行系统调用。
通过文件描述符fd表示一个打开的文件,可以访问各种类型的文件。
Linux下,标准IO基于文件IO实现。
1.2 文件描述符文件描述符是一个非负整数。Linux为程序中每个打开的文件分配一个文件描述符;
文件描述符由系统从0开始分配,依次递增;
每个程序中打开的文件所对应的文件描述符都是独立的,互不影响。
2. 打开文件 2.1 open#includeint open(const char *path, int oflag, ...); //打开已经存在的文件时使用两个参数 //创建文件时,第三个参数指定新文件的访问权限 参数: 要打开的文件的路径 指定打开方式 返回值: 成功时返回文件描述符;出错时返回EOF
设备文件只能通过open打开;需要通过特定的函数来创建。
2.2 打开方式 2.2.1 函数原型#includeint open(const char *pathname, int flags, mode_t mode);
pathname:被打开的文件名(可包括路径名)
mode:三位8进制数表示 CREAT时必须加权限
O_RDONLY:只读方式打开文件; O_WRONLY:只写方式打开; O_RDWR:以读写的方式打开; //以上三种读写方式互斥 O_CREAT:如果文件不存在,就创建新的文件; //创建文件时mode表示新建文件的权限 O_EXCL:测试用CREAT创建文件时文件是否存在; //如果文件存在,则可以返回错误信息 O_TRUNC:如果文件已经存在,打开文件时删除原有数据; O_APPEND:以添加的方式打开文件,写的内容会追加到文件末尾;2.2.2 示例
只写方式打开文件1.txt,不存在创建,存在清空;
int fd;
if((fd = open("1.txt", O_WRONLY|O_CREAT|O_TRUNC,0666)) < 0)
{
perror("open");
return -1;
}
读写方式打开1.txt,不存在创建,存在报错
int fd;
if((fd = open("1.txt", O_RDWR|O_CREAT|O_EXCL,0666)) < 0)
{
if(errno == EEXIST)//因为文件存在报错
{
perror("exit error");
}
else
{
perror("other open");
}
}
3. 文件关闭
3.1 close
#include4. 文件IO编程接口 4.1 读取文件 4.1.1 read函数int close(int fd); 参数:要关闭文件的文件描述符 返回值:成功时:0 出错时:-1 //程序结束时自动关闭打开的所有文件 //文件关闭后,文件描述符不再代表文件
#includessize_t read(int fd, void *buf, size_t count); 参数: 指定读取的文件描述符 缓冲区首地址//count一般指定为缓冲区大小 指定读取的字节数//不能超过缓冲区大小 返回值: 成功时返回实际读取的字节数;出错时返回-1
读到文件末尾时返回0
4.1.2 示例从指定文件中读取内容并统计大小
int fd, n, count = 0;
char buf[64];
if(argc < 2)
{
printf("Usage: %s n",argv[0]);
return -1;
}
if((fd = open(argv[1], O_RDONLY)) < 0)
{
perror("open");
return -1;
}
while((n = read(fd, buf, 64)) > 0)
{
count += n;
}
printf("文件大小为:%dByten",count);
4.2写入文件
4.2.1 write函数
#include4.2.2 示例ssize_t write(int fd, void *buf,size_t count); 参数: 指定写入的文件描述符 发送缓冲区首地址//count一般指定为缓冲区大小 指定读取的字节数//不能超过缓冲区大小 返回值: 成功时返回实际写入的字节数;出错时返回-1
将键盘输入的内容写入文件,直到输入quit
int fd;
char buf[20];
if((fd = open(argv[1], O_WRONLY|O_CREAT|O_TRUNC,0666)) < 0)
{
perror("open");
return -1;
}
while(fgets(buf, 20, stdin) != NULL)
{
if(strcmp(buf, "quitn") == 0)//fgets会读取换行符
break;
write(fd, buf, strlen(buf));//buf中字符串长度不确定,所以用strlen
}
4.3定位文件
4.3.1 lseek函数
#include4.4 思考off_t lseek(int fd, off_t offset, int whence); 参数: 文件描述符 偏移量//同fseek 基准点//同fseek 返回值: 成功返回当前文件的读写位置(基准点+偏移量);出错时返回-1
利用文件IO实现文件的复制 — 文件名通过命令行参数确定
#include5. 目录操作和文件属性 5.1 读取目录 5.1.1 opendir函数#include #include //定义缓冲区 #define N 64 int main() { int fds, fdt, n;//定义两个文件描述符,n每次读取的字节数 char buf[N]; //检查命令行参数 if(argc < 3) { printf("Usage: %s n",argv[0]); return -1; } //只读方式打开源文件 if((fds = open(argv[1], O_RDONLY)) == -1) {//往标准错误流中打印错误信息 fprintf(stderr, "open %s : %sn", argv[2], strerror(errno)); return -1; } //只写方式打开目标文件 if((fds = open(argv[1], O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1) {//往标准错误流中打印错误信息 fprintf(stderr, "open %s : %sn", argv[2], strerror(errno)); return -1; } //将从源文件中读取到的n个数据存入buf while((n = read(fds, buf, N)) > 0) { write(fdt, buf, n);//把缓冲区的内容写入目标文件,读多少写多少,用n不用N } close(fds); close(fdt); return 0; }
打开一个目录文件
#include5.1.2 readdir函数DIR *opendir(const char *name); //DIR是一个结构体类型,用来描述一个打开的目录文件 参数:打开目录文件的路径 返回值:成功返回目录流指针;出错时返回NULL;
读取目录流中的内容
#include5.1.3 closedir函数struct dirent *readdir(DIR *dirp); 结构体中包含成员char d_name[256] 存放目录项中文件的名称 struct dirent { ino_t d_ino; off_t d_off; unsigned short d_reclen; unsigned char d_type; char d_name[256]; }; 参数:opendir打开的目录流指针 返回值:一个结构体指针(用来描述目录流中的目录项) //需要用循环来读取直到结束 出错或到末尾时返回NULL;
关闭目录文件
#include5.1.4 示例int closedir(DIR *dirp); 参数:打开的目录流指针 返回值:成功时返回0;失败时返回-1
打印指定目录下的所有文件名称
DIR *dirp;
struct dirent *dp;
if(argc < 2){
printf("Usage: %s n",argv[0]);return -1;
}
if((dirp = opendir(argv[1])) == NULL){
perror("opendir");return -1;
}
while((dp = readdir(dirp)) != NULL){
printf("%sn", dp->d_name);
}
closedir(dirp);
5.2 修改文件访问权限
5.2.1 chmod/fchmod
#includeint chmod(const char *path, mode_t mode); //通过路径指定要修改的文件 int fchmod(int fd, mode_t mode); //通过文件按描述符指定要修改的文件---所以要先用open打开文件 返回值: 成功时返回0;出错时返回-1; //超级用户和文件所有者能修改文件权限
示例:
chmod("text.txt", 0666);
5.3 获取文件属性
5.3.1 stat/lstat/fstat 函数
#include5.3.2 结构体中存储的信息int stat(const char *pathname, struct stat *statbuf); //符号链接文件指向的文件的文件属性 int lstat(const char *pathname, struct stat *statbuf); int fstat(int fd, struct stat *statbuf); //符号文件本身的文件属性 参数: 路径 结构体指针(指向的地址保存获取的文件属性) int fstat(int fd, struct stat *statbuf); //通过文件描述符获取文件属性 参数: 文件描述符 结构体指针(指向的地址保存获取的文件属性) 返回值: 成功时返回0;出错时返回-1;
struct stat是存放文件属性的结构体类型
//常用文件属性如下;完整结构体可以man 2 stat查看
struct stat {
mode_t st_mode;//文件的类型和访问权限
uid_t st_uid;//文件所有者ID
uid_t st_gid//用户组ID
off_t st_size;//文件大小
time_t st_mtime;//文件最后修改时间
}
st_mode的文件类型获取—通过系统提供的宏来判断
S_IFREG(st_mode); //普通文件 S_IFDIR(st_mode); //目录文件 ...
st_mode的文件访问权限获取—通过系统提供的宏来判断
USR代表用户;GRP代表同组用户;OTH代表其他用户;
R、W、X分别代表可读、可写、可执行;1表示有相应权限,0表示无权限;
5.4 示例获取并显示文件属性;$ ./a.out text.c ;-rw-r–r-- 317 2014-11-08 text.c
#include库 1. 库的概念#include #include #include #include int main(int argc, char *argv[]) { //创建结构体保存文件属性 struct stat buf; int n; struct tm *tp;//将time获得的秒利用localtime转换为本地时间 //判断终端是否输入参数 if(argc < 2) { printf("Usage : %s n", argv[0]); return -1; } //获取指定文件的属性并保存到buf中 if(lstat(argv[1], &buf) < 0) { perror("lstat"); return -1; } //判断当前文件类型 switch(buf.st_mode & S_IFMT) { case S_IFREG: printf("-");//判断是否为普通文件 case S_IFDIR: printf("d");//判断是否位目录文件 //可以加case继续判断文件类型 } //判断文件权限(从第8位开始判断每一位是1是0) for(n = 8; n>=0; n--) { if(buf.st_mode & (1< //876.543.210 第8.5.2位表示读权限位,共同点是%3==2;同理可case1;case0; case 2: printf("r"); break; case 1: printf("w"); break; case 0: printf("x"); break; } else { printf("-"); } } //打印文件大小-无符号的长整型 printf(" %lu",buf.st_size); tp = localtime(&buf.st_mtime); printf(" %d-%02d-%02d", tp->tm_year+1900, tp->tm_mon+1, tp->tm_mday); printf(" %sn", argv[1]); return 0; }
一个二进制文件,包含的代码可以被程序调用。
根据库的不同功能可以分为标准c库、数学库、线程库等。
库有源码,可下载源码后编译,也可以直接安装二进制包。
库的目录/lib或/user/lib
库是事先编译好的,可以复用的代码。
在OS上运行的程序基本上都要使用库,可以提高开发效率。
Windows和Linux下库文件你的格式不兼容。
2. 库的分类Linux下包含静态库和动态库
3.静态库编译(链接)时把静态库中相关代码复制到可执行文件中
程序中已经包含静态库中的部分代码,运行时不再需要静态库;
程序运行时无需加载库,运行速度更快;
占用更多磁盘和内存空间;
静态库升级后,程序需要重新编译链接;
3.1 静态库的创建流程确定库中函数的功能、接口
3.2 编写库源码hello.c文件
#include3.3 编译生成目标文件void hello(void){ printf("hello wordn"); return; }
$gcc -c hello.c -Wall//先把 .c 生成对应的 .o3.4 创建静态库
$ar crs libhello.a hello.o//ar crs 库文件名称 库中包含的目标文件(可以多个) //库文件命名规则lib+name+.a name为库名 已经为2进制形式3.5 查看库中符号信息(可省略)
查看库中包含的函数名称
$nm libhello.a
hello.o:
0000000 T hello
U puts
3.6 链接静态库
text.c文件
#include3.7 编译void hello(void);//声明函数原型 int main(){ hello();//调用库函数 return 0;
编译text.c并链接静态库libhello.a
$ gcc -o text text.c -L. -lhello //L.添加库的搜索路径,'.'代表当前路径(hello这个库放在当前路径下,并没有放到系统默认的lib路径中,系统编译时只会去默认路径搜索) //l 指定要链接的库名,***而不是库文件名*** 不加-l 编译器默认链接c库 //-o 指定程序名称4. 动态库
编译(链接)时仅记录用到哪个共享库中的哪个符号,不复制共享库中相关代码;
程序不包含库中代码,尺寸小;
多个程序可共享一个库;
程序运行时需要加载库;
库升级方便,无需重新编译程序;
4.1 动态库的创建确定库中函数的功能、接口
4.2 编写库源码hello.c bye.c文件
#includevoid hello(void){ printf("hello wordn"); return; }
#include4.3 编译生成目标文件void hello(void){ printf("Byen"); return; }
$gcc -c -fPIC hello.c bye.c -Wall//先把 .c 生成对应的 .o //-fPIC 生成位置无关代码---生成的.o文件可以在任意位置执行4.4 创建共享库
$gcc -share -o libcommon.so.1 hello.o bye.o//gcc -shar -o 共享库文件名 库中包含的目标文件列表(可以多个) //lib+name+.so.X 数字X表示版本号 name为库名 已经为2进制形式4.5 为共享库创建符号链接文件
$ln -s libcommon.so.1 libcommon.so//ln -s 目标文件 创建的符号链接文件名称 //符号链接文件命名规则lib+name+.so 符号链接指向->实际的库文件4.6 调用共享库
text.c文件
#include#include "common.h" int main(){ hello(); bye();//调用库函数 return 0; }
common.h文件
void hello(void); void bye(void);4.7 编译
编译text.c并链接静态库libcommon.so
$ gcc -o text text.c -L. -lcommon //L.添加库的搜索路径,'.'代表当前路径(hello这个库放在当前路径下,并没有放到系统默认的lib路径中,系统编译时只会去默认路径搜索) //l 指定要链接的库名,***而不是库文件名*** 不加-l 编译器默认链接c库
先去链接动态库,再去链接静态库,如果都没有则报错;
-static //直接连接静态库
出错:共享库需要加载,系统没有在/lib或/user/lib中找到共享库,共享库在当前路径;
4.8 共享库加载修改当前share的环境变量;//可以添加库的指定搜索路径
$export LD_LIBRARY_path=$LD_LIBRARY_path:. //$引用原来的值:追加的新的路径 .代表当前路径
缺陷:只对当前的share有效;
修改系统的配置文件,把路径加到系统默认搜索路径;
*.conf文件存放库名称和库的路径信息 // 添加/etc/ld.so.conf.d/*.conf文件,执行ldconfig刷新
$sudo vi /etc/ld.so.conf.d/my.conf //在打开的文件中输入以下绝对路径并保存退出 /home/linux/io/share //绝对路径 $sudo ldconfig



