将填充了零的字节转换为UTF-8字符串

23

我正在解包几个包含C语言中's'类型字段的结构体。这些字段包含由C代码中的strncpy处理的零填充UTF-8字符串(请注意此函数的遗留行为)。如果我解码这些字节,我会得到一个带有大量末尾NUL字符的Unicode字符串。

>>> b'hiya\0\0\0'.decode('utf8')
'hiya\x00\x00\x00'

我以为尾随的零字节是UTF-8的一部分,会被自动删除。

如何正确地删除这些零字节?


愿意接受一个可以对UTF-8尾随字节的处理进行评论的答案。 - Matt Joiner
1
我的理解是,Unicode中的NULL代码点应该以UTF-8编码的空字节来编码,但由于某些语言使用null作为字符串终止符,因此存在另一种编码方式,例如Java中的修改版UTF-8编码,它使用C0,80来编码null。请参见http://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8。 - Duncan
你可以在解包阶段修复它:如果你的输入始终以空字符结尾,那么请使用 ctypes.c_char_p 类型,在输入时将 C 字符串转换为 Python 的 bytes。参见 reading struct in python from created struct in c - jfs
3个回答

25
使用 str.rstrip() 函数来移除末尾的 NUL 字符:
>>> 'hiya\0\0\0'.rstrip('\0')
'hiya'

如果c字符串没有被初始化为零或旧字符串被短字符串覆盖,这可能会失败。当c字符串变量被更改时,只有最后一个字符后面的字节被设置为零。 - phobie
这并不能证明输出没有尾随的空值,因为如果有的话,它就不会被打印出来。然而,如果你使用 len(),那么它可能会有所帮助,因为 len 不关心尾随的空值。 - Nawaz

24

如果字符串没有用null填充到缓冲区的末尾,那么rstripreplace都不会起作用。实际上,缓冲区可能一开始就没有被初始化为null,因此你可能会得到像b'hiya\0x\0'这样的结果。

如果您能够百分之百确定C代码始终使用已初始化的null缓冲区且从不重复使用它,则可能发现rstrip更简单,否则我会选择略微混乱但更安全的方法:

>>> b'hiya\0x\0'.split(b'\0',1)[0]
b'hiya'

这个函数将第一个null视为终止符。


6
我建议使用b'hiya\0x\0'.partition(b'\0')[0]代替。 - tzot
好的。我总是忘记 partition - Duncan

3
与分割/分区解决方案不同,这种方法不会复制多个字符串,对于长字节数组可能更快。
data = b'hiya\0\0\0'
i = data.find(b'\x00')
if i == -1:
  return data
return data[:i]

data[:data.find(0)] 相同的数据。 - iperov
不,如果缓冲区已满(不包含零),该代码将失败。即 data = b'hiyafoo' - phobie

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接