level5 文件IO

1. 标准IO介绍及缓存区

标准IO介绍
  • ‌目的:完成Linux下的APP开发

  • ‌Linux下一切皆文件

  • Linux文件的种类 :

    •      常规文件
    •      目录文件
    •      字符文件
    •      块文件
    •      链接文件(相当于windows快捷方式)
 IO的概念
  • I :input 输入设备 比如键盘鼠标都是Input设备
  • O :output 输出设备 比如显示器,优盘,网口,既是输入也是输出
系统调用和库函数
  •     系统调用就是操作系统提供的接口函数.
  •     如果我们把系统调用封装成库函数就可以起到隔离的作用,提供程序的可移植性。
  •     Printf就是库函数然后调用了系统调用才在显示器上显示字符。
‌文件类型
  • ‌FILE 流(stream)

  • ‌标准IO采用结构体存放打开文件的相关信息

  • ‌文本流和二进制流在window系统中二进制流换行符:\n,文本流换行符:\r \n。Linux下换行符:\n

  • l 缓冲区的概念:为了减少操作IO设备的次数,提高运行效率,在内存里面设置的缓冲区。

  • 全缓冲:缓冲区满才输出

  • 行缓冲:遇到换行符输出

  • ‌流的缓冲类型:1 全缓冲,2 行缓冲,3 无缓冲

  • ‌标准IO流:stdin,stdout,stderr

  • 三种标准IO

  • 标准输入流(键盘)

    0

    STDIN_FILENO

    stdin

    标准输出流(显示器)

    1

    STDOUT_FILENO

    stdout

    标准错误流

    2

    STDERR_FILENO

    stderr

缓冲区实验
  • ‌while死循环时加上sleep()<unistd.h>释放CPU,否则一直占用

  • ‌printf要是输出必须行缓冲结束,比如换行或者行缓冲满

  • ‌行缓冲大小 :1024

  • 查看头文件方法:man 2 函数 ,或者 man 3 函数

2. 标准IO文件的打开和关闭

  • ‌打开就是占用资源,关闭就是释放资源

打开文件函数
  • ‌函数原型:FILE *fopen(const char *path,const char *mode);成功返回流指针,失败返回NULL

  • ‌mode参数

  • “r” 或 “rb”

    以只读方式打开文件,文件必须存在。

    “r+” 或 ”r+b”

    以读写方式打开文件,文件必须存在。

    “w” 或 “wb”

    以只写方式打开文件,若文件存在则文件长度清为0。若文件不存在则创建。

    “w+” 或 “w+b”

    以读写方式打开文件,其他同”w”。

    “a” 或 “ab”

    以只写方式打开文件,若文件不存在则创建;向文件写入的数据被追加到文件末尾。

    “a+” 或 “a+b”

    以读写方式打开文件。其他同”a”

‌处理错误信息函数
  • ‌函数原型:void  perror(const char * s) <stdio.h>

  • extern int errno;头文件 errno.h

  • ‌函数原型:char * strerror(errno)<string.h>

  • ‌perror和strerror 功能:打印系统的错误描述(注意:是系统错误,不是你自己代码错误)

文件关闭函数
  • ‌函数原型:int fclose(FILE *stream)

  • fclose()调用成功返回0,失败返回EOF(-1),并设置errno

  • ‌流关闭时自动刷新缓冲中的数据并释放缓冲区,比如:常规文件把缓冲区内容写入磁盘

  • ‌当一个程序正常终止时,所有打开的流都会被关闭

  • ‌fclose()函数的入参stream必须保证为非空,否则出现断错误。

3. 标准IO的读写

