因学习需要,现要将二进制bin文件转化成十六进制格式并保存于txt文件中。
使用C语言编写该程序较为简单,定义FILE指针, 在合适的地方运用getc(),fprintf()函数即可。这里主要讲使用C++文件流方法。
一直以来对C++文件和流都不是太熟,遇到文件读写使用的都是C的方式。因此这次使用C++的方式编写该程序,记录编写过程中的一些细节和问题,防止今后再出错。
一、 什么是bin文件?
文件的后缀其实意义不大,它存在的意义是告诉用户或者程序该文件是什么格式,但文件内部是否按该格式存储并不一定。举个例子,有个.mp3音频文件,该后缀是为了告诉你,这是一个音频文件,你可以用通常的音乐软件打开它。我完全可以把该后缀更改为.docx文件,那这个文件变成文档了吗?并没有,只是电脑识别出,这个文件的后缀是.docx,你可以尝试使用word打开它,但是否能正确打开并读取文件中的信息,不能保证。事实上,这个.docx文件,你在点击右键,打开方式中仍然选择音乐播放软件,依旧可以播放音乐。当然,有些软件在打开文件之前就会检查后缀,不符合的不再继续进行的除外。我甚至可以将这个文件的后缀更改为.SeaOtter,并设置默认打开方式为网易云音乐,依旧能正确打开。所以,需要区分文件以什么格式存储,这是同后缀无关的。
那么什么是bin文件呢?通过上述分析,这里的bin文件我指的就不仅仅是.bin后缀的文件了,而是以二进制格式存储的文件。
二、如何打开bin文件?
打开bin文件的方式有很多,可以下载binary view软件,或者在notepad++中下载hex-editor插件。以后者为例,打开一个二进制文件后的截图如下,这里转换成了十六进制:
![](https://i-blog.csdnimg.cn/blog_migrate/919bf6f0ed37c8c0791ed9c18ed3fca1.png)
由于需要的也是将二进制文件转换成十六进制存储于.txt中,因此通过notepad++的hex-editor插件可以直接预览到正确转换结果。
三、bin2txt.cpp
写该程序需要学习文件流是如何读写二进制文件。通过网上查阅资料可以了解到,C++文件流可以读写文本文件和二进制文件。
对于读写文本文件,通常使用析取器(>>)和插入器(<<),同cin与cout是同一个含义,只不过cin就是ifstream,cout就是ofstream,这也是为什么叫做文件流的原因。
对于读写二进制文件,网上通常说选择析取器(>>)和插入器(<<)不会有语法错误,但是没有意义,通常使用get()和put()来进行字符的读写。但是实际操作中发现,读取二进制文件还是应该选择getc(),使用析取器(>>)会出现问题(下面会详述)。
代码如下:
#include <iostream>
#include <fstream>
#include <iomanip>
using namespace std;
int main()
{
ifstream infile;
infile.open("0927.bin", ios_base::binary); //attention1
ofstream outfile;
outfile.open("output.txt", ios_base::binary);
char readByte;
unsigned char readByte2;
int cnt = 0;
outfile << "unsigned char hex_num[ ] = " << endl;
outfile << "{ " << endl;
while(!infile.eof())
{
if(cnt % 16 == 0)
{
outfile << " " << endl;
outfile << " ";
}
infile.get(readByte); //attention2
readByte2 = (unsigned char)readByte; //attention3
cnt++;
outfile << "0x" << setfill('0') << setw(2); //attention4
outfile << hex << (unsigned int)readByte2; //attention5
outfile << ",";
}
outfile << " \n";
outfile << "}; \n";
infile.close();
outfile.close();
return 0;
}
来看一下五个attention:
attention1:ios::binary,表明为二进制文件读写,使用该语句可以避免遗漏特殊字符,比如0x0d,0x0a等。但是实测该语句并没有这样的功效……还望解答;
attention2:infile.get(readbyte),get()函数要求参数为char类型,因此readByte定义为char类型。这里注意不能使用infile>>readByte,否则会丢失0x0d等特殊字符;
attention3:强制转换。由于get()函数的参数要求为char类型,但是二进制文件每8位一组,很可能大于127,因此需要再强制转换位unsigned char类型;
attention4:格式设定。不同于C语言中可以通过fprintf(txtFile,"0x%02x,",readByte)直接输出两位的十六进制数,C++文件流要复杂一些,但是相对清晰很多。setfill('0')用于高位填0,setw(2)用于指定输出两位,这两个函数需要包含头文件iomanip。
attention5:进制设定。用于输出十六进制数,这里要注意,hex后要跟着整型数才可正确输出,如果不进行unsigned int强制转换,会输出乱码。
程序很简单,但是写该程序时因为丢失0x0d,0x0a等特殊字符而卡了很久,之前在网上看二进制文件选择析取器无伤大雅,就没有使用get()函数,结果总是会丢失特殊字符,即便加了ios::binary也无法解决,最后选择了get()才完整读取了下来。对于输出部分,仍使用的是插入器(>>),因为输出到了文本文件中。
通过这次的小尝试,了解了C++的文件流,比起C语言的文件读写,“流”的概念使得文件读写过程更为清晰,同cin,cout能够关联上。但是也有较为复杂的地方,比如格式与进制的设定过程。最重要的是,要弄清楚读取与写入的文件类型,至少在我的实验当中,二进制文件选择get(),put()组合(如果输出也输出到二进制文件的话)不容易错。
对于我出现的问题,如果有朋友能帮忙解决infile >> readByte与ios::binary组合为什么依旧会丢失特殊字符的问题,麻烦评论区留言,感谢!