Python:使用十六进制分隔符拆分字节

Python:使用十六进制分隔符拆分字节,python,split,hex,Python,Split,Hex,我正在处理几个二进制文件,我想解析存在的UTF-8字符串 我当前有一个函数,它获取文件的起始位置,然后返回找到的字符串: def str_extract(file, start, size, delimiter = None, index = None): file.seek(start) if (delimiter != None and index != None): return file.read(size).explode('0x00000000')[inde

我正在处理几个二进制文件,我想解析存在的UTF-8字符串

我当前有一个函数,它获取文件的起始位置,然后返回找到的字符串:

def str_extract(file, start, size, delimiter = None, index = None):
   file.seek(start)
   if (delimiter != None and index != None):
       return file.read(size).explode('0x00000000')[index] #incorrect
   else:
       return file.read(size)
文件中的某些字符串由
0x00
分隔,是否可以像PHP的explode那样拆分这些字符串?我是Python新手,所以欢迎任何关于代码改进的建议

示例文件:

48 00 65 00 6C 00 6C 00 6F 00 20 00 57 00 6F 00 72 00 6C 00 64 00 | 00 00 00 | 31 00 32 00 33 00
这是
Hello World123
,我注意到了
00 00 00 00 00 00 00
分隔符,用
条围起来

因此:

同样地:

str_extract(file, 0x00, 0x20, 0x00000000, 1) => '123'

首先,您需要在中打开文件

然后使用四个零字节的分隔符
b'\0\0\0'
拆分
str
(或
bytes
,具体取决于Python版本):

def str_extract(file, start, size, delimiter = None, index = None):
   file.seek(start)
   if (delimiter is not None and index is not None):
       return file.read(size).split(delimiter)[index]
   else:
       return file.read(size)
此外,您还需要处理编码,因为
str_extract
只返回二进制数据,并且您的测试数据采用UTF-16 little-endian格式,如下所述:


此外:使用测试变量是否不为
None

首先需要在中打开文件

然后使用四个零字节的分隔符
b'\0\0\0'
拆分
str
(或
bytes
,具体取决于Python版本):

def str_extract(file, start, size, delimiter = None, index = None):
   file.seek(start)
   if (delimiter is not None and index is not None):
       return file.read(size).split(delimiter)[index]
   else:
       return file.read(size)
此外,您还需要处理编码,因为
str_extract
只返回二进制数据,并且您的测试数据采用UTF-16 little-endian格式,如下所述:


此外:使用测试一个变量,使其不是
None

我假设您在这里使用的是Python 2,但编写的代码同时适用于Python 2和Python 3

您有UTF-16数据,而不是UTF-8。您可以将其读取为二进制数据,并使用以下命令将其拆分为四个NUL字节:

结果数据编码为UTF-16 little endian(您可能在开始时省略了,也可能没有省略;您可以使用以下代码解码数据:

result.decode('utf-16-le')
但是,这将失败,因为我们只是在最后一个NUL字节处截断文本;Python将在找到的前4个NUL上拆分,并且不会跳过作为文本一部分的最后一个NUL字节

更好的方法是先解码为Unicode,然后在Unicode双NUL码点上拆分:

file.read(size).decode('utf-16-le').split(u'\x00' * 2)[index]
将其作为一项功能组合在一起将是:

def str_extract(file, start, size, delimiter = None, index = None):
   file.seek(start)
   if (delimiter is not None and index is not None):
       delimiter = delimiter.decode('utf-16-le')  # or pass in Unicode
       return file.read(size).decode('utf-16-le').split(delimiter)[index]
   else:
       return file.read(size).decode('utf-16-le')

with open('filename', 'rb') as fobj:
    result = str_extract(fobj, 0, 0x20, b'\x00' * 4, 0)

如果文件在开始时作为BOM,请考虑将文件打开为UTF16,而不是以:

开始。
import io

with io.open('filename', 'r', encoding='utf16') as fobj:
    # ....
并移除显式解码

Python 2演示:

>>> from io import BytesIO
>>> data = b'H\x00e\x00l\x00l\x00o\x00 \x00W\x00o\x00r\x00l\x00d\x00\x00\x00\x00\x001\x002\x003\x00'
>>> fobj = BytesIO(data)
>>> str_extract(fobj, 0, 0x20, '\x00' * 4, 0)
u'Hello World'
>>> str_extract(fobj, 0, 0x20, '\x00' * 4, 1)
u'123'

我假设您在这里使用的是Python2,但是要编写代码来处理Python2和Python3

您有UTF-16数据,而不是UTF-8。您可以将其作为二进制数据读取,并使用以下命令在四个NUL字节上拆分:

结果数据编码为UTF-16 little endian(您可能在开始时省略了,也可能没有省略;您可以使用以下代码解码数据:

result.decode('utf-16-le')
但是,这将失败,因为我们只是在最后一个NUL字节处截断文本;Python将在找到的前4个NUL上拆分,并且不会跳过作为文本一部分的最后一个NUL字节

更好的方法是先解码为Unicode,然后在Unicode双NUL码点上拆分:

file.read(size).decode('utf-16-le').split(u'\x00' * 2)[index]
将其作为一项功能组合在一起将是:

def str_extract(file, start, size, delimiter = None, index = None):
   file.seek(start)
   if (delimiter is not None and index is not None):
       delimiter = delimiter.decode('utf-16-le')  # or pass in Unicode
       return file.read(size).decode('utf-16-le').split(delimiter)[index]
   else:
       return file.read(size).decode('utf-16-le')

with open('filename', 'rb') as fobj:
    result = str_extract(fobj, 0, 0x20, b'\x00' * 4, 0)

如果文件在开始时作为BOM,请考虑将文件打开为UTF16,而不是以:

开始。
import io

with io.open('filename', 'r', encoding='utf16') as fobj:
    # ....
并移除显式解码

Python 2演示:

>>> from io import BytesIO
>>> data = b'H\x00e\x00l\x00l\x00o\x00 \x00W\x00o\x00r\x00l\x00d\x00\x00\x00\x00\x001\x002\x003\x00'
>>> fobj = BytesIO(data)
>>> str_extract(fobj, 0, 0x20, '\x00' * 4, 0)
u'Hello World'
>>> str_extract(fobj, 0, 0x20, '\x00' * 4, 1)
u'123'


split
是PHP的
explode
的等效函数。您是基于
0x00000000
的实际字符串进行拆分,还是检查文件中的实际零字节?@figs检查4个零的实际序列。我举了一个例子来说明我的观点。那么您的文件中的
字符是什么e?它们实际上不在我的文件中,只是我用来指示可读性的零序列的管道。这是在Python 2中还是在Python 3中?
split
是PHP的
explode
的等效函数。您是基于
0x00000000
的实际字符串进行拆分,还是检查文件中的实际零字节?@figs对于4个零的实际序列。我已经举了一个例子来说明我的观点。那么文件中的
字符是什么呢?它们实际上不在我的文件中,只是我用来指示一个零序列以便于可读的管道。这是在Python 2中还是在Python 3中?不完全是
'Hello World'
;更像
'H\x00e\x00l\x00l\x00l\x00o\x00\x00W\x00o\x00r\x00l\x00d'
,这是UTF-16小端数据。不太像
'Hello World'
;更像
'H\x00e\x00l\x00o\x00\x00W\x00o\x00r\x00l\x00d'
,这是UTF-16小端数据。现在读取BOM,我会如何检测到这一点?我更喜欢这种方法,因为它比显式解码更干净。@VeraWang:r文件将以字节FF FE开始(将U+FEFF零宽度无中断空格编码为UTF-16 little endian)。抱歉,这里还有另一个问题:这些文件包含英文、德文、法文、日文字符,也可以包含字符串以外的内容。Python对这些字符集是否有预定的十六进制范围?我只想要“可读的”如果有意义,请输入字符。@Verawan:您可以使用
str.translate()
有效地删除某些代码点;
result.translate({0:None})
告诉方法将
0
代码点(一个NUL)映射到
None
,这意味着删除它。@Verawan:或者,使用字典(其键必须是整数,表示Unicode码点)映射到其他码点(同样是整数,但单个Unicode字符也可以),以用其他码点替换码点。so
result.translate({0:u'?'))
将用问号替换NUL字符。现在在BOM表上阅读,我将如何检测?我更喜欢这种方式,因为它比显式解码更干净。@Verawan:您的文件将以字节FF FE(编码U+FEFF零宽度无中断空间到UTF-16小结尾)开头