学习笔记(一)之I/O
输入:数据从内核空间-->用户空间 scanf
输出:数组从用户空间-->内核空间 printf
linux中文件类型:b:块设备文件,c:字符设备文件,d:目录文件,-:普通文件,l:链接文件,s:套接字文件,p:管道文件
标准IO 利用文件流指针对文件操作
1.C库调用(属于用户空间) man 3
2.移植性强 高级IO
3.自带缓存
行缓存 标准输出 stdout
1k 1024byte
刷新条件:1.'\n' 2.满了 3.程序正常退出 4.fflush
fflush
int fflush(FILE *stream);
1 #include <stdio.h> 2 3 4 5 int main(int argc, const char *argv[]) 6 { 7 printf("hello world"); 8 fflush(NULL); 9 while(1) 10 { 11 ; 12 } 13 return 0; 14 }
功能:按照参数刷新指定流,填NULL时刷新所有打开的输出流
返回值:成功返回0,失败返回EOF -1 并设置errno号
全缓存 标准输入 stdin
4k 4*1024byte
刷新条件:1.满了 2.程序正常退出 3.fflush 4.关闭流
无缓存 标准错误 stderr
流指针:
FILE * == struct _IO_FILE *
typedef struct _IO_FILE FILE;
对文件操作:
1.打开文件:fopen
FILE *fopen(const char *path, const char *mode);
参数:1.文件所在路径(字符串形式) 2.对文件的访问权限(字符串形式)
mode:
r:只读打开文件,文件指针指向文件开头,若文件不存在,报错
r+:读写打开文件,文件指针指向文件开头,若文件不存在,报错
w:只写打开文件,文件存在,清空文件,文件不存在,创建该文件,文件指针指向文件开头
w+:读写打开文件,文件存在,清空文件,文件不存在,创建该文件,文件指针指向文件开头
a:如果文件不存在,创建该文件,文件存在,再文件末尾追加 (文件指针指向文件末尾)
a+:读和追加模式,如果文件不存在,创建该文件,文件存在,在文件末尾追加 (指针指向文件末尾) 读操作时,从文件开头读
返回值:成功返回该文件的文件流指针,失败返回NULL,并设置errno
errno:指定的一些错误 使用时加头文件:#include <errno.h>
strerror:将errno号对应的错误信息转换成字符串
char *strerror(int errnum);
1 #include <stdio.h> 2 #include <string.h> 3 #include <errno.h> 4 5 6 int main(int argc, const char *argv[]) 7 { 8 //打开文件 9 FILE *fp = NULL; 10 fp = fopen("./444.txt","a+"); 11 if(fp == NULL) 12 { 13 fprintf(stderr,"fail to fopen:%s\n",strerror(errno)); 14 return -1; 15 } 16 17 //关闭文件 18 fclose(fp); 19 20 return 0; 21 }
fprintf
int fprintf(FILE *stream, const char *format, ...);
功能:将信息打印到指定的流上
参数:1.指定流 2.格式控制串 3.输出表
注:fprintf(stdout,....) == printf(....)
容错函数:perror
void perror(const char *s);
参数:自己书写的报错信息
1 #include <stdio.h> 2 #include <string.h> 3 4 5 int main(int argc, const char *argv[]) 6 { 7 //打开文件 8 FILE *fp = NULL; 9 fp = fopen("./111.txt","r"); 10 if(fp == NULL) 11 { 12 perror("fail to fopen"); 13 return -1; 14 } 15 16 //关闭文件 17 fclose(fp); 18 19 return 0; 20 }
关闭文件:fclose
int fclose(FILE *fp);
功能:关闭指定文件
字符输入输出:
fgetc:从指定流中获取单个字符
int fgetc(FILE *stream);
返回值:成功返回获取到字符的ascii码,失败返回EOF 注:1.error 2.文件末尾判断:feof
fgetc(stdin) == getchar()
1 #include <stdio.h> 2 3 4 5 int main(int argc, const char *argv[]) 6 { 7 int ret; 8 ret = fgetc(stdin); 9 printf("%c\n",ret); 10 return 0; 11 }
int feof(FILE *stream);
功能:判断是否到达文件末尾
返回值:到达文件末尾返回非0值
fputc:将单个字符输出到指定的流中
int fputc(int c, FILE *stream);
参数:1.字符的ascii码 2.指定的流
返回值:成功返回输出字符的ascii码,失败返回EOF
fputc(..,stdout) == putchar(..)
1 #include <stdio.h> 2 3 4 5 int main(int argc, const char *argv[]) 6 { 7 8 if(argc != 2) 9 { 10 fprintf(stderr,"fail to input filename\n"); 11 return -1; 12 } 13 14 FILE * fp = NULL; 15 int ret = 0; 16 17 fp = fopen(argv[1],"r"); 18 if(fp == NULL) 19 { 20 perror("fail to fopen"); 21 return -1; 22 } 23 24 while(1) 25 { 26 ret = fgetc(fp); 27 if(ret == -1) 28 { 29 if(feof(fp)) //判断是否到达文件末尾 30 { 31 break; 32 } 33 else 34 { 35 fprintf(stderr,"fail to fgetc\n"); 36 return -1; 37 } 38 } 39 40 fputc(ret,stdout); 41 42 } 43 44 fclose(fp); 45 return 0; 46 }
按行输入输出
fgets
char *fgets(char *s, int size, FILE *stream);
参数:1.应用层buf(用于存放fgets获取的一行数据) 2.安全枷锁(sizeof(buf)) 3.指定流
功能:从指定流中读取一行数据,并在末尾自动加上'\0',并且会将'\n'作为字符读入
返回值:成功返回获取字符串的首地址,失败返回NULL 注:1.error 2.判断文件末尾 feof
例如:
char a[10]
fgets(a,10,stdin); 除了'\0'做多只能存放9个字符
注:
1.fgets(a,sizeof(a),stdin); == gets(a);
2.fgets不能对图片操作,会损坏图片
gets :该函数可以淘汰了,没有安全性的检查,很容易造成数组越界导致段错误
char *gets(char *s);
fputs
int fputs(const char *s, FILE *stream);
参数:1.需要输出的内容 2.指定流
功能:将字符串输出到指定的流上,fputs末尾不会自动加'\n'
fputs(s,stdout); == puts(s);
1 #include <stdio.h> 2 3 4 5 int main(int argc, const char *argv[]) 6 { 7 char array[10]; 8 fgets(array,sizeof(array),stdin); 9 // gets(array); 10 fputs(array,stdout); 11 return 0; 12 }
清空函数:
#include <strings.h>
bzero
void bzero(void *s, size_t n); size_t == unsigned int
参数:1.需要被操作的空间的首地址 2.空间大小
功能:根据首地址将该空间清空
#include <string.h>
memset
void *memset(void *s, int c, size_t n);
参数:1.需要被操作的空间的首地址 2.想要变成的数值(例如'\0' 'a') 3.空间大小
功能:根据首地址将空间内的内容变成指定的内容int c
memset(a,0,sizeof(a)) == bzero(a,szieof(a))
#include <stdio.h> #include <strings.h> int main(int argc, const char *argv[]) { if(argc != 2) { fprintf(stderr,"fail to input filename\n"); return -1; } FILE * fp = NULL; char a[10]; fp = fopen(argv[1],"r"); if(fp == NULL) { perror("fail to fopen"); return -1; } while(1) { bzero(a,sizeof(a)); //memset(a,0,sizeof(a)); if(NULL == fgets(a,sizeof(a),fp)) { if(feof(fp)) { break; } else { fprintf(stderr,"fail to fgets!\n"); return -1; } } fputs(a,stdout); } fclose(fp); return 0; }
按块输入输出
fread
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
参数:1.应用层准备的buf(用于保存即将读取的数据) 2.单个块的大小
3.最多一次能搬运的块数sizeof(ptr)/sizeof(size) 4.指定流
返回值:成功返回读取的块数的个数 失败返回0 注:1.error 2.到达文件末尾feof
fwrite
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
参数:1.buf 2.单个块的大小 3.最多一次书写的块数(看fread的脸色,参照fread的返回值)(读多少写多少)
4.指定流
1 #include <stdio.h> 2 #include <strings.h> 3 4 5 int main(int argc, const char *argv[]) 6 { 7 8 if(argc != 2) 9 { 10 fprintf(stderr,"fail to input filename\n"); 11 return -1; 12 } 13 14 FILE * fp = NULL; 15 int ret; 16 char buf[10]; 17 18 19 20 fp = fopen(argv[1],"r"); 21 if(fp == NULL) 22 { 23 perror("fail to fopen"); 24 return -1; 25 } 26 27 while(1) 28 { 29 bzero(buf,sizeof(buf)); 30 ret = fread(buf,sizeof(char),sizeof(buf)/sizeof(char),fp); 31 if(ret < 0) 32 { 33 fprintf(stderr,"fail to fread!\n"); 34 return -1; 35 } 36 else if(ret == 0) 37 { 38 break; 39 } 40 else 41 { 42 fwrite(buf,sizeof(char),ret,stdout);//读多少写多少 43 } 44 } 45 46 fclose(fp); 47 return 0; 48 }
fseek:文件指针偏移
int fseek(FILE *stream, long offset, int whence);
参数:1.文件流指针 2.偏移量 +向后偏移 -向前偏移
3.相对位置 SEEK_SET 文件开头 SEEK_CUR 当前位置 SEEK_END 文件末尾
返回值:成功返回0,失败返回-1,并设置errno号
注:文件开头不能像前偏移,文件末尾也最好不要向后偏移(文件空洞)
ftell:告诉当前文件指针距离文件开头的偏移量
long ftell(FILE *stream);
rewind:将文件指针回归文件开头
void rewind(FILE *stream);
1 #include <stdio.h> 2 3 4 5 int main(int argc, const char *argv[]) 6 { 7 FILE *fp; 8 int ret; 9 fp = fopen("./log.txt","r+"); 10 if(fp == NULL) 11 { 12 perror("fail to fopen"); 13 return -1; 14 } 15 16 fseek(fp,10,SEEK_SET); 17 ret = ftell(fp); 18 printf("%d\n",ret); 19 20 rewind(fp); 21 printf("%ld\n",ftell(fp)); 22 23 return 0; 24 }
文件IO 利用文件描述符对文件操作
1.系统调用 man 2 注:系统调用:内核提供的函数接口
2.移植性差 低级IO
3.无缓存(内核缓存)
文件描述符:本质是一个整型数值 (其实是数组下标,是索引)
1.在当前操作系统中,一个程序打开的文件描述符取值范围0~1023 共1024个
2.默认打开的三个文件描述符: 0 stdin , 1 stdout , 2 stderr
3.每当打开新的文件时,分配的文件描述符总是最小且未使用的文件描述符
打开文件:open
int open(const char *pathname, int flags); 直接访问文件时使用
int open(const char *pathname, int flags, mode_t mode); 创建文件时使用
参数:1.文件路径(字符串形式) 2.旗帜
3.创建权限mode (在使用O_CREAT时才会使用) 直接书写想要的权限例如 0666 0777
注:最终真实得到的文件权限 mode & ~umask 当前目录下输入umask就能看到文件权限掩码
flag: 想要的功能通过 '|' 的方式组合
访问旗:O_RDONLY只读 O_WRONLY只写 O_RDWR读写 flag必须包含有且一个访问旗
创建旗:O_CREAT创建 O_EXCL测试是否文件存在(通常情况下与O_CREAT连用) O_TRUNC 清空
状态旗:后期学习
返回值:成功返回文件描述符,失败返回-1,并设置errno号
例子:
r:O_RDONLY | O_EXCL
w:O_WRONLY | O_CREAT | O_TRUNC
a:O_WRONLY | O_APPEND | O_CREAT
关闭文件:close
int close(int fd);
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 7 8 9 int main(int argc, const char *argv[]) 10 { 11 #if 0 12 //直接访问形式的open样例 13 int fd; 14 fd = open("./log.txt",O_RDONLY); 15 if(fd == -1) 16 { 17 perror("fail to open"); 18 return -1; 19 } 20 21 22 #endif 23 //创建时open样例 24 int fd; 25 int flag = O_WRONLY | O_CREAT | O_EXCL; 26 fd = open("./1.txt",flag,0666); 27 if(fd == -1) 28 { 29 perror("fail to open"); 30 return -1; 31 } 32 33 34 35 close(fd); 36 return 0; 37 }
输入输出函数:
read
ssize_t read(int fd, void *buf, size_t count);
参数:1.文件描述符 2.应用层准备的buf(用于接收数据) 3.buf的大小,一次最多读取的数据大小限制
返回值:成功时返回真实读取字节的个数 返回0时表示数据读取完毕,到达文件末尾
返回-1时代表错误,并设置errno号
write
ssize_t write(int fd, const void *buf, size_t count);
参数:1.文件描述符 2.应用层准备的buf(用于输出的数据)
3.和read连用时,该参数需要参照read的返回值,读多少写多少
返回值:-1表示出错,并设置errno号
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 #include <strings.h> 7 8 9 int main(int argc, const char *argv[]) 10 { 11 int fd1,fd2; 12 char buf[20]; 13 int ret_read; 14 fd1 = open("./log.txt",O_RDONLY); 15 if(fd1 == -1) 16 { 17 perror("fail to open1"); 18 return -1; 19 } 20 21 fd2 = open("./2.txt",O_WRONLY | O_CREAT | O_EXCL,0666); 22 if(fd2 == -1) 23 { 24 perror("fail to open2"); 25 return -1; 26 } 27 28 29 30 while(1) 31 { 32 bzero(buf,sizeof(buf)); 33 ret_read = read(fd1,buf,sizeof(buf)); 34 if(ret_read < 0) 35 { 36 perror("fail to read"); 37 return -1; 38 } 39 else if(ret_read == 0) 40 { 41 break; 42 } 43 else 44 { 45 write(fd2,buf,ret_read); //读多少写多少 46 } 47 } 48 49 close(fd1); 50 close(fd2); 51 return 0; 52 }
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 #include <strings.h> 7 8 9 int main(int argc, const char *argv[]) 10 { 11 int fd; 12 char buf[20]; 13 int ret_read; 14 fd = open("./log.txt",O_RDONLY); 15 if(fd == -1) 16 { 17 perror("fail to open"); 18 return -1; 19 } 20 21 while(1) 22 { 23 bzero(buf,sizeof(buf)); 24 ret_read = read(fd,buf,sizeof(buf)); 25 if(ret_read < 0) 26 { 27 perror("fail to read"); 28 return -1; 29 } 30 else if(ret_read == 0) 31 { 32 break; 33 } 34 else 35 { 36 write(1,buf,ret_read); //读多少写多少 37 } 38 } 39 40 close(fd); 41 return 0; 42 }
lseek:文件偏移
off_t lseek(int fd, off_t offset, int whence);
参数:1.文件描述符 2.偏移量 +向后偏移 -向前偏移 3.相对位置 SEEK_SET SEEK_CUR SEEK_END
功能:参照相对位置偏移大小
返回值:成功返回距离文件开头相差的字节数 失败返回-1并设置errno号
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 #include <unistd.h> 6 7 8 9 int main(int argc, const char *argv[]) 10 { 11 int fd; 12 off_t ret; 13 fd = open("./log.txt",O_RDONLY); 14 if(fd == -1) 15 { 16 perror("fail to open"); 17 return -1; 18 } 19 20 ret = lseek(fd,10,SEEK_SET); 21 22 printf("%d\n",(int)ret); 23 24 25 close(fd); 26 return 0; 27 }
练习:
time
localtime
1 #include <stdio.h> 2 #include <time.h> 3 #include <unistd.h> 4 5 6 int main(int argc, const char *argv[]) 7 { 8 time_t t; 9 struct tm *p = NULL; 10 char * week[] = {"星期日","星期一","星期二","星期三","星期四","星期五","星期六"}; 11 FILE * fp = NULL; 12 13 fp = fopen("./log.txt","a"); 14 if(fp == NULL) 15 { 16 perror("fail to fopen"); 17 return -1; 18 } 19 20 while(1) 21 { 22 t = time(NULL); 23 p = localtime(&t); 24 fprintf(fp,"%d-%d-%d %d-%d-%d %s\n" 25 ,p->tm_year+1900,p->tm_mon+1,p->tm_mday,p->tm_hour,p->tm_min,p->tm_sec,week[p->tm_wday]); 26 fflush(fp); 27 sleep(1); 28 } 29 30 31 32 fclose(fp); 33 return 0; 34 }
ctime
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <time.h> 4 5 6 7 int main(int argc, const char *argv[]) 8 { 9 time_t t; 10 while(1) 11 { 12 t = time(NULL); 13 printf("%s\n",ctime(&t)); 14 sleep(1); 15 } 16 return 0; 17 }
扩展:
opendir man 3
DIR *opendir(const char *name);
功能:打开目录文件
参数:目录路径
返回值:成功返回目录流指针,失败返回NULL,1并设置errno号
closedir
int closedir(DIR *dirp);
功能:关闭指定目录
readdir
struct dirent *readdir(DIR *dirp);
参数:目录流指针
功能:读取目录中的文件信息
返回值:成功返回记录该目录下文件信息的结构体指针struct dirent* 失败返回NULL,如果errno为0则代表读取到目录末尾如果不为0则error,并设置errno号
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* offset to the next dirent */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all file system types */
char d_name[256]; /* filename */
};
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <dirent.h> 4 #include <errno.h> 5 6 int main(int argc, const char *argv[]) 7 { 8 if(argc != 2) 9 { 10 fprintf(stderr,"please input dirname!\n"); 11 return -1; 12 } 13 14 DIR *dp; 15 struct dirent * p = NULL; 16 dp = opendir(argv[1]); 17 if(dp == NULL) 18 { 19 perror("fail to opendir"); 20 return -1; 21 } 22 23 while(1) 24 { 25 p = readdir(dp); 26 if(p == NULL) 27 { 28 if(errno == 0) 29 { 30 break; 31 } 32 else 33 { 34 perror("fail to readdir"); 35 return -1; 36 } 37 } 38 39 40 printf("%s\n",p->d_name); 41 42 } 43 44 closedir(dp); 45 return 0; 46 }
stat:获取文件详细信息 man 2
int stat(const char *path, struct stat *buf);
参数:1.文件路径2.文件信息结构体
注:(双向传参) 在应用层准备对应数据类型的空间,并将该空间首地址传入函数,函数执行结束后,内核将结果通过地址返回
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <dirent.h> 4 #include <errno.h> 5 #include <sys/stat.h> 6 #include <unistd.h> 7 #include <strings.h> 8 struct stat buf; 9 10 11 int fun(char *name); 12 13 14 int main(int argc, const char *argv[]) 15 { 16 if(argc != 2) 17 { 18 fprintf(stderr,"please input dirname!\n"); 19 return -1; 20 } 21 22 DIR *dp; 23 struct dirent * p = NULL; 24 //打开目录文件 25 dp = opendir(argv[1]); 26 if(dp == NULL) 27 { 28 perror("fail to opendir"); 29 return -1; 30 } 31 32 while(1) //循环读取目录中文件信息 33 { 34 p = readdir(dp); 35 if(p == NULL) 36 { 37 if(errno == 0) 38 { 39 break; 40 } 41 else 42 { 43 perror("fail to readdir"); 44 return -1; 45 } 46 } 47 //获取该目录下文件的名字,传递给fun函数执行 48 fun(p->d_name); 49 50 } 51 52 closedir(dp); 53 return 0; 54 } 55 56 //调用stat函数获取文件信息 57 int fun(char *name) 58 { 59 bzero(&buf,sizeof(buf)); 60 stat(name,&buf); 61 switch(buf.st_mode & S_IFMT) 62 { 63 case S_IFSOCK: 64 printf("s "); 65 break; 66 case S_IFLNK: 67 printf("l "); 68 break; 69 case S_IFREG: 70 printf("- "); 71 break; 72 case S_IFBLK: 73 printf("b "); 74 break; 75 case S_IFDIR: 76 printf("d "); 77 break; 78 case S_IFCHR: 79 printf("c "); 80 break; 81 case S_IFIFO: 82 printf("p "); 83 break; 84 } 85 86 printf("%ld %s\n",buf.st_size,name); 87 88 return 0; 89 }
库:
ELF文件 可执行可链接格式
静态库 在编译时需求 在运行时不需求
特点:编译生成的文件会较大,编译完成后运行时可以脱离静态库运行
静态库的制作:
1.gcc -c 需要被制作的文件.c -o 需要被制作的文件.o
例如:gcc -c fun1.c -o fun1.o
2.ar crs lib库名.a 需要被制作的文件.o
例如:ar crs libmyhello.a fun1.o
运行时链接库:
3.gcc xxx.c -o xxx -L(指定静态库所在路径)路径 -l库名
例如:gcc main.c -o hello -L. -lmyhello
动态库 在编译时不需求 在运行时需求
特点:编译时不需求动态库,生成的文件较小,但是运行时需要加载动态库的代码
动态库的制作:
1.gcc -fPIC -Wall -c 需要被制作的文件.c
例如: gcc -fPIC -Wall -c fun1.c
2.gcc -shared -o lib库名.so 需要被制作的文件.o
例如:gcc -shared -o libhqyj.so fun1.o
3.gcc xxx.c -L. -l库名
例如:gcc main.c -L. -lhqyj
4.mv lib库名.so /lib 或者 /usr/lib
例如:sudo mv libhqyj.so /lib