二进制I/O

数据写入到文件效率最高的是用二进制形式写入,二进制输出避免了在数值转换为字符串过程中,所涉及的开销和精度损失,但而精致并非人眼所能阅读,所以这个技巧只有当数据被另一个程序按顺序读取才能使用。

/*二进制读和写函数
buffer是指向用于保存数据的内存位置指针
size 是缓冲区中每个元素的字节数
count 是读取或写入的元素数量
stream是读写流*/
size_t fread(void *buffer, size_t size, size_t count, FILE *steam);
size_t fwrite(void *buffer, size_t size, size_t count, FILE *steam);

buffer被解释为一个或多个值的数组,count参数指定数组中有多少值,所以读取标量,count值应该为1。函数返回值是实际读取或者写入的元素(非字节)数目。如果遇到文件结尾或者错误,那么这个数字可能比请求的元素数目要小。

下面的代码先二进制写入浮点数到文件,然后再读出:

#include <stdio.h>
#include <string.h> int main(){
float array[2] = {1.1, 2.2};
float temp[2]; FILE *file = fopen("D:\\C\\writeBin.txt", "wb");
if(file == NULL){
printf("open error");
}
//二进制写入两个浮点数
if(fwrite(array, sizeof(float), 2, file) != 2){
printf("写入失败\n");
}
fclose(file); //二进制打开
file = fopen("D:\\C\\writeBin.txt", "rb");
//二进制读入两个浮点数
if(fread(temp, sizeof(float), 2, file) == 2){
printf("temp: %.2f %.2f\n", temp[0], temp[1]);
} return 0;
}

运行:

文件打开是二进制不可见的,所以乱码

刷新的定位函数:

fflush迫使一个输出流的缓冲区内的数据进行物理写入,不管它是否已经写满,原型如下:

int fflush(FILE *stream);

当需要把输出缓冲立即写入时可以使用这个函数

正常情况下,数据以线性的方式写入,后写入的数据在文件中是在之前写入的数据之后的,C语言支持随机访问I/O,可以任意顺序访问文件的不同位置。通过在读写前确定到文件中需要的位置来实现。下面两个函数:

//返回流当前位置,也就是下一个读取或者写入开始的位置,距离文件起始位置的偏移量
long ftell(FILE *stream);

二进制流中,返回值就是距离开始的字节数,文本流中,这个值表示一个位置,但它不一定是距离文件开始的字节数,因为系统会对行尾字符进行翻译转换,但是它的值都可以传递给fseek,作为距离开始位置偏移量。

//fseek函数允许你在一个流中定位,将改变下一个读入和写入操作的位置。
int fseek(FILE *stream, long offset, int from);

定位到起始位置之前是错误的,定位到文件尾之后写入将扩展这个文件,读取将导致“到达文件尾”信息。

#include <stdio.h>

int main()
{
float array[2] = {1.1, 2.2};
FILE *file = fopen("D:\\C\\writeBin.txt", "wb"); fwrite(array, sizeof(float), 5, file);
//偏移到seek_end之后,将扩展文件
fseek(file, 1000, 0);
fwrite(array, sizeof(float), 1, file); fclose(file);
return 0;
}

SEEK_SET,流起始位置

SEEK_CUR,流当前位置

SEEK_END,流尾部结束位置

对于二进制流,从SEEK_END进行定位可能不被支持,应该避免

对于文本流,因为会执行行末字符映射,所以文本文件的字节数可能和程序写入的字节数不同,所以无法通过SEET_CUR和SEEK_END准确定位,所以用这个两个位置时offset应该为0,如果是从SEEK_SET,offset必须是ftell的返回偏移位置。

如果使用fseek,将导致三个副作用,

1.行尾指示字符被清除

2.ungetc把字符返回流中,那么这个被退回的字符将被丢掉,因为在定位操作后,它不再是下一个字符了。

3.定位允许从写模式切换到读取模式

#include <stdio.h>

int main()
{
float array[2] = {1.1, 2.2};
float temp[1];
FILE *file = fopen("D:\\C\\writeBin.txt", "wb");
fwrite(array, sizeof(float), 5, file);
fseek(file, 1000, 0);
//更改写模式到读模式
fread(temp, sizeof(float), 1, file);
//打印1.1
printf("%lf\n", temp[1]);
fclose(file);
return 0;
}

运行:

//将读写指针设置为指定流的起始位置,同时清除流错误提示标志。
void rewind(FILE *stream);

fgetpos和tfsetpos分别是ftell和fseek的替代,但是不是标准定义的。

改变缓冲方式

当流打开后,没有进行任何其他操作时,可以通过下面的函数改变留的缓冲方式

//另设一个数组,用于对流进行缓冲
void setbuf(FILE *stream, char *buf);

为流自行指定一个缓冲,大小为BUFSIZ,定义在stdio.h中,可以防止I/O函数库为他动态分配缓冲,如果使用NULL调用,则关闭所有缓冲方式

int setvbuf(FILE *stream, char *buf, int mode, size_t size);

mode参数用于设置缓冲模式,_IONBF指定不缓冲,__IOLBF,行缓冲,每遇到换行符,缓冲刷新,__IOFBF指定一个完全缓冲流。

流错误函数

//遇到文件尾,返回真,可以通过fseek和rewind清除
int feof(FILE *stream)
//报告流错误状态,如果出现错误返回真
int ferror(FILE *stream);
//对流错误标志重置
void clearerr(FILE *stream);

