目录
char *fgets(char *s, int size, FILE *stream);
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
标准IO与文件IO
IO就是针对文件的输入与输出
文件分类
基本分类
1、文本文件(ASCII码文件),文件中的内容是用ASCII码或者字符来显示的 2、二进制文件:文件当中存放的内容是二进制数据。 两个文件在物理存储上没有差别(对于计算机来说都是二进制方式处理),逻辑有差别(文件的编码方式不同) linux系统文件分类: 普通文件:- 目录文件:d 管道文件:p 套接字文件:s 链接文件:l 块设备文件:b 字符设备文件:c
linux文件的操作
标准IO
标准IO是由ANSI c标准提供的c语言标准函数库 文件流:指数据像水流一样,所有的I/O操作仅是简单的从程序进或移除,这种字节流(比特流),就称为流。 FILE* 是指文件指针类型,在类型指向是一个结构体,该结构体描述了文件相关信息,每一个应用程序会默认定义三个文件流指针:stdio stdout、stderr stdin:标准输入流 stdout:标准输出流 stderr:标准错误流 man手册: man 1:查看shell命令 man 2:查看系统调用 man 3:查看c标准库
文件打开操作
1、打开 -- fopen FILE *fopen(const char *path, const char *mode); path:表示文件路劲字符串 mode:打开文件的方式 ”r“:只读打开,不会创建文件、文件流指针默认指向开头 ”r+“:读写打开,不会创建文件、文件流指针默认指向开头 “w”:只写打开,会创建文件(文件不存在时)或清空文件(文件存在),文件流指针默认指向开头 “w+”:读写打开,会创建文件(文件不存在时)或清空文件(文件存在),文件流指针默认指向开头 “a”:只写打开,会创建文件(文件不存在时)或追加内容(文件存在)。 “a+”:读写打开,会创建文件(文件不存在时)或追加内容(文件存在)。 ”b“:以二进制流方式,需要与前六个方式组合使用 返回值: NULL:表示错误返回 正确返回文件指针流:该指针可以认为指向一个文件(path路径文件),因此利用该指针就可以间接操作文件。
读取文件操作
int fgetc(FILE *stream)
int fgetc(FILE *stream) 从指定文件中读取一个字符, 并且将字符返回. stream: 返回值: EOF:文件末尾 或 错误 正确返回当前读取的字符
char *fgets(char *s, int size, FILE *stream);
char *fgets(char *s, int size, FILE *stream);//从指定文件读取一行,因为遇到‘\n’结束 意义: 从指定文件中读取一行数据. 当遇到'\n'表示一行读取结束. 1.当size的值小于等于文件中一行数据的个数时, 读取的实际字符数是size-1,最后会默 认加一个'\0' 2.当size-1的值 大于 文件中一行数据的个数时,遇到'\n'正常结束. 并且读取到的内容 包含'\n' 3.两种情况都必须以'\0'为结束标志. s: 数据首地址 size: 想要读取一行文件内容的字节数 stream: 文件流指针 返回值: NULL: 错误返回 正确返回数据首地址. fgets只能用于读取文本文件(.txt),可能把其他字符认识成‘\n’。
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 意义:按照指定的个数对文件进行数据读取.(这里的个数不是指字节数..) ptr: 数据首地址,可以为任意指针类型. size: 一个数据的字节大小 例子: 读取整数数据的话 size = sizeof(int) nmemb: 想要读取的数据个数. stream: 文件流指针 返回值: 错误 或 文件末尾: 0 或 比nmemb小的数字 正确返回成功读取到的数据个数. 因为fread函数不作错误返回还是结尾返回,如果要知道原因需要调用函数 判断出错或者是文件末尾可以使用以下函数(只有fread函数需要,只有读取需要判断): feof(): 判断是否到达文件末尾. 用法: if(foef(fp) != 0) ferror()://非零表示结尾退出 判断文件操作是否出错. 用法: if(ferror(fp) != 0)// 非零表示错误退出
写入文件
int fputc(int ch, FILE *stream):向文件写入一个字符. int fputs(const char *s, FILE *stream): 向文件写入一串数据. s表示数据的首地址. size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); 返回值: 错误: 0 或 比nmemb小的数字 正确返回成功写入的数据个数.
关闭----fclose()
int fclose(FILE *stream); 成功返回 0 失败返回 EOF
使用相关函数复制任意大小的文件(包括图片)
#include <stdio.h> //FILE *fopen(const char *pathname, const char *mode) //size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); int main(int argc, char *argv[]) { if(argc != 3) { printf("Parameter error"); return -1; } int buf[13] = {0}; int ret; FILE* fd1 = fopen(argv[1],"r"); if(NULL == fd1) { perror("why"); return -1; }else{ printf("%s open success\n",argv[1]); } FILE* fd2 = fopen(argv[2],"w+"); if(NULL == fd2) { perror("why"); return -1; }else{ printf("%s open success\n",argv[2]); } while(1) { ret = fread(buf,sizeof(char),sizeof(buf),fd1); if(0 == ret) { if(ferror(fd1) != 0 ) { perror("why"); return -1; }else{ printf("end of file\n"); break; } } ret = fwrite(buf,sizeof(char),ret,fd2); if(0 == ret) { perror("why"); return -1; } } fclose(fd1); fclose(fd2); return 0; }
二进制与文本
在物理结构上没有区别,因为存储在磁盘上都是以二进制存储。区别在逻辑上(编码不一样)。 Linux { 换行符: ‘\n’ 例如:abc‘\n’ 文本:abc‘\n’ 二进制:00110001 00110010 00110011 00001010 } woinds { '\r\n'相当于linux系统'\n'表示换行 回车:'\r' 0x0d 换行符:'\n' 0x0a 文本方式:会被解释为一个字符'\n' 二进制:会被解释层两个字符,正常读出 } 总结:在linux平台下,对文件操作时r与rb没区别 在wionds下有区别
主函数参数
int main(int argc, char * argv[]) { return 0; } argc:命令行参数个数 argv:指针参数,每一个元素指向每一个命令行参数。
命令行命令:eog 打开图片文件
其他标准IO
fseek()
fseek():可以修改文件的读写定位指针的指向位置。 int fseek(FILE *stream, long offset, int whence); stream:文件指针 long offset:偏移量。可以是负数。 whence: SEEK_SET(头), SEEK_CUR(当前), SEEK_END(结尾)。 返回值: 成功 :0 失败: -1
ftell()
ftell(),默认当前位置 long ftell(FILE *stream); 返回当前文件流定位的位置,可以用于求文件大小。 求文件大小: fseek(fd,0,SEEK_END); ret = ftell(fd);
rewind()
rewind(),默认回到开头。 void rewind(FILE *stream);
fprintf()
fprintf()//格式化的字符串写入文件函数,格式串写入指定文件 int fprintf(FILE *stream, const char *format, ...);
sprintf()
sprintf()格式化字符串函数,并将字符串存入指定地址空间 int sprintf(char *str, const char *format, ...); 根据自己的意愿产生字符串。
atoi() 字符串转整数
int atoi(const char *nptr);
Lniux文件IO
linux文件IO是linux系统提供的接口,所以又被称为系统调用。
与标准IO的区别:
c库函数是语言级函数,只要用c语言就可以用。系统调用由系统提供,跨系统就无法使用。标准IO带缓存,效率更高,大文件使用效率高。 标准IO 1、接口是由c标准(ANSI C标准)提供的,与语言及程序有关。 2、带缓存IO,又被称为高级IO。 3、可用于所有普通文件的操作。 文件IO 1、接口由操作系统(POSIX标准)提供,与操作系统有关。 2、低级IO,不带缓存。 3、linux系统下,有的特殊类型文件只能用文件IO操作,如:管道、设备文件等
缓存的分类
1、全缓存:对文件的读写,一般采用全缓存 缓存区满、程序结束、fflush()函数。 2、行缓存:标准输入与标准输出采用的缓存方式 刷新缓存区(缓存区数据写入文件stdout):遇到‘\n’、缓存区满、程序结束、fflush()函数。 3、无缓存:标准错误输出使用的方式 没有刷新条件,有数据马上输出。
行缓存大小 1k int main(int args, char **argv) { int i = 0; for (i = 0; i < 1025; ++i) /* 行缓存的边界值 1024:不能输出,i1025刚好可以输出*/ { fprintf(stdout, "a"); } while (1); }
全缓存大小 4k int main(int args, char **argv) { FILE *fp = NULL; if ((fp = fopen("./a.txt", "w+")) == NULL) { perror("19:fail to fopen"); exit(EXIT_FAILURE); } putc('a', fp); // 只有对文件进行读写操作了,buf才会为非0 printf("IO_cache = %d\n", fp->_IO_buf_end - fp->_IO_buf_base); }
文件IO接口(系统调用)
打开 open
int creat(const char *pathname, mode_t mode);创建 int open(const char *pathname, int flags);只打开(文件必须不存在) int open(const char *pathname, int flags, mode_t mode);创建并打开,需要指定第三个参数mode。(文件存在,要么清空,要么追加 flags)。 mode:设置文件的属性权限,使用八进制表示0664 pathname:文件路径名 flags:文件相关标志位,可以设置文件在程序中的读写权限 三个必须有一个(O_RDONLY, O_WRONLY, or O_RDWR.) O_APPEND 以追加方式打开文件 O_CREAT 以创建方式打开 O—TRUNC 清空文件 o_EXCL 检测文件是否已经存在与O_CREAT一起使用 O_NONBLOCK or O_NDELAY 非阻塞 错误:-1 正常:返回描述符(非负整数),是操作文件的句柄 open()
读取read
ssize_t read(int fd, void *buf, size_t count);从fd中读取count大小的数据到buf fd:文件描述符 buf:存储数据空间地址 count:读取字节数 返回值: 成功: 1、大于零,返回读取字节数 2、等于零,读到末尾 失败: -1
写入 write
ssize_t write(int fd, const void *buf, size_t count); fd:文件描述符 buf:存储数据空间地址 count:写入字节数 返回值: 成功:写入字节数 失败:-1
close 关闭
int close(int fd);关闭文件 成功:返回0 失败:返回-1
lseek 定位函数
off_t lseek(int fd, off_t offset, int whence); 成功:返回目前位置 失败:-1
复制文件(文件io)
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include<string.h> //int open(const char *pathname, int flags, mode_t mode); //ssize_t read(int fd, void *buf, size_t count); //ssize_t write(int fd, const void *buf, size_t count); int main(int argc, char *argv[]) { char buf[13] = {0}; int ret,ret2; int fd1 = open(argv[1],O_RDONLY); if(-1 == fd1) { perror("why"); }else{ printf("open %s success\n",argv[1]); } int fd2 = open(argv[2],O_WRONLY|O_CREAT|O_EXCL,0664); if(-1 == fd2) { perror("why"); }else{ printf("open %s success\n",argv[2]); } while(1) { ret = read(fd1,buf,13); if(-1 == ret) { perror("why"); return -1; }else if(0 == ret) { printf("end if file\n"); break; }else{ printf("ret = %d\n",ret); } ret2= write(fd2,buf,ret); if(-1 == ret2) { perror("why"); return -1; } memset(buf,0,13); } close(fd1); close(fd2); return 0; }
perror()
perror():错误输出函数 void perror(const char *s) { 意义: 函数内部会自动检测错误类型并且输出 错误原因. }
获取文件属性
stat()/fstat()/lstata()
三个函数的意义都是获取文件相关属性 int stat(const char *path, struct stat *buf); { 该函数不能对链接文件进行处理。 struct stat 结构体内部包含文件属性的描述 path:文件路径 返回值: 出错:-1 成功:0 } int fstat(int fd, struct stat *buf); { 第一个参数需要传递文件描述符。 } int lstat(const char *path, struct stat *buf); { 可以对链接文件进行处理。 }
目录操作函数
DIR *opendir(const char *name); { 意义: 打开指定的目录. 返回值: 正确返回目录流指针. 错误返回NULL } struct dirent *readdir(DIR *dirp); { 意义: 依次读取目录中的文件信息. 返回值: NULL: 读取到目录流末尾 或 产生错误. 正确返回一个 概述当前目录文件 的结构体.(struct dirent) } getpwuid()、根据id获取用户结构体 getgrgid()、根据id获取用户主结构体 localtime()与time()连用
库的创建
可以看作是可执行文件的二进制形式,可以加载到内存运行 1、将模块功能封装成库,可以便于移植、复用、共享 2、保护源码、开放功能
静态库
1、是在编译阶段被链接 2、生成可执行文件体积较大(链接了静态库) 3、可执行文件的移植较方便(库已经链接好了) 编译静态库 { 用法: ar 【选项】【libxx.a】【目标文件1】【目标文件2】.... c: 创建一个库,不管以前是否已经存在. r: 替换库当中重名的、已经存在的模块. s: 建立索引方式,可以快速查找库中的模块. 例如: ar crs libmy.a fun1.o fun2.o 编译使需要指明库的路径,使用 -L 与 -l来完成 例如: gcc test.c -L./ -lmyfun -L./day2/ -lmyfun2 -L: 表示需要链接库,后续跟库的路径 -l: 紧跟库的名字 名字不需要lib 也不需要.a }
动态库
1、是在运行阶段被链接. 2、生成的可执行文件体积较小. 3、可执行文件的移植不方便(运行时需要库,所以还要移植库) 编译动态库 { 用法: 先生成目标文件: gcc -c -fPIC fun.c -fPIC: 创建与地址无关的编译程序.(表示当动态库里的模块被链接到程序中时,放在任意地址都 可以) 动态库: gcc -shared -o libmyfun.so fun1.o fun2.o.. 程序运行时链接动态库方式: 1、将动态库移动到/usr/lib 或 /lib下(注意: 避免覆盖系统自带的库) 2、临时修改环境变量LD_LIBRARY_PATH=./ ./a.out 只在当前这一句有效. 3、添加/etc/ld.so.conf.d/*.conf文件,把库所在的路径加到文件末尾,并执行 ldconfig刷新• 这样,加入的目录下的所有库文件都可见. }