字符输入(读单个字符)
  • ‌‌函数原型:int  fgetc(FILE *stream);<stdio.h>

  • 函数原型:int  getc(FILE *stream);   //宏<stdio.h>

  • ‌函数原型:int  getchar(void);<stdio.h>

  • 成功时返回读取的字符;若到文件末尾或出错时返回EOF(-1)

  • ‌getchar()等同于fgetc(stdin)

  • ‌getc和fgetc区别是一个是宏一个是函数。宏(getc)速度快,但是保证调用的stream值不能改变。

  • ‌函数返回值是int类型不是char类型,主要是为了扩展返回值的范围。

  • ‌tdin 也是FILE *的指针,是系统定义好的,指向的是标准输入(键盘输入)

  • ‌打开文件后读取,是从文件开头开始读。读完一个后读写指针会后移。读写注意文件位置!

  • ‌调用getchar会阻塞,等待你的键盘输入

字符输出(写单个字符)
  • 函数原型:‌int  fputc(int c, FILE *stream);<stdio.h>

  • ‌函数原型:int  putc(int c, FILE *stream);<stdio.h>

  • ‌函数原型:int  putchar(int c);<stdio.h>

  • ‌成功时返回写入的字符;出错时返回EOF

  • ‌putchar(c)等同于fputc(c, stdout)

  • 遇到这种错误:Bad file descriptor,很可能是文件打开的模式错误(只读模式去写,只写模式去读)

行输入(读取整个行)
  • 函数原型:‌char  *gets(char *s);  读取标准输入到缓冲区s

  • ‌函数原型:char *fgets(char *s, int size, FILE *stream);

  • ‌成功时返回s,到文件末尾或出错时返回NULL

  • ‌遇到’\n’或已输入size-1个字符时返回,总是包含’\0’

  • ‌gets函数已经被淘汰,因为会导致缓冲区溢出

  • fgets 函数第二个参数,输入的数据超出size,size-1个字符会保存到缓冲区,最后添加’\0’,如果输入数据少于size-1 后面会添加换行符。

行输出(写整行)
  • 函数原型:‌int  puts(const char *s);<stdio.h>

  • 函数原型:‌int fputs(const char *s,  FILE *stream);<stdio.h>

  • 成功时返回非负整数;出错时返回EOF

  • ‌puts将缓冲区s中的字符串输出到stdout,并追加’\n’;fputs将缓冲区s中的字符串输出到stream,不追加  ‘\n’

4. 标准IO读写:二进制方式

二进制读写
  • ‌系统中的文件分为二进制文件和文本文件

  • ‌文本文件和二进制的区别:存储的格式不同:文本文件只能存储文本。

  • 计算机内码概念:文本符号在计算机内部的编码(计算机内部只能存储数字0101001....,所以所有符号都要编码)

‌二进制读写函数格式:
  • 函数原型:‌size_t fread(void *ptr, size_t size, size_t n, FILE *fp);<stdio.h> 

    • void *ptr  读取内容放的位置指针 ;

    • size_t size 读取的块大小;

    • size_t n 读取的个数;

    • FILE *fp  读取的文件指针

  • ‌函数原型:size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp);<stdio.h> 

    • void *ptr  写文件的内容的位置指针;

    • size_t size 写的块大小;

    • size_t n 写的个数;

    • FILE *fp  要写的文件指针

  • ‌文件写完后,文件指针指向文件末尾,如果这时候读,读不出来内容。解决办法:移动指针(后面讲解)到文件头;关闭文件,重新打开

5. 流刷新定位

  • 函数原型: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以下的文件

6. 格式化输入输出

格式化输出
  • 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

7. 标准IO练习

  • time()用来获取系统时间(秒数)

  • time_t time(time_t *seconds) 1970.1.1 0:0:0

  • localtime()将系统时间转换成本地时间

  • struct tm *localtime(const time_t *timer)

struct tm {

    int tm_sec; /* 秒,范围从 0 到 59 */

    int tm_min; /* 分,范围从 0 到 59 */

    int tm_hour; /* 小时,范围从 0 到 23 */

    int tm_mday; /* 一月中的第几天,范围从 1 到 31 */

    int tm_mon; /* 月份,范围从 0 到 11 */

    int tm_year; /* 自 1900 起的年数 */