临时文件和文件操纵

//创建一个文件,wb+打开,程序关闭后自动删除,不会与已存在文件同名
FILE *tmpfile(void);
//删除文件,成功返回0
int remove(char const *filename);
//重命名,成功返回0
int rename(char const*oldname, char const * newname);

重命名后删除文件:

#include <stdio.h>

int main()
{
printf("%d\n", rename("writeBin.txt", "newName"));
printf("%d\n", remove("newName"));
return 0;
}

运行

C和指针 第十五章 二进制I/O的更多相关文章

  1. C和指针 第十五章 文件I/O

    stdio.h中包含了声明FILE结构 struct _iobuf { char *_ptr; //文件输入的下一个位置 int _cnt; //当前缓冲区的相对位置 char *_base; //指 ...

  2. C和指针 第十五章 习题

    15.8 十六进制倾印码 #include <stdio.h> #include <stdlib.h> #include <string.h> #include & ...

  3. C和指针 第十五章 输入输出缓冲

    对于C,所有的I/O操作都只是简单的从程序移进或移出字节,这种字节流便成为流(stream),我们需要关心的只是创建正确的输出字节数据,以及正确的输入读取数据,特定的I/O设备细节都是对程序隐藏的. ...

  4. C和指针 第十五章 错误报告perror和exit

    15.1 错误报告 perror 任何一种程序都存在出错的可能,包括系统的函数库,当出现错误时,系统提示发生错误,标准库函数在一个外部整型变量中保存错误代码,然后把错误代码传给用户程序,提示错误原因. ...

  5. C++ Primer Plus学习:第十五章

    第十五章 友元.异常和其他 友元 友元类 表 0-1 class Tv { public: friend class Remote; } Remote类可以使用Tv的数据成员,Remote类在Tv类后 ...

  6. 【C++】《C++ Primer 》第十五章

    第十五章 面向对象程序设计 一.OOP:概述 面向对象程序设计(OOP)的核心思想是数据抽象.继承和动态绑定. 通过使用数据抽象,可以将类的接口和实现分离. 使用继承,可以定义相似的类型并对其相似关系 ...

  7. 15第十五章UDF用户自定义函数(转载)

    15第十五章UDF用户自定义函数 待补上 原文链接 本文由豆约翰博客备份专家远程一键发布

  8. 《Linux命令行与shell脚本编程大全》 第十五章 学习笔记

    第十五章:控制脚本 处理信号 重温Linux信号 信号 名称 描述 1 HUP 挂起 2 INT 中断 3 QUIT 结束运行 9 KILL 无条件终止 11 SEGV 段错误 15 TERM 尽可能 ...

  9. CSS3秘笈复习:十三章&十四章&十五章&十六章&十七章

    第十三章 1.在使用浮动时,源代码的顺序非常重要.浮动元素的HTML必须处在要包围它的元素的HTML之前. 2.清楚浮动: (1).在外围div的底部添加一个清除元素:clear属性可以防止元素包围浮 ...

随机推荐

  1. Java并发包源码分析

    并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力.如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可交互性将大大改善.现代的PC都有多个CPU或一个CPU中有多个 ...

  2. 关于如何使用sourcetree将本地项目提交到远端github总结?

    使用sourcetree将本地项目提交到github里,目前来说还是很流行的,我也是听说好玩,所以来琢磨了一下,从环境搭建到配置好,差不多用了一下午加一晚上的时间,有点虐心,好吧,废话不多说,介绍一下 ...

  3. Intellij IDEA的一些东西

    Intellij IDEA的一些东西 2016-03-19 15:26 Ctrl + R 在当前文件进行文本替换 (必备) Ctrl + N 根据输入的 类名 查找类文件 Ctrl + Ctrl + ...

  4. Gearman使用示例

    最近的一个旧项目重构过程中,使用到了gearman这个开源项目,简单来讲,这是一个类似MQ的异步系统,一边派发任务,一边处理任务(有类似MQ中的消息发送方与接收方),目前支持java,php等多种语言 ...

  5. [LeetCode] Binary Watch 二进制表

    A binary watch has 4 LEDs on the top which represent the hours (0-11), and the 6 LEDs on the bottom ...

  6. C#进阶系列——WebApi 身份认证解决方案:Basic基础认证

    前言:最近,讨论到数据库安全的问题,于是就引出了WebApi服务没有加任何验证的问题.也就是说,任何人只要知道了接口的url,都能够模拟http请求去访问我们的服务接口,从而去增删改查数据库,这后果想 ...

  7. 数组为什么可以使用linq查询

    问题引出 这视乎是个完全不必要进行讨论的话题,因为linq(这里具体是linq to objects)本来就是针对集合类型的,数组类型作为集合类型的一种当然可以使用了.不过我还是想写一下,这个问题源于 ...

  8. docker 启动安装等命令

    确认是否安装url whereis curl 启动docker服务: sudo service docker start sudo service docker stop 安装curl sudo ap ...

  9. StringIO和BytesIO

    1. StringIO 很多时候,数据读写不一定是文件,也可以在内存中读写. StringIO顾名思义就是在内存中读写str. 要把str写入StringIO,我们需要先创建一个StringIO,然后 ...

  10. 常用SQL

    1. CEILING 向上取整2. FLOOR 向下取整3. FORMATmysql> SELECT FORMAT(12332.123456, 4); -> '12,332.1235'my ...