怎么样将unsigned char类型变量存入文件(如TXT文件),在从文件中读入,要求保持原来unsigned char

本文介绍如何使用C++处理二进制文件,包括如何将unsigned char类型的变量正确存储和读取,以及如何操作结构体变量。通过具体示例展示了文件流对象的read和write函数的使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于文件的操作可以看博客:http://blog.sina.com.cn/s/blog_661314940100o0ym.html


最近在编密码学上有限域GF(2^8),碰到一个问题:那就是把unsigned char类型的变量怎么存入一个txt文件后,使得再次从文件中读出这些数据时,保持这些值仍不变。

刚开始,我用文件流对象以文本文件的方式代开一个文件。然后用输出操作符“<<”输出unsigne char变量。但是再次用输入操作符“>>”读入这些变量时,得到的值却不是我所期望的值。要知道,我们的ASCII码的8位二进制的最高位为0。如果我把unsigned char类型变量存入文本文件的时候,对于他们高位为1的情况,就有可能在文本文件里被解释为汉字,或者其他什么。再次读入的时候就会出问题。。。。。。由于对文件操作这部分学的不好,这个问题困惑了我一天。然后到网上搜答案,也没有搜到。

和同学讨论这个问题时,他突然说到二进制。我猛然想到,用二进制文件存储unsigned char类型值是可以的!因此,第二天进行了下面的实验。真是感觉书到用时,方恨少!


首先给出c++中输入输出流对象可操作二进制文件的两个函数:

read函数,函数声明:istream &read( char *buffer, streamsize num );     函数read()用于输入流,在将字符放入buffer 之前从流中读取num 个字节。如果碰到EOF,read()中止,丢弃不论多少个字节已经放入。可用于读取结构体数据:

 struct {
      int height;
      int width;
    } rectangle;
    
    input_file.read( (char *)(&rectangle), sizeof(rectangle) );
    if( input_file.bad() ) {
      cerr << "Error reading data" << endl;
      exit( 0 );
    }

write函数,函数原型: ostream &write( const char *buffer, streamsize num );   write()函数用于输出流,从buffer中写num个字节到当前输出流中。


下面的代码是我对unsigned char变量存入二进制文件,并从二进制文件中再读取出的实验代码。


#include<iostream>
#include<fstream>
using namespace std;

int main(void){
	//新建一个文件流对象file,以二进制方式打开文件fir.txt,向这个文件中输入变量
	fstream file("fir.txt", ofstream::app | ios::binary | ofstream::out);
	if (!file){
		cerr << "open fir.txt failure" << endl;
		return 0;
	}
	unsigned char i = 0;
	//将置为1~255的unsigned char值写入到二进制文件中
	while ( i != 255){
		i++;
		file.write((char *)&i , sizeof(i));
	}
	cout << endl;
	file.close();
	file.clear();

	//打开二进制文件,进行读
	file.open("fir.txt", ifstream::in | ios::binary);
	if (!file){
		cerr << "open fir.txt for read failure!" << endl;
		return 0;
	}
	//依次读取二进制文件中的unsigned char值读入到变量i,并输出
	while (file.read((char *)&i, sizeof(i))){
		cout << (int)i << "\t";
		if (i % 10 == 0){
			cout << endl;
		}
	}
	cout << endl;
	file.close();
	file.clear();

	return 0;
}

实验结果:





对结构体的操作:

代码

#include<iostream>
#include<fstream>
using namespace std;
struct Student{
	int age;
	char sex;
	double salary;
	static int count;
	Student(int a = 0, int s = 'm', double m = 0.0) : age(count + a), sex(s), salary(m + count){
		count++;
	}
};
int Student::count = 0;