    int tm_wday; /* 一周中的第几天,范围从 0 到 6 */

    int tm_yday; /* 一年中的第几天,范围从 0 到 365 */

    int tm_isdst; /* 夏令时 */

};

注意:
  • int tm_mon; 获取的值要加1是正确的月份

  • int tm_year; 获取的值加1900是正确的年份

获取文件内的所有行数量:

while(fgets(buf,32,fp)!=NULL){

    if(buf[strlen(buf)-1] =='\n'){ //注意判断是否是一行结束

        linecount++;

    }

}

  • 写完文件记得fflush ,写到磁盘里面去。

  • 标准IO磁盘文件的缓冲区一般为4096

  • 注意和标准输出的全缓冲区别,标准输出是1024

8. 文件IO(概念、打开、读、写、关闭)

  • 文件IO的概念:posix(可移植操作系统接口)定义的一组函数

  • Linux下, 标准IO基于文件IO实

  • 文件IO不提供缓冲机制

  • 标准IO和文件IO调用函数对比

  • 文件IO的API:open close read read

  • 文件描述符(file descriptor):每个打开的文件都对应一个文件描述符。文件描述符是一个非负整数。Linux为程序中每个打开的文件分配一个文件描述符。文件描述符从0开始分配,依次递增。

文件IO 打开
  • int open(const char *pathname, int flags); 不创建文件

  • int open(const char *pathname, int flags, mode_t mode); 创建文件,不能创建设备文件

原型

int open(const char *pathname, int flags, mode_t mode);

参数

pathname

被打开的文件名(可包括路径名)。

flags

O_RDONLY:只读方式打开文件。

这三个参数互斥

O_WRONLY:可写方式打开文件。

O_RDWR:读写方式打开文件。

O_CREAT:如果该文件不存在,就创建一个新的文件,并用第三的参数为其设置权限。

O_EXCL:如果使用O_CREAT时文件存在,则可返回错误消息。这一参数可测试文件是否存在。

O_NOCTTY:使用本参数时,如文件为终端,那么终端不可以作为调用open()系统调用的那个进程的控制终端。

O_TRUNC:如文件已经存在,那么打开文件时先删除文件中原有数据。

O_APPEND:以添加方式打开文件,所以对文件的写操作都在文件的末尾进行。

mode

被打开文件的存取权限,为8进制表示法。

  • 成功时返回文件描述符;出错时返回EOF

  • 真正建文件时的权限会受到umask 值影响,实际权限是mode-umaks

  • 可以打开设备文件,但是不能创建设备文件

  • umask概念:umask 用来设定文件或目录的初始权限

  • 文件或目录的初始权限 = 文件或目录的最大默认权限 - umask权限

文件的关闭
  • int close(int fd)#include  <unistd.h>

  • 关闭后文件描述符不能代表文件

文件IO的读写和定位
  • ssize_t  read(int fd,void *buf, size_t count); #include  <unistd.h>

  • 成功时返回实际读取的字节数;出错时返回EOF;读到文件末尾时返回0

  • count不应超过buf大小

文件IO的写
  • ssize_t write(int fd,void *buf, size_t count); #include  <unistd.h>

  • 成功时返回实际写入的字节数;出错时返回EOF

文件IO的定位
  • off_t  lseek(int fd, off_t offset, intt whence);#include  <unistd.h>

9. 目录操作和文件属性获取

打开目录
  • DIR *opendir(const char *name); #include<dirent.h>

  • DIR *fdopendir(int fd);  #include<dirent.h>

  • 使用文件描述符,要配合open函数使用

  • DIR是用来描述一个打开的目录文件的结构体类型

  • 成功时返回目录流指针;出错时返回NULL

读取目录
  • struct dirent *readdir(DIR *dirp); #include <dirent.h>

  • struct dirent是用来描述目录流中一个目录项的结构体类型

  • 包含成员char d_name[256]

  • 成功时返回目录流dirp中下一个目录项;

  • 出错或到末尾时时返回NULL

