一、标准IO
解释:用户应用程序可以直接调用系统驱动,实现操作。但是存在着多种多样的系统,因此需要一个统一的标准来来适配所有的系统,因此通过c库函数来调用系统内核驱动。
全缓冲
当流的缓冲区无数据或无空间时才执行实际I/O操作
行缓冲
当在输入和输出中遇到换行符(‘\n’)时,进行I/O操作
当流和一个终端关联时,典型的行缓冲
无缓冲
数据直接写入文件,流不进行缓冲
标准预定义I/O
文件的打开和关闭
标准I/O – fopen – mode参数
标准I/O – 处理错误信息
extern int errno;
void perror(const char *s);
char *strerror(int errno);
errno 存放错误号,由系统生成
perror先输出字符串s,再输出错误号对应的错误信息
strerror根据错误号返回对应的错误信息
标准I/O – 关闭文件
int fclose(FILE *stream);
fclose()调用成功返回0,失败返回EOF,并设置errno
流关闭时自动刷新缓冲中的数据并释放缓冲区
当一个程序正常终止时,所有打开的流都会被关闭。
流一旦关闭后就不能执行任何操作
文件读写
字符的输入(读单个字符):
int fgetc(FILE *stream);
int getc(FILE *stream); //宏
int getchar(void);
成功时返回读取的字符;若到文件末尾或出错时返回EOF(-1),
getchar()等同于fgetc(stdin)
getc和fgetc区别是一个是宏一个是函数
注意事项:
1函数返回值是int类型不是char类型,主要是为了扩展返回值的范围。
2 stdin 也是FILE *的指针,是系统定义好的,指向的是标准输入(键盘输入)
3 打开文件后读取,是从文件开头开始读。读完一个后读写指针会后移。读写注意文件位置!
4 调用getchar会阻塞,等待你的键盘输入
字符的输出(写单个字符):
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
int putchar(int c);
成功时返回写入的字符;出错时返回EOF
putchar©等同于fputc(c, stdout)
注意事项:
1返回和输入参数都是int类型
2遇到这种错误:Bad file descriptor, 很可能是文件打开的模式错误(只读模式去写,只写模式去读)
行输入(读取整个行)
char *gets(char *s); 读取标准输入到缓冲区s
char *fgets(char *s, int size, FILE *stream);
成功时返回s,到文件末尾或出错时返回NULL
遇到’\n’或已输入size-1个字符时返回,总是包含’\0’
注意事项:
1 gets函数已经被淘汰,因为会导致缓冲区溢出
2 fgets 函数第二个参数,输入的数据超出size,size-1个字符会保存到缓冲区,最后添加’\0’,如果输入数据少于size-1 后面会添加换行符。
行输出(写整行)
int puts(const char *s);
int fputs(const char *s, FILE *stream);
成功时返回非负整数;出错时返回EOF
puts将缓冲区s中的字符串输出到stdout,并追加’\n’
fputs将缓冲区s中的字符串输出到stream,不追加 ‘\n’
1 #include<stdio.h>
2 #include<string.h>
3 #include<stdlib.h>
4
5 int main(){
6 FILE* fp;
7 char data[100]={"abcdef"};
8 int ret;
9 char* r;
10 fp=fopen("1.txt","r+");
11 if(fp==NULL){
12 perror("fopen");
13 return -1;
14 }
15
16 ret=fputs(data,fp);
17 if(ret==-1){
18 perror("fputs");
19 }
20
21 // r=fgets(data,5,stdin);
22 r=fgets(data,10,fp);
23
24 if(r==NULL){
25 perror("fgets");
26 }
27 printf("fgets:%s\n",data);
28
29 if((ret=fclose(fp))==-1){
30
31 perror("fclose");
32 }
33
34 return 0;
35
二进制文件读写
文本文件和二进制的区别:
存储的格式不同:文本文件只能存储文本。
计算机内码概念:文本符号在计算机内部的编码(计算机内部只能存储数字0101001…,所以所有符号都要编码)
二进制读写函数格式:
size_t fread(void *ptr, size_t size, size_t n, FILE *fp);
void *ptr 读取内容放的位置指针
size_t size 读取的块大小
size_t n 读取的个数
FILE *fp 读取的文件指针
size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp);
void *ptr 写文件的内容的位置指针
size_t size 写的块大小
size_t n 写的个数
FILE *fp 要写的文件指针
注意事项:
文件写完后,文件指针指向文件末尾,如果这时候读,读不出来内容。
解决办法:移动指针(后面讲解)到文件头;关闭文件,重新打开
1 #include<stdio.h>
2 #include<string.h>
3 #include<stdlib.h>
4 typedef struct {
5 char name[20];
6 int age;
7 char sex[8];
8
9 }student;
10
11 int main(){
12 student stu;
13 student stu_r;
14 strcpy(stu.name,"zhangsan");
15 stu.age=61;
16 strcpy(stu.sex,"male");
17
18 FILE* fp;
19 if((fp=fopen("1.bin","w+"))==NULL){
20 perror("fopen:");
21 return 0;
22 }
23 size_t ret;
24
25 ret=fwrite(&stu,sizeof(stu),1,fp);
26 if(ret==-1){
27 perror("fread");
28 return 0;
29 }
30 fclose(fp);
31
32 if((fp=fopen("1.bin","r"))==NULL){
33 perror("fopen");
34 return 0;
35 }
36
37 ret=fread(&stu_r,sizeof(stu),1,fp);
38 if(ret==-1){
39 perror("fread");
40 }
41 printf("name=%s age=%d sex=%s\n",stu_r.name,stu_r.age,stu_r.sex);
42 if((ret=fclose(fp))==-1){
43 perror("fclose");
44 return 0;
45 }
46
47 return 0;
48
49
50
51 }
文件流的刷新和定位
流的刷新
int fflush(FILE *fp);
成功时返回0;出错时返回EOF
将流缓冲区中的数据写入实际的文件
Linux下只能刷新输出缓冲区,输入缓冲区丢弃
如果输出到屏幕使用fflush(stdout)
流的定位:
long ftell(FILE *stream);
long fseek(FILE *stream, long offset, int whence);
void rewind(FILE *stream);
fseek 参数whence参数:SEEK_SET/SEEK_CUR/SEEK_END
SEEK_SET 从距文件开头 offset 位移量为新的读写位置
SEEK_CUR:以目前的读写位置往后增加 offset 个位移量
SEEK_END:将读写位置指向文件尾后再增加 offset 个位移量
offset参数:偏移量,可正可负
注意事项:
1.文件的打开使用a模式 fseek无效
2.rewind(fp) 相当于 fseek(fp,0,SEEK_SET);
3.这三个函数只适用2G以下的文件
编译告警错误:
ffseek_t.c:13:11: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘long int’ [-Wformat=]
printf(“current fp=%d\n”,ftell(fp));
表示参数类型不匹配
1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4
5 int main(){
6 FILE* fp;
7 int ret;
8 fp=fopen("1.txt","r+");
9 if(fp==NULL){
10 perror("fopen");
11 }
12
13 char data[10]={"abcdef"};
14
15 ret=fputs(data,fp);
16 printf("fputs location=%ld\n",ftell(fp));
17 if(ret==-1){
18 perror("fputs");
19 }
20 //fseek(fp,-2,SEEK_CUR);
21
22 fseek(fp,2,SEEK_SET);
23
24 printf("fseek location=%ld\n",ftell(fp));
25 ret=fputs("123",fp);
26
27
28 fclose(fp);
29
30 return 0;
31
32
33
34 }
格式化输出
int fprintf(FILE *stream, const char *fmt, …);
int sprintf(char *s, const char *fmt, …);
成功时返回输出的字符个数;出错时返回EOF
格式化输入
int fscanf(FILE *stream, const char *format, …);
int sscanf(const char *str, const char *format, …);
重点掌握sprintf 和sscanf
格式化输入输出练习
sysytime_t.c
1 #include<stdio.h>
2 #include<string.h>
3 #include<stdlib.h>
4 #include<time.h>
5 #include<unistd.h>
6 /*
7
8 struct tm {
9 int tm_sec;
10 int tm_min;
11 int tm_hour;
12 int tm_mday;
13 int tm_mon;
14 int tm_year;
15 int tm_wday;
16 int tm_yday;
17 int tm_isdst;
18 };
19
20 */
21
22
23 int main(){
24 FILE* fp;
25 time_t ctime_t;
26 struct tm* ctime;
27 int linecount=1;
28 char data[32]={0};
29 fp=fopen("time_t.txt","a+");
30 if(fp==NULL){
31 perror("fopen");
32 return -1;
33 }
34 while(fgets(data,32,fp)!=NULL){
35 if(data[strlen(data)-1]=='\n'){
36 linecount++;
37 }
38
39 }
40
41
42
43
44 while(1){
45 ctime_t=time(NULL);
46 //printf("%ld",ctime_t);
47 ctime=localtime(&ctime_t);
48 printf("%d-%d-%d %d:%d:%d\n",ctime->tm_year+1900,ctime->tm_mon+1,ctime->tm_mday,
49 ctime->tm_hour,ctime->tm_min,ctime->tm_sec);
50 fprintf(fp,"%d %4d-%2d-%2d %2d:%2d:%2d\n",linecount++,ctime->tm_year+1900,ctime->tm_mon+1,ctime->tm_mday,
51 ctime->tm_hour,ctime->tm_min,ctime->tm_sec);
52 fflush(fp);
53 sleep(1);
54
55 }
56 fclose(fp);
57 return 0;
58
59
60
61
62
63
64 }
二、文件IO
文件IO的概念
什么是文件IO,又称系统IO,系统调用
是操作系统提供的API接口函数。
POSIX接口 (了解)
注意:文件IO不提供缓冲机制
文件IO的API
open close read write
文件描述符概念:
英文:缩写fd(file descriptor)
是0-1023的数字,表示文件。
0, 1, 2 的含义 标准输入,标准输出,错误
文件IO 打开
open
int open(const char *pathname, int flags); 不创建文件
int open(const char *pathname, int flags, mode_t mode); 创建文件,不能创建设备文件
成功时返回文件描述符;出错时返回EOF
文件IO和标准的模式对应关系:
r O_RDONLY
r+ O_RDWR
w O_WRONLY | O_CREAT | O_TRUNC, 0664
w+ O_RDWR | O_CREAT | O_TRUNC, 0664
a O_WRONLY | O_CREAT | O_APPEND, 0664
a+ O_RDWR | O_CREAT | O_APPEND, 0664
umask概念:
umask 用来设定文件或目录的初始权限
文件的关闭
int close(int fd)
关闭后文件描述符不能代表文件
文件IO的读写和定位
容易出错点:
求字符串长度使用sizeof,对二进制数据使用strlen
printf 的字符最后没有’\0’
文件io测试代码
1 #include<stdio.h>
2 #include<string.h>
3 #include<unistd.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7
8 int main(){
9 int fd;
10 int ret;
11 fd=open("file_io.txt",O_RDWR | O_CREAT | O_APPEND,0664);
12 if(fd<0){
13 printf("open file failed\n");
14 return 0;
15 }
16 char data[32]={"hello wrold"};
17 char data2[32]={0};
18 ret=write(fd,data,strlen(data));
19 if(ret==-1){
20 printf("write failed\n");
21 goto END;
22 }
23 printf("write %d\n",ret);
24
25 lseek(fd,0,SEEK_SET);
26
27 ret=read(fd,data2,32);
28 if(ret==-1){
29 printf("read failed\n");
30 goto END;
31
32 }
33 data2[31]=0;
34 printf("ret=%d ,%s\n",ret,data2);
35 END:
36 close(fd);
37 return 0;
38
39 }
三、目录
打开目录
#include <dirent.h>
DIR *opendir(const char *name);
DIR *fdopendir(int fd); 使用文件描述符,要配合open函数使用
DIR是用来描述一个打开的目录文件的结构体类型
成功时返回目录流指针;出错时返回NULL
读取目录
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
struct dirent是用来描述目录流中一个目录项的结构体类型
包含成员char d_name[256] 参考帮助文档
成功时返回目录流dirp中下一个目录项;
出错或到末尾时时返回NULL
关闭目录
closedir函数用来关闭一个目录文件:
#include <dirent.h>
int closedir(DIR *dirp);
成功时返回0;出错时返回EOF
修改文件权限
chmod/fchmod函数用来修改文件的访问权限:
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);
成功时返回0;出错时返回EOF
注意:在vmware和windows共享的文件夹下,有些权限不能改变。
获取文件属性
#include <sys/stat.h>
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
成功时返回0;出错时返回EOF
如果path是符号链接stat获取的是目标文件的属性;而lstat获取的是链接文件的属性
四、库的概念:
静态库
创建静态库步骤:
1 . 编写库文件代码,编译为.o 目标文件。
2.ar 命令 创建 libxxxx.a 文件
ar -rsv libxxxx.a xxxx.o
注意:1 静态库名字要以lib开头,后缀名为.a
2 没有main函数的.c 文件不能生成可执行文件。
链接错误:
test.c:(.text+0x15):对‘hello’未定义的引用
collect2: error: ld returned 1 exit status
含义:表示hello函数在编译的源码内没有找到实现
解决:实现代码或者找到对应函数的库并且链接它。
链接静态库:
gcc -o 目标文件 源码.c -L路径 -lxxxx
-L 表示库所在的路径
-l 后面跟库的名称
动态库的生成步骤:
1.生成位置无关代码的目标文件
gcc -c -fPIC xxx.c xxxx.c … -c
2.生成动态库
gcc -shared -ols
libxxxx.so xxx.o xxx.o …
3.编译可执行文件
gcc -o 目标文件 源码.c -L路径 -lxxxx
执行动态库的可执行文件错误
./test: error while loading shared libraries: libmyheby.so: cannot open shared object file: No such file or directory
含义:可执行文件所使用的动态库找不到
解决办法:
找到动态库,添加到/usr/lib里面
或者使用export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:你的动态库目录
添加在~/.bashrc 文件里面
使用source ~/.bashrc 生效。
查看可执行文件使用的动态库:
ldd 命令 : ldd 你的可执行文件
root@haas-virtual-machine:/mnt/hgfs/share/newIOP# ldd test
linux-vdso.so.1 => (0x00007fff6548d000)
libmyheby.so => /mnt/hgfs/share/newIOP/day5/libmyheby.so (0x00007f5c89521000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5c89144000)
/lib64/ld-linux-x86-64.so.2 (0x000055fe52211000)
root@haas-virtual-machine:/mnt/hgfs/share/newIOP/day5# ldd test
linux-vdso.so.1 => (0x00007ffcb652c000)
libmyheby.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fbeeffaf000)
/lib64/ld-linux-x86-64.so.2 (0x0000561003c3b000)