1 二进制IO(Binary IO)

在前一篇我们了解了逐字符读写和逐行读写函数。

如果我们在读写二进制文件,希望以此读写整个文件内容,这两个函数虽然可以实现,但是明显会很麻烦且多次循环明显效率很低。

为了应对这种场景,标准IO库提供了fread和fwrite函数。

函数声明:

#include

size_t fread(void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);

size_t fwrite(const void *restrict ptr, size_t size size_t nobj, FILE *restrict fp);

函数用法;

a) 读写一个数组。

float data[10];

if (write(&data[2], sizeof(float), 4, fp) != 4)

err_sys(“fwrite error");

本例中,从流fp中读取4个float型数据填入到数组下表从2到5得位置中。

b) 读写一个结构体

struct {

short  count;

long   total;

char   name[NAMESIZE];

} item;

if (fwrite(&item, sizeof(item), 1, fp) != 1)

err_sys(“fwrite error");

本例中,从fp读取数据填入到一个结构体中。

上面两例都可以认为是读写一个结构体的数组,参数size是结构体的长度,参数nobj是数组中要读写的元素的个数。

函数返回值:

两个函数的返回值都是读写的元素个数。

对于读函数,返回值可能会比nobj小,如果有异常抛出或者读到了文件结尾。这时需要调用函数ferror或feof来判断。

对于写函数,返回值比nobj小,则一定是有异常抛出。

函数细节:

在上面的例子中,我们通过fwrite函数填充了一个结构体,那么如果读写不在一个系统中,那么结构体的内存布局可能并不相同,这对于现在的多系统互联工作的场景下很常见。我们会在讨论socket时回来继续看这个问题,实际的解决方案就是在不同系统间读写二进制数据时使用相同的协议。

2 定位流(Positioning a Stream)

我们有三种方法对流进行定位:

函数ftell和fseek。将文件的当前偏移位置存储在long integer型变量中;

函数ftello和fseeko。将文件的当前偏移量存储在off_t型变量中;

函数fgetpos和fsetpos。使用数据类型fpos_t记录文件的当前偏移量。

ftell和fseek函数声明:

#include

long ftell(FILE* fp);    // Returns:current file position indicator if OK, -1L on error

int fseek(FILE* fp, long offset, int whence);       // Returns:0 if OK , -1 on error

void rewind(FILE* fp);

函数细节:

二进制文件的偏移量是从文件开始到当前位置的字节数;

ftell函数返回当前文件的偏移位置;

fseek函数用来定位文件到指定偏移位置;

fseek函数的参数whence,用来设置计算偏移量的方法:SEEK_SET表示从文件开头开始计算,SEEK_CUR表示从文件当前偏移位置开始计算,SEEK_END表示从文件结尾开始计算。

对于一些非Unix操作系统,存储文本文件的存储格式会有所不同,当前文件偏移量无法通过字节数来表示,这种情况下,参数whence需要设置为SEEK_SET,并且offset只有两个值可以使用:0,表示倒回文本开头;另一个可用值为函数ftell的返回值。

ftello和fseeko函数声明:

#include

off_t ftello(FILE* fp);     // Returns: current file position indicator if OK, (off_t) -1 on error

int fseeko(FILE* fp, off_t offset, int whence);     /// Returns: 0 if OK, -1 on error

函数细节:

这两个函数和上面的ftell和fseek功能相同,只是返回值类型不是long,而改成了off_t,实现上可以让off_t的表示范围更大。

fgetpos和fsetpos函数声明:

#include

int fgetpos(FILE* restrict fp, fpos_t *restrict pos);

int fsetpos(FILE* fp, const fpos_t pos);

函数细节:

fgetpos函数保存当前文件偏移量到参数pos中

fgetpos得到的pos可以用来使用fsetpos设置当前文件偏移量到之前的位置。

3 格式化输入输出

格式化输出函数

有五个printf函数负责格式化输出。

函数声明:

#include

int printf(const char *restrict format, ...);

int fprintf(FILE *restrict fp, const char *restrict format, ...);

int dprintf(int fd, const char *restrict format, ..);

// All three return : number of characters output if OK , negative value if output error

int sprintf(char *resrict buf, const char *restrict format, ...);

// Returns: number of characters stored in array if OK, negative value if encoding error

int snprintf(char *restrict buf, size_t n, const char *restrict format, ...);

// Returns: number of characters,that would have been stored in array if buffer was large enough, negative value if encoding error

函数细节:

printf输出到标准输出;

fprintf输出到指定的流中;

dprintf输出到指定的文件描述符中;

sprintf将格式化字符串写入到指定的buffer数组中,自动在结尾处加上一个null结尾符,但是不计入返回值中,并且,sprintf在buffer不够大时可能发生越界,因此需要使用者保证buffer足够大;

snprintf防止越界,在springf的参数中增加了buffer的大小参数,所有越界写入的字符都被忽略,如果返回值比buffer得长度要小,则说明输出没有被截断。

格式化输入函数

函数声明:

#include

int scanf(const char *restrict format, ...);

int fscanf(FILE *restrict fp, const char *restrict format, ...);

int sscanf(const char *restrict buf, const char *restrict format, ...);

函数细节:

format参数后面接得参数,包含存放读入字符串的变量地址。

更多关于格式化输入输出的细节可以自己查询Unix操作系统手册。

4 从流中获取文件描述符

函数声明:

#include

int fileno(FILE* fp);       // Returns: the file descriptor associated with the stream

如果我们需要调用dup和fcntl,则需要调用该函数。

5 临时文件(Temporary Files)

标准IO库提供了两个函数用于创建临时文件。

函数声明:

#include

char* tempnam(char *ptr);

FILE* tmpfile(void);

函数细节:

函数tmpnam生成一个字符串,该字符串为一个合法的路径名,并且不和任何已存在的文件重复。

函数tmpnam每次调用都生成不同的字符串,知道TMP_MAX次数。

如果函数tempnam的参数ptr为NULL,则生成的路径字符串存在内存静态区,函数返回值为指向该路径字符串的指针。如果随后再次使用null参数调用tempnam,会覆盖之前生成的字符串。

如果函数tempnam的参数ptr不是NULL,那么生成的路径字符串存在ptr指向的数组内,所以需要保证ptr指向的数组的长度至少为L_tmpnam。

函数tmpfile函数创建一个临时二进制文件(type wb+),程序终止或者该文件被关闭,则该文件自动被删除。对于UNIX操作系统而言,生成一个二进制文件并没有什么影响,因为内核并不区分文本文件还是二进制文件。

Example:

Code:

#include "apue.h"

int

main(void)

{

char    name[L_tmpnam], line[MAXLINE];

FILE    *fp;

printf("%s\n", tmpnam(NULL));       /* first temp name */

tmpnam(name);                       /* second temp name */

printf("%s\n", name);

if ((fp = tmpfile()) == NULL)       /* create temp file */

err_sys("tmpfile error");

fputs("one line of output\n", fp);/* write to temp file */

rewind(fp);                         /* then read it back */

if (fgets(line, sizeof(line), fp) == NULL)

err_sys("fgets error");

fputs(line, stdout);                /* print the line we wrote */

exit(0);

}

在系统The Single UNIX Specification定义了另外两个函数处理临时文件:

函数声明:

char* mkdtemp(char* template);    // Returns: pointer to directory name if OK, NULL on error

int mkstemp(char* template);    // Returns: file descriptor if OK, -1 on error

函数细节:

mkdtemp函数创建一个名字唯一的文件夹

mkstemp函数创建一个名字唯一的常规文件(regular file)

命名规则为 template + 六位随机字符

6 内存流(Memory Streams)

有的标准输入输出流并没有对应打开的硬盘文件,所有操作都是与内存中buffer进行数据交换,这些流被叫做内存流(memory streams)。

函数声明:

#include

FILE* fmemopen(void *restrict buf, size_t size, const char *restrict type);

// Returns: stream pointer if OK, NULL on error

函数细节:

参数buf指定使用的buffer,size为该buffer的大小,如果只指定size,而buf为null,那么fmemopen根据size的大小分配内存,由fmemopen分配的内存在流关闭时自动被释放;

参数type控制该流的功能.

7 总结

标准IO函数库被大多数UNIX应用使用。

在使用的时候,注意哪里使用了buffer来处理,因为这是容易引起迷惑的地方。

linux 流函数,标准IO函数库 - 二进制文件IO,流定位,创建临时文件和内存流相关推荐

  1. 【Linux系统编程学习】C库IO函数与系统IO函数的关系

    此为黑马Linux课程笔记. 1. C标准IO函数工作流程 如图,以C库函数的fopen为例,其返回类型是FILE类型的指针,FILE类型包含很多内容,主要包含三个内容:文件描述符.文件读写指针的位置 ...

  2. 标准C函数库头文件、POSIX标准库头文件和Windows API函数库头文件说明

    1. 标准C函数库头文件 名字 源自 描述 <assert.h> 包含断言宏,被用来在程序的调试版本中帮助检测逻辑错误以及其他类型的bug. <complex.h> C99 一 ...

  3. 标准C函数库的使用方法

    本篇介绍若干经常使用的标准C函数的使用方法,主要介绍stdio(标准输入输出).math(数字函数库).time(时间函数库).stdlib(标准函数库)string(标准字符串函数)等. 最后更新  ...

  4. C和指针_第16章_标准数函数库_学习笔记

    1.整型函数 这组函数返回整型数值.分为算术.随机数和字符串转换 1.1算术<stdlib.h> int abs( int value ); long int labs( long int ...

  5. libnet编译linux,求助,libnet函数库的应用问题?出现 undefined reference to `libnet_init'错误。...

    我在linux下已经安装了libnet函数库,可是在使用libnet_init()等函数的时候,用gcc编译总是显示 Undefined reference to "libnet_init& ...

  6. Linux中Shell脚本函数库的笔记

    Shell函数库的作用:函数库就是对一些十分常用的功能,独立出来,然后集中存放在一些独立的文件中,这些文件可以理解为函数库.函数库本质上也是函数,一般情况下函数库的函数建议使用下划线开头. 示例: c ...

  7. linux命令安装openssl函数库,ubuntu安装openssl库

    Linux版本:ubuntu14.04 输入以下命令: sudo apt-get install openssl sudo apt-get install libssl-dev sudo apt-ge ...

  8. linux临时文件创建失败,-bash: 无法为立即文档创建临时文件: 设备上没有空间

    Linux在使用中,突然出现空间不足情况,解决步骤大致为先清除进程,再删除文件 一.进程状况 1.查看磁盘空间和使用情况(​"df -h"显示目前磁盘空间和使用情况. " ...

  9. 【Linux】标准IO库

    Linux系统的文件IO都是针对文件描述符的,而标准IO(ISO C)的操作则是围绕流进行的,一个最明显的区别是标准IO比Linux文件IO多了缓冲机制.为了使用流,需要用到文件指针即指向FILE结构 ...

最新文章

  1. layer.open嵌入地址链接
  2. GitHub是如何征服Google、微软和其它公司的
  3. 使用 json_serializable (flutter packages pub run build_runner build) 问题
  4. Android 控件
  5. 【XSY2519】神经元 prufer序列 DP
  6. 压缩软件自动化测试,FOR…IN…ZIP循环——自动化测试精解(14)
  7. san分布式共享文件系统_基于SAN存储共享卷实现openstack高可用的方法与流程
  8. TypeScript 热度超 C 与 Python、Go 开发收入高、运维吃香,调查了 65000 名开发者有这些发现!...
  9. quartz mysql数据源_配置quartz数据源的三种方式
  10. 关于PV,流量和带宽
  11. 英文速记教程(Handwrite)
  12. 如何确认IAR软件有没有激活
  13. 工作随笔,一个java开发的一感慨
  14. MySQL的金科玉律:“不要使用SELECT *”
  15. i5处理器学计算机怎么设置,处理器怎么超频 酷睿i3/i5/i7系列CPU超频详细教程 (全文)...
  16. C#自定义控件添加到工具箱:
  17. 基于rdkit将smiles转换为smarts
  18. Linux多网卡绑定(bond)及网络组(team)
  19. 自学Java语言网络编程局域网内与电脑无线传输视频,图片文件,调用系统媒体播放视频图片文件
  20. JavaSE基础知识(附上代码实现)1

热门文章

  1. hdu2844 Coins(普通的多重背包 + 二进制优化)
  2. svchost.exe占用CPU 100%,也可能是这样的原因
  3. 如何优雅地测量一只猫的体积?
  4. LwIP之netbuf
  5. STM32之外部中断例程
  6. 如何成为一名糟糕的程序员?
  7. 消息队列之推还是拉,RocketMQ 和 Kafka是如何做的?
  8. 面试题:InnoDB 中一棵 B+ 树能存多少行数据?【面试不翻车,翻车就跑路】
  9. 如何区分普通感冒流感和新型冠状病毒肺炎?
  10. Docker - 安装并持久化PostgreSQL数据