int main(void){
	//新建一个文件流对象file,以二进制方式打开文件sec.txt,向这个文件中输入变量
	fstream file("sec.txt", ofstream::app | ios::binary | ofstream::out);
	if (!file){
		cerr << "open sec.txt failure" << endl;
		return 0;
	}
	Student s[10];
	for(int i = 0 ; i != 10; i++){
		cout << "s[" << i << "]'s information :" << endl << s[i].age << "\t" << s[i].sex << "\t" << s[i].salary << endl;
	}
	//将数组s中元素写入到二进制文件中
	for ( int i = 0; i != 10; i++){
		file.write((char *)&s[i] , sizeof(s[i]));
	}
	cout << endl;
	file.close();
	file.clear();

	//打开二进制文件,进行读
	file.open("sec.txt", ifstream::in | ios::binary);
	if (!file){
		cerr << "open sec.txt for read failure!" << endl;
		return 0;
	}
	//依次读取二进制文件中的unsigned char值读入到变量i,并输出
	Student ss;
	int i = 0;
	cout << "*******************************" << endl;
	cout << "the result reading from sec.txt" << endl;
	cout << "*******************************" << endl;
	while (file.read((char *)&ss, sizeof(ss))){
		cout << "ss[" << i << "]'s information" << endl << ss.age << "\t" << ss.sex << "\t" << ss.salary << endl;
		i++;
	}
	cout << endl;
	file.close();
	file.clear();

	return 0;
}


结果:


<think>嗯,用户的问题是关于C++中读取二进制文件时的字节序问题。他们现在用的是`unsigned short *in_8bit = new unsigned short[fileSize];`然后通过Qt的Qfile读取,但发现是按小端读取的,想要改成大端。首先,我需要理解他们的代码逻辑。 首先,用户声明了一个unsigned short的指针,分配了fileSize大小的空间。然后用Qfile.read将文件内容读入这个缓冲区,这里用的是reinterpret_cast<char*>转换,这是标准的读取二进制数据的方式。但问题在于读取后的数据是按小端解释的,而用户需要大端。 字节序的问题确实常见于处理二进制文件,尤其是跨平台时。小端是低位字节在前,高位在后,而大端相反。比如0x1234在小端存储为0x34 0x12,大端则是0x12 0x34。所以用户的数据现在可能被错误解析,需要转换。 接下来,我需要确定用户的文件是如何存储的。如果文件本身是大端存储的,而程序在小端机器(如x86)上运行,直接读取会导致错误。这时候就需要进行字节交换。 Qt提供了QDataStream,可以设置字节序。用户目前使用的是QFile的read方法,可能需要改用QDataStream来读取,并设置字节序为大端。例如,使用QDataStream::setByteOrder(QDataStream::BigEndian)。但需要注意,QDataStream的默认是BigEndian吗?或者用户之前可能没有设置,导致默认是小端?或者可能文件本身的存储方式与程序读取方式不一致。 另外,用户分配的是unsigned short数组,每个元素是16位。当从文件中读取时,每个16位的值都需要按大端处理。如果数据本身存储为大端,而系统是小端,那么需要逐个元素进行字节交换。 可能的解决方案有两种:一是在读取时使用QDataStream并设置字节序,让Qt自动处理转换;二是在读取数据后,手动交换每个unsigned short的字节顺序。 第一种方法更高效,但需要检查QDataStream的行为。例如,QDataStream默认使用大端吗?文档指出,默认的字节序是BigEndian,但可能在构造时根据平台不同设置?需要确认。例如,用户可能在x86平台上,所以即使QDataStream默认是大端,读取时会自动转换?或者需要显式设置? 如果用户继续使用QFile的read方法,那么读取的数据是按原样存入内存的,不会进行转换。因此,如果文件是大端存储,而系统是小端,直接读取后,每个unsigned short的值会是错误的,需要手动交换字节。 所以可能的步骤是: 1. 确认文件的字节序是大端。 2. 读取文件数据到缓冲区(和现在一样)。 3. 遍历缓冲区中的每个unsigned short元素,进行字节交换。 例如,对于每个元素in_8bit[i],交换高8位和低8位。可以用位操作:value = ((value << 8) & 0xff00) | ((value >> 8) & 0x00ff); 或者使用Qt的qFromBigEndian<quint16>函数来处理。 或者,使用QDataStream来读取,并设置字节序,这样在读取时会自动转换。 例如: QFile file("file.dat"); if (!file.open(QIODevice::ReadOnly)) return; QDataStream in(&file); in.setByteOrder(QDataStream::BigEndian); // 设置大端 unsigned short *buffer = new unsigned short[fileSize/sizeof(unsigned short)]; for (int i = 0; i < count; ++i) { in >> buffer[i]; } 这样可能更直接,但需要注意QDataStream的读写方式可能与直接读取二进制不同,比如可能包含头部信息,或者处理数据的方式不同。如果文件是纯二进制数据,没有头部,可能需要确保QDataStream不会添加额外内容。 另外,用户当前使用的是Qfile.read,直接读取整个数据块到内存,效率更高。而QDataStream逐个读取可能会影响性能,尤其是大文件时。因此,更好的方法可能是读取整个数据块到内存,然后逐个交换字节。 所以,用户的代码可以修改为: 读取数据后,遍历每个元素进行交换: for (qint64 i = 0; i < bytesRead / sizeof(unsigned short); ++i) { in_8bit[i] = (in_8bit[i] << 8) | (in_8bit[i] >> 8); } 或者使用Qt的qFromBigEndian: for (qint64 i = 0; i < count; ++i) { in_8bit[i] = qFromBigEndian(in_8bit[i]); } 但需要注意qFromBigEndian的作用。如果数据存储的是大端,而当前系统是小端,那么qFromBigEndian会将数据从大端转换为当前系统的字节序,即转换为小端,但实际上可能需要将数据转换为正确的值。或者,如果用户希望将读取的数据视为大端,无论系统如何,可能需要使用该函数来转换。 或者更准确地说,假设文件中的数据是以大端格式存储的16位无符号整数,而程序运行在小端机器上,那么读取的每个unsigned short的字节顺序是相反的,因此需要交换字节。 例如,文件中的数据是0x12 0x34(两个字节),在小端系统中,会被存储为0x34 0x12,对应的unsigned short值是0x3412。而正确的大端值应该是0x1234。所以需要交换这两个字节的位置。 因此,在读取整个数据到in_8bit数组后,循环遍历每个元素,交换高8位和低8位。 另外,需要注意的是,fileSize应该是文件的总字节数,而in_8bit分配的是unsigned short数组,所以元素数量应该是fileSize / sizeof(unsigned short)。但用户代码中的new unsigned short[fileSize]可能有问题,因为fileSize是字节数,而每个unsigned short占2字节,所以正确的分配数目应该是fileSize / sizeof(unsigned short)。例如,如果fileSize是1024字节,则分配512个unsigned short。如果用户直接使用fileSize作为元素数目,会导致分配的内存是实际需要的两倍,这会导致内存溢出。这可能是一个错误,需要指出。 例如,假设fileSize是字节数,正确的分配应该是: unsigned short *in_8bit = new unsigned short[fileSize / sizeof(unsigned short)]; 但用户代码中是new unsigned short[fileSize],这会导致分配fileSize * sizeof(unsigned short)字节的空间,而实际读取的字节数是fileSize,这样可能会导致缓冲区溢出,这很危险。所以这可能是一个错误,需要提醒用户。 例如,假设fileSize是1024字节,用户分配了1024个unsigned short,每个占2字节,总共有2048字节的空间。但读取时只读取了1024字节,所以后半部分未被写入,可能没问题,但分配的空间过大,浪费内存,也可能导致问题。 所以用户代码中的这一行应该修改,正确的数目是fileSize / sizeof(unsigned short)。这可能是一个需要指出的问题。 综上,用户的代码存在潜在的内存分配错误,且需要处理字节序问题。 总结解决方案: 1. 修正内存分配的大小:new unsigned short[fileSize / sizeof(unsigned short)]。 2. 读取数据后,遍历每个unsigned short元素,交换其字节顺序。 例如: qint64 bytesRead = Qfile.read(reinterpret_cast<char*>(in_8bit), fileSize); if (bytesRead % sizeof(unsigned short) != 0) { // 处理错误,文件大小不是2字节的整数倍 } for (qint64 i = 0; i < bytesRead / sizeof(unsigned short); ++i) { in_8bit[i] = (in_8bit[i] >> 8) | (in_8bit[i] << 8); // 交换高低字节 } 或者使用Qt提供的函数: #include <QtEndian> for (qint64 i = 0; i < count; ++i) { in_8bit[i] = qFromBigEndian(in_8bit[i]); } 或者更准确地说,如果数据存储为大端,而当前系统是小端,则qFromBigEndian会将大端转换为小端的值。如果当前系统是大端,则该函数不会有动作。因此,使用这个函数可以确保无论系统字节序如何,都能正确转换。 因此,更健壮的方法是使用Qt的字节序转换函数。 此外,用户需要注意文件的大小是否为偶数,因为每个unsigned short占2字节,否则最后会有一个字节剩余,无法构成完整的unsigned short。 总结步骤: - 分配正确数量的unsigned short。 - 读取文件内容到缓冲区。 - 检查读取的字节数是否为偶数。 - 遍历每个元素,转换字节序。 另外,用户可能误用了变量名,in_8bit可能应该是16位的数据类型,因为unsigned short通常是16位。变量名可能让人误解,但可能不影响代码逻辑。 最后,用户需要确保文件的字节顺序确实是大端,否则转换后会得到错误的结果。因此,确认文件的实际存储格式是必要的。</think>在C++中处理二进制文件字节序时,需要注意内存分配和字节交换两个关键点。您的代码需要以下改进: ### 1. 内存分配修正 当前代码存在内存分配错误: ```cpp // 错误写法:fileSize单位是字节,每个unsigned short占2字节 unsigned short *in_8bit = new unsigned short[fileSize]; // 正确写法:计算实际需要的元素数量 int elementCount = fileSize / sizeof(unsigned short); unsigned short *in_8bit = new unsigned short[elementCount]; ``` ### 2. 字节序转换方案 #### 方案一:手动字节交换(推荐) ```cpp // 读取原始数据 qint64 bytesRead = file.read(reinterpret_cast<char*>(in_8bit), fileSize); // 校验数据完整性 if (bytesRead % sizeof(unsigned short) != 0) { qWarning() << "文件包含不完整的16位数据"; delete[] in_8bit; return; } // 进行字节序转换 const qint64 elementCount = bytesRead / sizeof(unsigned short); for (qint64 i = 0; i < elementCount; ++i) { // 使用位操作交换高低字节 in_8bit[i] = (in_8bit[i] << 8) | (in_8bit[i] >> 8); // 或使用Qt内置函数(需要包含<QtEndian>) // in_8bit[i] = qFromBigEndian(in_8bit[i]); } ``` #### 方案二:使用QDataStream自动转换 ```cpp QFile file("data.dat"); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "文件打开失败"; return; } QDataStream stream(&file); stream.setByteOrder(QDataStream::BigEndian); // 设置大端模式 // 计算需要读取的元素数量 const qint64 elementCount = file.size() / sizeof(unsigned short); unsigned short *in_8bit = new unsigned short[elementCount]; // 逐元素读取(自动转换字节序) for (qint64 i = 0; i < elementCount; ++i) { stream >> in_8bit[i]; } ``` ### 3. 重要注意事项 1. **文件验证**:确保文件实际使用大端存储 2. **性能考量**:对于大文件(>100MB),建议采用内存映射文件方式处理 3. **跨平台处理**:使用Qt内置类型保证一致性: ```cpp quint16 *data = reinterpret_cast<quint16*>(buffer); ``` 4. **错误处理**:增加校验逻辑 ```cpp if (bytesRead != fileSize) { qWarning() << "文件读取不完整,已读取" << bytesRead << "/" << fileSize; } ``` ### 4. 完整示例代码 ```cpp QFile file("data.dat"); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "文件打开失败"; return; } const qint64 fileSize = file.size(); if (fileSize % sizeof(quint16) != 0) { qWarning() << "文件包含不完整的16位数据"; return; } const qint64 elementCount = fileSize / sizeof(quint16); quint16 *buffer = new quint16[elementCount]; if (file.read(reinterpret_cast<char*>(buffer), fileSize) == fileSize) { // 字节序转换 for (qint64 i = 0; i < elementCount; ++i) { buffer[i] = qFromBigEndian(buffer[i]); } // 现在buffer中的数值已正确转换为当前系统的字节序 // 后续处理代码... } delete[] buffer; ``` ### 5. 字节序验证方法 调试时可添加验证代码: ```cpp // 测试数据:已知大端存储的0x1234 qDebug() << Qt::hex << buffer[0]; // 正确应输出0x1234 ``` 建议使用十六进制查看工具(如HxD)直接验证文件原始字节排列,确保对大端模式的理解正确。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值