Linux中的IO可分为三类:文件IO、标准IO和目录IO。
一、文件IO(系统调用函数)
是直接调用内核提供的系统调用函数,头文件是unistd.h
1)open — 打开或创建一个文件
open(char *,flag,mode)在fcntl.h文件中声明
函数作用:创建或打开某个文件
参数: char* 包含有文件名和路径
flag 打开文件的方式(只读,只写,读写)
mode 创建文件的权限
flag内容如下:
O_RDONLY:只读
O_WRONLY:只写
O_RDWR:读写
O_CREAT:创建一个文件
O_EXCL:如果使用O_CREAT时文件存在,则可返回错误消息,这一参数可测试文件是否存在
O_TRUNC:打开文件(会把已经存在的内容删除)
O_APPEND:追加方式打开文件(不会把已经存在的内容删除)
返回值:成功:文件描述符,它是一个非负的正整数,即文件的ID号
出错:-1
文件描述符:内核映射到用户程序的文件ID号
ID号的规律:从0开始累加
程序进行时(进程),内核会自动打开3个文件描述符,0,1,2,分别对应,标准输入、输出和出错。这样在程序中,每打开一个文件,文件描述符值从3开始累加。
open函数创建的权限是:
==mode & (~umask)
例:111 111 111B & ~(000 010 101B) = 111 101 101B
2)write(int fd,void *buf,size_t count)
第一个参数:向哪一个文件中去写
第二个参数:向这个文件中写什么内容
第三个参数:向这个文件中写多少个
返回值:是实际写的字节数
3)read(int fd,void *buf,size_t count)
第一个参数:从哪一个文件里去读
第二个参数:读到什么地方去
第三个参数:读多少个
返回值:实际读的字节数
4)lseek(int fd,off_t offest,int whence)
作用:调整读写的位置指针
该函数的头文件:sys/types.h ,unistd.h
第一个参数:要调整的文件的文件描述符
第二个参数:偏移量,每一次读写操作所需要移动的距离,单位是字节的数量,可正可负(向前移,向后移)
第三个参数:当前位置的基点,有三个标志:
SEEK_SET:当前位置为文件的开头,新位置为偏移量的大小。
SEEK_CUR:当前位置为文件指针的位置,新位置为当前位置加上偏移量
SEEK_END:当前位置为文件的结尾,新位置为文件的大小加上偏移量的大小
返回值:成功:文件当前的位置
出错:-1
二、标准IO(C库函数)
是间接调用系统调用函数,头文件是stdio.h
三个缓存的概念(数组)
1)、每个程序中的缓存,就是用户想从内核读写的缓存(数组)——用户空间的缓存
2)、每打开一个文件,内核在内核空间中也会开辟一块缓存 —— 内核空间缓存
文件IO中的写即是将用户空间中的缓存写到内核空间的缓存中
文件IO中的读即是将内核空间的缓存写到用户空间的缓存中
3)、标准IO的库函数中也有一个缓存 —— 库缓存
文件IO 标准IO
open fopen
close fclose
lseek fseek,rewind
read 读写函数比较多(分全缓存、行缓存和无缓存)
write
1)、FILE *open(const char *path,const char *mode)
返回值:FILE* 文件流指针 —— 类似于文件IO中的文件描述符
FILE 定义:struct_IO_FILE,在/usr/include/libio.h,包含读写缓存的首地址、大小、位置指针等。
标准的输入流:stdin 0
标准的输出流:stdout 1
标准出错流: stderr 2
2)、三类读写函数
一类:行缓存 ——遇到新行符(\n)或写满缓存时,即调用系统调用函数将库函数写到内核
读:fgets,gets,printf,fprintf,sprintf
写:fputs,puts,scanf
一个字符:
读:fgetc,getc,getchar
写:fputc,putc,putchar
二类:无缓存 —— 只要用户调用这个函数,就会将其内容写到内核中
三类:全缓存 —— 只有写满缓存才调用系统调用函数
读:fread
写:fwrite
3)、fclose()
包含fflush()函数
调用成功返回0, 失败返回EOF,并设置errno
在该文件被关闭前,刷新缓存中的数据。如果标准IO库已经为该流自动分配了一个缓存,则释放此缓存
4)、刷新缓存函数:fflush(FILE *fp)
把库函数中的缓存的内容强制写到内核中
5)、无缓存:stderr
6)、调整读写位置指针函数:
fseek()参数与lseek()一样,但返回值不一样
lseek()的返回值是:当前文件的位置指针值
fseek()的返回值是:成功返回0,失败返回-1
rewind(FILE *fp)用于设定流的文件位置指示为文件开始,该函数调用成功无返回值。
rewind()等价于(void)fseek(fp,0,SEEK_SET)
ftell(FILE *fp) 用于取得当前文件位置,调用成功则为当前文件位置指示,出错为-1。
7)、行缓存的读写函数gets和puts
gets与fgets的区别:
gets()不能指定缓存的长度,这样可能造成缓存越界(若该行长度大于缓存长度),写到缓存之后的存储空间中,从而产生不可预料的后果。
gets()只能从标准输入中读
gets()并不将新行符存入缓存中,fgets()将新行符存入缓存中
puts与fputs的区别:
puts()只能向标准输出中写
puts()输出时会添加一个新行符,fputs()不会添加。
8)、fprintf、printf、sprintf
int fprintf(FILE *stream,“字符串格式”)
fprirntf 可以输出到文件中,也可以输出到显示器
printf 只能输出到显示器中
int sprintf(str*,“字符串格式”)
输出内容到一个字符串中
9)、一个字符读写函数fgetc和fputc
int fgetc(FILE *fp) ——>不是行缓存函数
功能:从文件中读取一个字符
返回值:正确为读取的字符,到文件结尾或出错则返回EOF
int fputc(int c,FILE *fp) ——>有缓存,但不是行缓存函数
功能:写一个字符到文件中
返回值:成功则返回输入的字符,出错返回EOF
10)、int feof(FILE *stream)
功能:判断是否已经到文件结束
返回值:到文件结束,返回为非0,没有则返回0
11)、int ferror(FILE *stream)
功能:判断是否读写错误
返回值:是读写错误,返回为非0,不是则返回0
12)、void clearerr(FILE *stream)
功能:清除流错误
memset(char *str,int n,int length)//清缓存函数
13)、全缓存的读写函数:fread和fwrite
size_t fread(void *ptr,size_t size,size_t nmemb,FILE *stream);
size_t fwrite(const void *ptr,size_t size,size_t nmemb,FILE *stream);
参数:ptr —— 写的内容
stream —— 写到哪里去
size —— 写的内容中,每一个单元所占的字节数
nmemb —— 写的内容中,有多少个单元数
总的字节数:size*nmemb
返回值:实际读写的单元数
三、Linux操作系统标准IO支持的函数库
1、静态库:libxxx.a,在编译时就将库编译进可执行程序中。
优点:程序的运行环境中不需要外部的函数库。
缺点:可执行程序大
2、动态库:又称共享库,libxxx.so,在运行时将库加载到可执行程序中
优点:可执行程序小
缺点:程序的运行环境中必须提供相应的库
函数库目录:/lib 或 /usr/lib
3、静态库的制作:
1)、将源文件生成目标文件:gcc -c -o file.o file.c
2)、静态函数库创建命令 :ar
ar -cr -o libfile.a file.o
-cr:表示当插入的模块file.o已经存在libfile.a中,则覆盖;反之ar显示一个错误消息
3)、如果从别处得到一个静态库libunknown.a,想知道其中包含哪些模块
用命令:ar -t libunknown.a
4)、静态库的编译:gcc -o main.c -L. -lfile(编译main.c就会把静态函数库整合进mian)
其中:-L 指定静态函数库的位置供查找,L后面有“.”,表示静态函数库在本目录下查找
-l 指定了静态函数库名,由于静态函数库的命名方式是lib***.a,其中的lib和.a忽略
5)、运行:./a.out
4、动态库的制作:
1)、生成目标文件:gcc -c -o file.o file.c
2)、动态函数库创建命令:
gcc -shared -fpic -o libsub.so file.o
-fpic:产生位置无关代码
-shared:生成共享库
3)、动态库编译:gcc -o main.c -L. -lsub
此时不能立即./a.out,因为在动态函数库使用时,会查找/usr/lib /lib目录下的动态函数库,而此时程序生成的库不在里面。
4)、方法一:将 libsub.so放到/usr/lib或lib中再运行./a.out
方法二:环境变量方法
假设libfile.so的路径是/home/my_project/c
执行命令:export LD_LIBRARY_PATH=/home/my_project/sub
再执行./a.out即可得到结果
没有学到函数库这一节的时候,总以为VI下的C语言分文件编写和windows下的编译环境是一样的,但试了之后,结果相当惨,当时懒得没有自己查,因为当时用ubuntu的时候也不多,现在确实是学到了,区别还是相当大的。
四、目录IO
1、两个头文件:#include "sys/types.h"
#include "dirent.h"
2、opendir —— 只能打开目录
mkdir —— 创建目录
readdir —— 读目录
rewinddir —— 调整位置指针
telldir —— 返回位置指针所在的位置
seekdir —— 调整位置指针到任何地方
closedir —— 关闭目录