Linux python 读取二进制文件,将其转化成16进制并以文本形式存储 每行8个字符
时间: 2025-07-23 17:05:12 浏览: 2
<think>我们计划将二进制文件读取后,将每个字节转换为两个字符的十六进制字符串(例如,字节0xAB转换为字符串"AB"),然后每8个这样的十六进制字符串组成一行(即每行8个字节,16个字符,但我们要求每行8个字符?注意:每个字节用两个十六进制字符表示,所以8个字节就是16个字符。但用户要求每行8个字符,也就是4个字节?)
用户要求:每行8个字符(注意:每个十六进制字符表示4位,所以两个字符表示一个字节)。因此,8个字符对应4个字节(因为8个字符/2=4个字节)。但用户说“8个字符”,所以每行就是4个字节的十六进制表示(因为每个字节用两个十六进制字符,所以4个字节就是8个字符)。
然而,用户的问题描述是:“以每行8个字符的形式保存为文本文件”。所以我们需要每行输出8个十六进制字符(即4个字节的十六进制表示,因为每个字节用两个字符表示)。
但是,请注意:用户可能是指每行有8个十六进制数字(即8个字符),那么每行就是4个字节。
然而,我们再看一下用户的原始问题:“每行8个字符”,所以就是每行8个字符,不管这些字符代表什么。
所以,我们可以这样理解:将二进制文件中的每个字节转换成两个十六进制字符,然后将这些字符按顺序拼接,每8个字符为一行。
例如,假设二进制内容为:0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x01, 0x23, ...
转换后:
第一行:12345678 (前4个字节,共8个字符)
第二行:9ABCDEF0 (接下来4个字节)
第三行:0123 (最后两个字节,不足8个字符,则只输出这些字符)
但是,用户要求每行8个字符,所以如果最后一组不足8个字符,则直接输出剩余字符(不补空格或0)。
因此,步骤:
1. 读取二进制文件,得到字节序列。
2. 将每个字节转换为两个十六进制字符(大写或小写?通常使用大写,但用户没有指定,我们可以用大写,或者让用户选择。这里先用大写)。
3. 将所有转换后的字符串连接起来。
4. 然后按每8个字符分割成一行。
但是,注意:如果文件很大,一次性读取并转换可能占用大量内存。我们可以分块读取并处理。
另一种方式:我们每次读取4个字节(因为4个字节对应8个字符),然后转换为8个字符的字符串,写入一行。然后继续读4个字节,直到文件结束。
但是,如果文件总字节数不是4的倍数,那么最后一次读取可能不足4个字节,我们也要将其转换为十六进制字符串(注意:每个字节都要转换为两个字符),然后写入一行(即使不足8个字符)。
所以,我们可以这样:
步骤:
1. 打开二进制文件和输出文本文件。
2. 每次读取4个字节(因为4个字节正好8个字符)。
3. 将读取的4个字节转换为十六进制字符串(每个字节两个字符,共8个字符)。
4. 写入一行。
5. 如果读取的字节数不足4个,那么将实际读取的字节转换为十六进制字符串(注意:如果读取了n个字节,则得到2*n个字符),然后写入一行(即使不足8个字符)。
6. 重复直到文件结束。
但是,用户要求每行8个字符,所以每次处理4个字节(即8个字符)是合适的。
但是,如果我们读取4个字节,然后转换为8个字符的字符串,那么每次写入一行就是8个字符。
注意:十六进制字符串可以用`binascii.hexlify`或者每个字节用`format(byte, '02X')`。
我们选择使用`binascii.hexlify`来转换整个块,因为它更高效。
但是,`binascii.hexlify`返回的是字节串(bytes),我们需要解码为字符串(默认是ASCII,因为十六进制字符都是ASCII字符)。
例如:`binascii.hexlify(b'\x12\x34').decode('ascii')` 得到字符串'1234'。
我们也可以使用`bytes.hex()`方法(Python 3.5+),但注意:`bytes.hex()`返回的是字符串,但是它是整个字节串的十六进制表示。例如:`b'\x12\x34'.hex().upper()`可以得到'1234'(大写)。
由于用户没有指定大小写,我们可以使用大写,这样更常见。
方案:
方法1:使用`binascii.hexlify`
方法2:使用`bytes.hex`然后转换为大写(因为hex返回的是小写,我们可以用upper转成大写)
这里我们使用方法2,因为更简洁。
代码步骤:
1. 打开二进制文件(rb)和文本文件(w)。
2. 设置每次读取的字节数为4(因为4字节对应8字符)。
3. 循环读取:
chunk = bin_file.read(4)
if not chunk: break
hex_str = chunk.hex().upper() # 得到小写字符串,然后转为大写
# 注意:如果chunk的长度不足4,那么hex_str的长度就是chunk_length*2,可能是2,4,6,8
# 然后写入文件,写入hex_str(即使不足8个字符,也要写入,并且换行)
4. 关闭文件。
但是,这样每行最多8个字符,最少可能是2,4,6,8个字符(因为每次读4个字节,但最后可能不足)。
但是,用户要求每行8个字符,但最后一行如果不足8个字符也是允许的(因为文件末尾)。
所以,这个方案满足要求。
示例:
假设文件内容:b'\x01\x02\x03\x04\x05\x06'
第一次读取4字节:b'\x01\x02\x03\x04' -> 转换为'01020304'(大写) -> 写入一行。
第二次读取2字节:b'\x05\x06' -> 转换为'0506' -> 写入一行(注意:这一行只有4个字符)。
但是,用户要求每行8个字符,而我们这里第一行是8个字符,第二行是4个字符。这符合要求吗?
用户要求是“每行8个字符”,但并没有说如果最后一行不足8个字符怎么办。通常,我们允许最后一行不足。
所以,这个方案是可行的。
但是,如果我们希望每行都是8个字符(即4个字节),那么最后不足4个字节的部分,我们是否应该补0?但是用户没有要求补0,所以我们就原样输出。
另外,我们也可以考虑另一种方式:不按4字节读取,而是按更大的块读取,然后按8个字符分割。但是,这样处理最后一块时,如果总字符数不是8的倍数,最后一行也会不足8个字符。所以效果一样。
我们选择按4字节读取,这样简单直接。
代码实现:
注意:如果文件很大,我们每次读取4字节会频繁IO,效率较低。我们可以增加缓冲区大小,比如一次读取4096字节(4KB的倍数),然后处理这个块,再写入文件。
但是,为了简单,我们先实现按4字节读取。如果用户需要高性能,我们可以再优化。
我们先写一个简单版本:
```python
def bin_to_hex_text(input_file, output_file):
with open(input_file, 'rb') as bin_f, open(output_file, 'w') as txt_f:
while True:
chunk = bin_f.read(4)
if not chunk:
break
# 将字节块转换为十六进制字符串(大写)
hex_str = chunk.hex().upper()
txt_f.write(hex_str + '\n')
```
但是,这样最后一行也会有一个换行。这没有问题,因为文本文件最后一行换行是可选的。
但是,如果文件为空,则不会写入任何内容。
测试:
创建一个包含6个字节的二进制文件(例如:0x00,0x01,0x02,0x03,0x04,0x05),运行程序后,输出文本文件内容:
第一行:00010203
第二行:0405
注意:第二行是0405,因为最后两个字节是0x04和0x05,转换为十六进制字符串就是'0405'(4个字符)。
但是,用户要求每行8个字符,这里第一行是8个字符,第二行是4个字符,所以符合要求(因为最后一行不足8个字符)。
但是,如果我们希望每行都是8个字符(即4个字节),那么当文件大小不是4的倍数时,最后一行就会不足。如果用户希望每行都是8个字符(即4个字节),那么应该要求文件大小是4的倍数?但用户没有这个要求,所以我们按实际读取的字节数转换。
另外,用户可能希望每行8个字符,但也可以每行16个字符(即8个字节)?但是用户明确要求每行8个字符。
所以,我们按照上述代码实现。
但是,性能优化:如果文件很大,频繁读写会影响性能。我们可以一次读取较大的块(比如4096字节),然后按每4个字节一组进行分割,分别转换为十六进制字符串并写入文件(每8个字符一行)。
优化版本:
```python
def bin_to_hex_text_large(input_file, output_file, buffer_size=4096):
# 注意:buffer_size应该是4的倍数,这样处理起来方便
with open(input_file, 'rb') as bin_f, open(output_file, 'w') as txt_f:
while True:
# 读取一个块
chunk = bin_f.read(buffer_size)
if not chunk:
break
# 处理这个块:将块按4字节分组(最后一组可能不足4字节)
# 计算块中的字节数
n = len(chunk)
# 将整个块转换为十六进制字符串(大写)
# 注意:我们不需要整个块转换成一个字符串再分割,而是可以每4个字节处理一次
# 但是,我们可以用列表推导式,但这样会生成一个字符串列表
# 或者,我们可以遍历块,每4个字节取一次
# 这里我们使用:将块切割成多个4字节的小块(最后一个可能不足)
# 方法:range(0, n, 4)
lines = []
for i in range(0, n, 4):
sub_chunk = chunk[i:i+4]
hex_str = sub_chunk.hex().upper()
lines.append(hex_str)
# 将所有的行写入文件
txt_f.write('\n'.join(lines) + '\n')
# 注意:最后一块的最后一个分组可能不足4字节,但上面已经处理了
```
但是,这样写的话,每块的最后一行和下一块的第一行之间会有一个换行连接,这是没问题的,因为每个小块处理完后我们写了一个换行(在最后加了一个'\n'),这样每个小块的最后一行的后面会多一个换行,导致两个块之间多一个空行?因为每个小块处理完后,我们写入的是每个小块内的所有行(用换行连接),然后最后再加一个换行,所以每个小块会以换行结束,这样下一个块的第一行就会在下一行开始,不会产生空行(因为最后一行已经换行,下一个块的第一行紧接着写在下一行)。但是,这样写会导致最后文件末尾多一个换行(因为最后一个小块处理完后也加了一个换行)。不过文本文件末尾换行是允许的。
但是,我们也可以不在最后加换行,而是在每个字符串后面加换行,这样在循环内每行就写入一次。这样避免了大字符串的构建,节省内存。
改进(更节省内存的方式):
```python
def bin_to_hex_text_large(input_file, output_file, buffer_size=4096):
# 设置buffer_size为4的倍数
if buffer_size % 4 != 0:
# 调整为4的倍数
buffer_size = (buffer_size // 4) * 4
if buffer_size == 0:
buffer_size = 4
with open(input_file, 'rb') as bin_f, open(output_file, 'w') as txt_f:
first_line = True
while True:
chunk = bin_f.read(buffer_size)
if not chunk:
break
n = len(chunk)
# 处理当前块
for i in range(0, n, 4):
sub_chunk = chunk[i:i+4]
hex_str = sub_chunk.hex().upper()
# 如果是第一行,则不加换行(因为文件开头不需要换行),但后续行需要在上次写入后换行
# 但是,我们可以在每次写入一行后换行,除了第一次写入之前不换行,但是第一次写入时,我们直接写入内容,然后后面每次写入先换行再写内容?这样不行,因为我们是分块处理的。
# 我们可以这样:每次写入一行,然后换行,包括第一行。这样第一行写入后换行,第二行写入在下一行。最后文件末尾会多一个换行,但这是文本文件标准格式。
txt_f.write(hex_str + '\n')
# 这样,每行(包括最后一行)后面都有一个换行符
```
这个版本每次处理一个4字节的分组,然后立即写入一行(带换行符)。这样内存占用很小,因为不会构建整个块的字符串列表。
但是,频繁写入可能会影响性能?我们可以将每个块的分组字符串先收集起来,然后一次性写入(如之前用`'\n'.join(lines)`),这样减少IO次数。但是,对于大文件,我们读取的块大小是固定的,所以每个块的组数也是固定的(buffer_size//4),所以构建一个字符串列表然后一次写入,也是可以接受的。
这里我们选择立即写入每一行,因为简单,而且buffer_size设置得较大时(比如1MB),每个块有262144行,这样写入次数为262144次,可能影响性能。所以我们可以将每个块的分组字符串收集到一个列表中,然后用`'\n'.join(lines)`写入,最后再写入一个换行(这样每个块只需要一次写入操作?但是`'\n'.join(lines)`会生成一个大字符串,然后一次写入)。
我们修改为:
```python
def bin_to_hex_text_large(input_file, output_file, buffer_size=4096):
buffer_size = (buffer_size // 4) * 4
if buffer_size == 0:
buffer_size = 4
with open(input_file, 'rb') as bin_f, open(output_file, 'w') as txt_f:
while True:
chunk = bin_f.read(buffer_size)
if not chunk:
break
n = len(chunk)
lines = []
for i in range(0, n, 4):
sub_chunk = chunk[i:i+4]
hex_str = sub_chunk.hex().upper()
lines.append(hex_str)
# 将当前块的所有行写入(每行一个字符串,用换行连接)
txt_f.write('\n'.join(lines) + '\n')
```
这样,每个块只写入两次(一次是所有的行,一次是最后的换行?不对,`'\n'.join(lines)`已经用换行连接,然后再加上一个换行,所以最后会多一个空行?实际上,每个块的最后一行后面会有一个换行,然后下一个块的第一行紧接着写在下一行(没有空行)。但是,这样会导致块之间没有空行,但是每个块内的行是连续的。而且,最后文件末尾会有一个换行(因为最后一块写入后加了一个换行)。这样是标准的文本文件。
但是,如果文件为空,则不会写入任何内容。
我们选择这个版本,因为IO次数少(每个块一次写入)。
但是,如果块很大,比如buffer_size=1MB,那么会生成一个包含262144个字符串的列表,然后合并成一个字符串,可能会占用较多内存。所以,如果内存有限,我们可以用生成器表达式,但是`'\n'.join`需要列表,所以无法避免。
因此,我们可以调整buffer_size,比如4KB(4096字节),这样每个块有1024行,不会占用太多内存。
现在,我们提供两个版本:一个简单版本(每次读4字节),一个分块版本(每次读多个4字节)。我们推荐分块版本。
最终代码:
我们让用户可以选择buffer_size,但默认4096。
另外,如果用户希望每行的字符数不是8(即每行不是4个字节),我们可以修改?但用户要求是8个字符,所以我们固定每行4个字节(8个字符)。
但是,为了灵活性,我们可以让用户指定每行多少个字符(即每行几个字节,因为每个字节两个字符,所以每行字符数=2*字节数)。但是用户要求是8个字符,所以我们这里固定为8个字符(即4个字节)。所以不需要参数化。
所以,我们提供以下函数:
```python
def bin_to_hex_text(input_bin, output_txt, buffer_size=4096):
"""
将二进制文件转换为十六进制文本文件,每行8个字符(即4个字节的十六进制表示)
:param input_bin: 输入二进制文件路径
:param output_txt: 输出文本文件路径
:param buffer_size: 每次读取的字节数(将调整为4的倍数)
"""
# 调整buffer_size为4的倍数
buffer_size = (buffer_size // 4) * 4
if buffer_size <= 0:
buffer_size = 4
with open(input_bin, 'rb') as bin_f, open(output_txt, 'w') as txt_f:
while True:
chunk = bin_f.read(buffer_size)
if not chunk:
break
# 处理当前块
lines = []
# 将块分割为4字节的小块
for i in range(0, len(chunk), 4):
# 提取4字节(可能不足)
sub_chunk = chunk[i:i+4]
# 转换为十六进制字符串(大写)
hex_str = sub_chunk.hex().upper()
lines.append(hex_str)
# 将当前块的所有行写入文件,用换行符连接,并在最后加一个换行符(保证每个块结束换行)
txt_f.write('\n'.join(lines))
txt_f.write('\n') # 每个块结束后换行,这样下一个块从新行开始
```
但是,这样写会导致每个块之间有一个空行吗?不会,因为第一个块写入后,我们写了一个换行,然后下一个块的第一行会紧接着这个换行之后(即下一行)。所以不会产生空行。
但是,最后一块也会在最后加一个换行,这样文件末尾会有一个换行符,这是正常的。
测试:用6个字节的文件(0,1,2,3,4,5)测试,设置buffer_size=4,那么:
第一次读取4字节:[0,1,2,3] -> 转换为'00010203',然后写入文件(第一行:00010203,然后换行)
第二次读取2字节:[4,5] -> 转换为'0405',然后写入第二行:0405,然后换行(即第二行后面还有一个换行,所以文件有两行,第一行00010203,第二行0405,然后第三行为空?不对,我们写入的是:第一块写入:'00010203\n',第二块写入:'0405\n',所以文件内容为:
00010203\n
0405\n
所以最后一行是0405,后面有一个换行。文本文件最后一行有换行是标准的。
所以,这个函数满足要求。
我们也可以考虑不按块换行,而是每行都独立,那么就不需要在最后额外加一个换行,而是每行后面都加换行。但是,我们在循环中每处理一个4字节组就写入一行(带换行)会更简单,但是那样IO次数多。所以我们选择现在的方式。
另外,如果用户希望不要最后一行换行,我们可以调整,但通常文本文件最后一行换行是标准做法。
所以,我们按照这个实现。
最后,注意:我们使用`chunk.hex().upper()`,这要求Python3.5+(因为`bytes.hex()`是Python3.5引入的)。如果用户使用更早的版本,我们可以用`binascii.hexlify`。
兼容性考虑:
如果需要在Python3.5以下版本运行,我们可以使用:
```python
import binascii
hex_str = binascii.hexlify(sub_chunk).decode('ascii').upper()
```
所以,我们增加兼容性:
```python
import binascii
def bin_to_hex_text(input_bin, output_txt, buffer_size=4096):
buffer_size = (buffer_size // 4) * 4
if buffer_size <= 0:
buffer_size = 4
with open(input_bin, 'rb') as bin_f, open(output_txt, 'w') as txt_f:
while True:
chunk = bin_f.read(buffer_size)
if not chunk:
break
lines = []
for i in range(0, len(chunk), 4):
sub_chunk = chunk[i:i+4]
# 尝试使用bytes.hex()(Python3.5+),如果不支持则用binascii
try:
hex_str = sub_chunk.hex()
except AttributeError:
hex_str = binascii.hexlify(sub_chunk).decode('ascii')
hex_str = hex_str.upper()
lines.append(hex_str)
txt_f.write('\n'.join(lines) + '\n')
```
但是,我们也可以直接使用`binascii`,因为它适用于所有Python3版本。
为了简单,我们假设用户使用Python3.5+,因为Python2已经不再支持。
所以,我们使用`bytes.hex()`。
最终代码(假设Python3.5+):
```python
def bin_to_hex_text(input_bin, output_txt, buffer_size=4096):
"""
将二进制文件转换为十六进制文本文件,每行8个字符(即4个字节的十六进制表示)
:param input_bin: 输入二进制文件路径
:param output_txt: 输出文本文件路径
:param buffer_size: 每次读取的字节数(将调整为4的倍数)
"""
# 调整buffer_size为4的倍数
buffer_size = (buffer_size // 4) * 4
if buffer_size <= 0:
buffer_size = 4
with open(input_bin, 'rb') as bin_f, open(output_txt, 'w') as txt_f:
while True:
chunk = bin_f.read(buffer_size)
if not chunk:
break
lines = []
for i in range(0, len(chunk), 4):
sub_chunk = chunk[i:i+4]
# 转换为十六进制字符串,并转为大写
hex_str = sub_chunk.hex().upper()
lines.append(hex_str)
# 写入当前块的所有行,并在块结束后换行
txt_f.write('\n'.join(lines))
txt_f.write('\n')
```
调用示例:
```python
bin_to_hex_text('input.bin', 'output.txt')
```
或者,为了大文件优化,可以增加buffer_size:
```python
bin_to_hex_text('largefile.bin', 'largehex.txt', buffer_size=1024*1024) # 1MB
```
这样,我们就完成了。
注意:每行8个字符,即4个字节的十六进制表示。
如果用户希望每行是其他数量的字符,比如16个字符(8个字节),那么可以修改代码,将循环步长和每次读取的字节数调整。但用户要求是8个字符,所以我们不提供这个选项。
最后,生成相关问题:
1. 如何将十六进制文本文件转换回二进制文件?
2. 如何以不同的格式(如每行16个字符)转换二进制文件为十六进制文本?
3. 如何处理大端序和小端序的二进制文件转换?
4. 如何将二进制文件转换为十六进制字符串并添加地址偏移量(如0100: 12 34 56 78)?</think>### Python在Linux下读取二进制文件并转换为每行8字符的十六进制文本文件
以下是实现该功能的完整Python代码,包含详细注释和优化处理:
```python
import os
def bin_to_hex_text(input_path, output_path, buffer_size=4096):
"""
将二进制文件转换为十六进制文本文件,每行8个字符(4字节)
参数:
input_path: 输入二进制文件路径
output_path: 输出文本文件路径
buffer_size: 读取缓冲区大小(字节),默认4KB
"""
# 确保缓冲区大小是4的倍数(每行4字节)
buffer_size = max(4, (buffer_size // 4) * 4)
try:
with open(input_path, 'rb') as bin_file, open(output_path, 'w') as txt_file:
while True:
# 读取数据块
chunk = bin_file.read(buffer_size)
if not chunk:
break
# 处理当前数据块
hex_lines = []
for i in range(0, len(chunk), 4):
# 提取4字节(可能不足)
sub_chunk = chunk[i:i+4]
# 转换为十六进制字符串并大写
hex_str = sub_chunk.hex().upper()
hex_lines.append(hex_str)
# 写入文件(每行8字符)
txt_file.write('\n'.join(hex_lines) + '\n')
print(f"转换成功: 已生成 {output_path}")
print(f"文件大小: {os.path.getsize(input_path)} 字节 → "
f"{os.path.getsize(output_path)} 字节")
except FileNotFoundError:
print(f"错误: 文件不存在 - {input_path}")
except Exception as e:
print(f"转换出错: {str(e)}")
# 示例用法
if __name__ == "__main__":
input_file = "/path/to/input.bin" # 替换为你的二进制文件路径
output_file = "/path/to/output.txt" # 替换为输出文件路径
# 执行转换(可调整buffer_size优化大文件处理)
bin_to_hex_text(input_file, output_file, buffer_size=65536)
```
### 关键功能说明
1. **每行8字符格式**:
- 每4字节(8字符)为一行:`[字节1][字节2][字节3][字节4]`
- 示例:二进制字节 `b'\x12\x34\x56\x78'` 转换为 `12345678`
2. **文件处理优化**:
- 缓冲区机制:避免一次性加载大文件
- 动态调整:确保缓冲区大小是4的倍数
- 流式处理:适合GB级大文件转换
3. **格式规范**:
- 十六进制大写字母(A-F)
- 无分隔符(纯8字符连续字符串)
- 自动处理文件末尾不足8字符的情况
### 验证转换结果
```bash
# 查看二进制文件前16字节
xxd -l 16 /path/to/input.bin
# 查看文本文件前4行(每行8字符)
head -n 4 /path/to/output.txt
```
### 数学原理
二进制到十六进制的转换基于基数转换公式:
$$ \text{十六进制值} = \sum_{k=0}^{n-1} b_k \cdot 16^k $$
其中$b_k$是十六进制位的值(0-9, A-F),$k$是位位置[^1]。
### 性能优化建议
1. **缓冲区大小**:
- 小文件(<1MB):默认4KB
- 大文件(>100MB):设置`buffer_size=1048576`(1MB)
- 巨型文件(>1GB):设置`buffer_size=16777216`(16MB)
2. **内存监控**:
```bash
# 实时监控内存使用
watch -n 1 "free -h | awk '/Mem:/ {print \$3}'"
```
### 实际应用场景
1. 嵌入式开发:固件二进制转可读格式
2. 数据恢复:分析损坏文件的十六进制结构
3. 加密研究:检查加密文件的熵分布模式
4. 网络协议分析:解析原始网络数据包
阅读全文
相关推荐


