关闭目录
  • closedir函数用来关闭一个目录文件

  • int closedir(DIR *dirp); #include <dirent.h>

  • 成功时返回0;出错时返回EOF

修改文件权限
  • chmod/fchmod函数用来修改文件的访问权限

  • int chmod(const char*path,mode_t mode); #include<sys/stat.h>

  • int fchmod(int fd, mode_t mode); #include  <sys/stat.h>

  • 成功时返回0;出错时返回EOF

  • 注意:在vmware和windows共享的文件夹下,有些权限不能改变。

获取文件属性
  • int stat(const char *path,struct stat *buf); #include  <sys/stat.h>

  • int lstat(const char *path,struct stat *buf);#include  <sys/stat.h>

  • int fstat(int fd, struct stat *buf); #include  <sys/stat.h>

  • 成功时返回0;出错时返回EOF

  • 如果path是符号链接stat获取的是目标文件的属性;而lstat获取的是链接文件的属性

10. 库的使用

  • 库的概念:库是一个二进制文件,包含的代码可被程序调用

  • Windows和Linux下库文件的格式不兼容

  • Linux下包含静态库和共享库

/lib   /usr/lib

静态库
  • 静态库特点

    • 编译(链接)时把静态库中相关代码复制到可执行文件中

    • 程序中已包含代码,运行时不再需要静态库

    • 程序运行时无需加载库,运行速度更快

    • 占用更多磁盘和内存空间

    • 静态库升级后,程序需要重新编译链接

  • 创建静态库步骤:

    • 1 . 编写库文件代码,编译为.o 目标文件。

    • 2. ar 命令 创建 libxxxx.a 文件

    • ar -rsv libxxxx.a xxxx.o

    • ar 参数:

      c禁止在创建库时产生的正常消息
      r如果指定的文件已经存在于库中,则替换它
      s无论 ar 命令是否修改了库内容都强制重新生成库符号表
      v将建立新库的详细的逐个文件的描述写至标准输出
      q将指定的文件添加到库的末尾
      t将库的目录写至标准输出
  • 静态库名字要以lib开头,后缀名为.a

  • 没有main函数的.c 文件不能生成可执行文件。

  • nm:显示指定文件中的符号信息 -a 显示所有符号

链接静态库:

  • gcc -o 目标文件 源码.c -L路径 -lxxxx

    -L 表示库所在的路径

    -l 后面跟库的名称(lib之后的名称)

动态库
  • 共享库特点

    • 编译(链接)时仅记录用到哪个共享库中的哪个符号,不复制共享库中相关代码

    • 程序不包含库中代码,尺寸小

    • 多个程序可共享同一个库

    • 程序运行时需要加载库

    • 库升级方便,无需重新编译程序

    • 使用更加广泛

  • 动态库的生成步骤:

    • 1. 生成与位置无关代码的目标文件

      • gcc -c  -fPIC  xxx.c xxxx.c ....   

    • 2. 生成动态库

      • gcc -shared -o libxxxx.so xxx.o xxx.o ....

    • 3. 编译可执行文件

      • gcc -o 目标文件 源码.c -L路径 -lxxxx

  •  为了让系统能找到要加载的共享库,有三种方法 :

    • 把库拷贝到/usr/lib和/lib目录下 

    • 在LD_LIBRARY_PATH环境变量中添加库所在路径(export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:你的动态库目录)

    • 添加/etc/ld.so.conf.d/*.conf文件,执行ldconfig刷新

  • 查看可执行文件使用的动态库:ldd 命令 : ldd 你的可执行文件

  • 静态库和动态库的区别

    • 1.静态库的扩展名一般为“.a”或“.lib”;动态库的扩展名一般为“.so”或“.dll”。

    • 2、静态库在编译时会直接整合到目标程序中,编译成功的可执行文件可独立运行;动态库在编译时不会放到连接的目标程序中,即可执行文件无法单独运行。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值