【C++基础】fopen打开文件时二进制方式与文本文件方式的区别【2023.03.14】

7 篇文章 1 订阅
4 篇文章 0 订阅

摘要

C++打开文件时有个模式选项,该选项可以指定文件的访问方式,b字符表示二进制方式访问文件,不加b的表示文本方式访问文件。本文主要总结二者的区别。

FILE *fopen(const char *filename, const char *mode)

涉及术语

CR ,Carriage Return,中文名回车,对应十六进制编码0x0D,ASCII编码为\r。理解为控制台输出时光标移动到最左侧。

LF:Linefeed,中文名换行,对应十六进制编码0x0A,ASCII编码\n。理解为控制台输出时光标往下挪了一行。

CRLF组合起来就是光标移动到行首+光标下移一行。

区别

1、在处理换行上的区别

1.在windows系统中,文本模式下,文件以"\r\n"代表换行。若以文本模式打开文件,并用fputs等函数写入换行符"\n"时,函数会自动在"\n"前面加上"\r"。即实际写入文件的是"\r\n" 。xp操作系统的记事本程序必须包含"\r\n"显示时才能换行,高级版本只要"\n"即可换行。测试程序如下,管理员权限运行,使用notepad++打开即可看到编码。

// testFopen.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdio.h>
#include <stdarg.h>
#include <memory.h>

int _tmain(int argc, _TCHAR* argv[])
{
	FILE *fp;
	if ((fp = fopen("c:\\123.log", "a")) != NULL)//"a"文本文件追加的方式写入
	{
		fprintf(fp, "111111\n");
		fclose(fp);
	}

	if ((fp = fopen("c:\\123.log", "ab")) != NULL)//"a"文本文件追加的方式写入
	{
		fprintf(fp, "111111\n");
		fclose(fp);
	}
	return 0;
}


2.在类Unix/Linux系统中文本模式下,文件以"\n"代表换行。所以Linux系统中在文本模式和二进制模式下并无区别。

2、在读取数据时对结束(EOF)的判断

1.二进制方式,程序指定大小就读指定大小,除了读到文件尾部,则不存在提前结束的情况。

2.文件文本方式读取二进制数据, 可能在文件结束之前将某段数据判定为文件末尾EOF

我遇到过一次的具体Bug场景是文件里存的结构体数组,循环读数据到结构体变量,每次读指定字节长度的数据,最终结构是乱码。错误原因就是使用了文本文件方式读写。

bug清楚的记得,尝试了很多方案,没有复现。2023-03-08

终于复现了 2023-03-12

直接上代码,注意+++标记处。windows有这个问题,是因为ctrl+z组合键被识别为程序的结束,对应的ASCII字符编码为26

// testFopen.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <stdio.h>
#include <stdarg.h>
#include <memory.h>
#include <iostream>


typedef struct _Data
{
	char arr[20];
	int nvar;
} Data;

int _tmain(int argc, _TCHAR* argv[])
{
	using namespace std;
	FILE *fp;
	if ((fp = fopen("c:\\123.log", "wb+")) != NULL)//"a"文本文件追加的方式写入  "w" 重新写
	{
		cout << "fwrite ---------------------------------------- " << endl;

		// for写入100个结构体变量的数据,每个数据占用sizeofData个字节
		cout << sizeof Data << endl;
		for (int i = 0; i < 100;i++)
		{
			Data data = { 0 };

			memcpy_s(data.arr, sizeof(data.arr), "Yang^z Naifeng",sizeof "Yang Naifeng");
			memset(&data.arr[1], 26, 2);
			
			data.nvar = 123450;
			fwrite(&data, sizeof Data, 1, fp);
		}
		//Data data2;
		//memset(data2.arr, '8', 10);
		//data2.arr[0] = 0;      //插入0
		//data2.arr[1] = 0;
		//data2.arr[2] = 0x0D;
		//data2.arr[3] = 0x0A;
		//data2.nvar = 678910;
		//fwrite(&data2, sizeof Data, 1, fp);
		//fclose(fp);
		fclose(fp);
	}


	/************************************************************************/
	/* +++  rb+  如果换成 r+就出错了!!! 因为文本文件格式 Ctrl+z 会被识别为文件的结束。 */
	/************************************************************************/
	if ((fp = fopen("c:\\123.log", "rb+")) != NULL)//"a"文本文件追加的方式写入
	{
		cout << "fread ---------------------------------------- "  << endl;
		
		fseek(fp, 0, SEEK_END);
		long ret = ftell(fp);
		fseek(fp, 0, SEEK_SET);
		for (int i = 0; i < 100; i++)
		{
			Data data;
			memset(data.arr, '0', 10);
			data.nvar = 0;
			auto ret = fread(&data, sizeof Data, 1, fp);

			//fgets((char*)&data, sizeof Data,  fp);

			printf( "data.var : %d", data.nvar );
			printf( "data1.arr: " );
			for (int i = 0; i < sizeof data.arr; i++)
			{
				printf("%c", data.arr[i]);
			}
			printf("\n");
		}
		
		fclose(fp);
	}

	char ch;
	cin >> ch;
	return 0;
}


建议

对于一切非"\n"结束的简单字符串存储方式,采用二进制读写。自己把size确定准确。文本文件方式读复杂数据不可靠。

fread读指定字节的数据遇到整数值26结束,fgets是读字符串,字符串的结束符会提前结束。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值