文件概念:一组相关数据的有序集合
文件类型:常规文件r
目录文件d
字符设备文件 c
块设备文件 b
管道文件 p
套接字文件 s
符号链接文件 L
标准I/O由ANSI C标准定义,主流操作系统上都实现了C库。标准I/O通过缓冲机制减少系统调用,实现更高的效率。
标准I/O用一个结构体类型来存放打开的文件的相关信息。标准I/O的所有操作都是围绕FILE来进行的
流(stream)FILE又被称为流(stream),文本流/二进制流
Windows
二进制流: 换行符< >’n’
文本流: 换行符< >’r’,’n’
Linux
换行符< >’n’
全缓冲(当流的缓冲区无数据或无空间时才执行实际I/O操作)
行缓冲(当在输入和输出中遇到换行符(‘n’)时,进行I/O操作。当流和一个终端关联时,典型的行缓冲)
无缓冲(数据直接写入文件,流不进行缓冲)
流的缓冲类型:stdin / stdout /stddir
Stdin/stdout默认时行缓冲
Stderr没有缓冲
写代码显示缓冲取大小
fopen()创建的文件访问权限是0666(rw-rw-rw-)
Linux系统中umask设定会影响文件的访问权限,其规则为(0666&~umask)
Root用户是022普通用户是002
用户可以通过umask函数或者命令修改相关设定
errno存放错误号,由系统生成
perror先输出字符串s,再输出错误号对应的错误信息
Strerror根据错误号返回对应的错误信息
Fclose()调用成功返回0,失败返回EOF,并设置errno
流关闭时自动刷新缓冲中的数据并释放缓冲区
当一个程序正常终止时,所有打开的流都会被关闭
流一旦被关闭后就不能执行任何操作。
程序中能够打开的文件或流的个数有限制
流支持不同的读写方式:
字符输入和输出:
读写一个字符:fgetc()/fputc()一次读/写一个字符
读写一行:fgets()和fputs()一次读/写一行
读写若干个对象:fread()/fwrite()每次读/写若干个对象,而每个对象具有相同的长度。
行输入和输出:
Char *gets(char *s);
Char *fgets(char *s,int size,FILE *stream);
成功时返回s,到文件末尾或出错时返回NULL。
Gets不推荐使用,容易造成缓冲区溢出
遇到’n’或已输入size-1个字符时返回,总是包含’
Int puts(const char *s);
Int fputs(const char *s,FILE *stream);
成功时返回输出的字符个数;出错时返回EOF
Puts将缓冲区s中的字符串输入到stdout,并追加’n’
fputs将缓冲区s中的字符串输出到stream,不追加’n’
Size_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);
成功返回读写的对象个数;出错时返回EOF。既可以读写文本文件,也可以读写数据文件效率高。
Int fflush(FILE *fp);
成功时返回0;出错时返回EOF
将流缓冲区中的数据写入实际的文件
Linux下只能刷新输出缓冲区,输入缓冲区丢弃
Long ftell(FILE *stream);
Long fseek(FILE *stream,long offset,int whence);
Void rewind(FILE *stream);
Ftell()成功时返回流的当前读写位置,出错时返回EOF
Fseek()定位一个流,成功时返回0,出错时返回EOF
Whence参数:SEEK_SET/SEEK_CUR/SEEK_END;
offset参数:偏移量,可正可负
Rewind()将流定位到文件开始位置
读写流时,当前读写位置自动后移
Int ferror(FILE *stream);
Int feof(FILE *stream);
Ferror()返回1表示流出错;否则返回0
Feof()返回1表示文件已到末尾;否则返回0
Int printf(const char *fmt,...);
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,...);
Time()用来获取系统时间(秒数)
Localtime()将系统时间转换成本地时间
Sleep()实现程序睡眠
什么是文件I/O?
Posix(可移植操作系统接口)定义的一组函数
不提供缓冲机制,每次读写操作都引起系统调用
核心概念是文件描述符
访问各种类型文件
Linux下,标准IO基于文件IO实现
每个打开的文件都对应一个文件描述符
文件描述符是一个非负整数。Linux为程序中每个打开的文件分配一个文件描述符
文件描述符从0开始分配,依次递增
文件IO操作通过文件描述符来完成
Open函数用来创建或打开一个文件:
#include
Int open(const char *path,int oflag,...);
成功时返回文件描述符;出错时返回EOF
打开文件时使用两个参数
创建文件时第三个参数指定新文件的权限
只能打开设备文件
Close函数用来关闭一个打开的文件:
#include
Int close(int fd);
成功时返回0;出错时返回EOF
程序结束时自动关闭所有打开的文件
文件关闭后,文件描述符不再代表文件
read函数用来从文件中读取数据:
#include
Ssize_t read(int fd,void *buf,size_t count);
成功时返回实际读取的字节数;出错时返回EOF;
读到文件末尾时返回0;
Buf是接收数据的缓冲区
Count不应超过buf大小
Write函数用来向文件写入数据:
#include
Ssize_t write(int fd,void *buf,size_t count);
成功时返回实际写入的字节数;出错时返回EOF
Buf是发送数据的缓冲区
Count不应超过buf大小
Lseek函数用来定位文件
#include
Off_t lseek(int fd,off_t offser,intt whence);
成功时返回当前的文件读写位置;出错时返回EOF
参数offset和参数whence同fseek完全一样
访问目录-opendir
Opendir函数用来打开一个目录文件:
#include
DIR *opendir(const char *name)
DIR是用来描述一个打开的目录文件的结构体类型
成功时返回目录流指针;出错时返回NULL
访问目录-readdir
Readdir函数用来读取目录流中的内容:
#include
Struct dirent *readdir(DIR *dirp)
Struct dirent是用来描述目录流中一个目录项的结构体类型
包含成员char d_name[256] 参考帮助文档
成功时返回目录流dirp中下一个目录项
出错或到末尾时返回NULL
访问目录-closedir
Closedir函数用来关闭一个目录文件:
#include
Int closedir(DIR *dirp)
成功时返回0,出错时返回EOF
修改文件访问权限-chmod/fchmod
Chmod/fchmod函数用来修改文件的访问权限:
#include
Int chmod(const char *path,mode_t mode);
Int fchmod(int fd,mode_t mode);
成功时返回0;出错时返回EOF
Root和文件所有者能修改文件的访问权限
获取文件属性-stat/lstat/fstat
Stat/lstat/fstat函数用来获取文件属性:
#include
Int stat(const chat *path,struct stat *buf);
Int lstat(const char *path,struct stat *buf);
Int fstat(int fd,struct stat *buf);
成功时返回0,出错时返回EOF;
如果path是符号链接stat获取的是目标文件的属性;而lstat获取的是链接文件的属性
文件属性-struct 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;最后修改时间
库是一个二进制文件,包含的代码可被程序调用
标准C库,数学库,线程库...
库有源码,可下载后编译;也可以直接安装二进制包
/lib /usr/lib
库是实现编译好的,可以复用的代码
在OS上运行的程序基本上都要使用库。使用库可以提高开发效率
Windows和Linux下库文件的格式不兼容
Linux下包含静态库和共享库
编译(链接)时把静态库中相关代码复制到可执行文件中
程序中已包含代码,运行时不再需要静态库
程序运行时无需加载库,运行速度快
占用更多磁盘和内存资源
静态库升级后,程序需要重新编译链接
静态库的创建
- 确定库中函数的功能、接口
- 编写库源码hello.c
#include
Void hello(void){
Printf(“hello worldn”);
Return;
}
- 编译生成目标文件
$gcc -c hello.c -Wall
- 创建静态库hello
$ar crs libhello.a hello.o
- 查看库中符号信息
$nm libhello.a
链接动态库
编写应用程序test.c
#include
Void hello(void);
Int main(){
Hello();
Return 0;
}
编译test.c并链接静态库libhello.a
$gcc -o test test.c -L. -lhello
$./test
Hello word
共享库的特点
编译(链接)时仅记录用到哪个共享库中的哪个符号,不复制共享库中相关代码
程序不包含库中代码,尺寸小
多个程序可共享同一个库
程序运行时需要加载库
库升级方便,无需重新编译程序
使用更加广泛
共享库创建
- 确定库中函数的功能、接口
- 编写库源码hello.c bye.c
#include
Void hello(void){
Printf(“hello worldn”);
Return;
}
- 编译生成目标文件
$gcc -c -fPIC hello.c bye.c -Wall
创建共享库common
$gcc -shared -o libcommon.so.1 hello.o bye.o
为共享库文件创建链接文件
$ln -s libcommon.so.1 libcommon.so
符号链接文件命名规则
Lib<库名>.so
如何找到共享库
为了让系统能找到要加载的共享库,有三种方法:
把库拷贝到/usr/lib和lib目录下
在LD_LIBRARY_PATH环境变量中添加库所在路径
添加/etc/ld.so.conf.d/*.conf文件,执行ldconfig刷新



