从文件中读取空终止(C 风格)字符串的简洁方法?
Clean way to read a null-terminated (C-style) string from a file?
我正在寻找一种干净简单的方法来从 Python 中的文件或类文件对象中读取以 null 结尾的 C 字符串。以一种不会从文件中消耗比它需要的更多输入的方式,或者将它推回到它所使用的任何 file/buffer 上,这样其他代码就可以在空终止字符串之后立即读取数据。
我见过 a bit of rather ugly code 这样做,但我想用的不多。
universal newlines support 仅适用于 open()
ed 文件,不适用于 StringIO 对象等,并且看起来不像它处理非常规的换行符。此外,如果它确实有效,它会导致附加 \n
的字符串,这是不可取的。
struct doesn't look like it supports reading arbitrary-length C strings at all,需要长度作为格式的一部分。
ctypes 有 c_buffer
,它可以从字节字符串构造,并将 return 第一个空终止字符串作为它的 value
。同样,这需要确定必须提前读取多少,并且不区分以 null 终止和未终止的字符串。 c_char_p
也是如此。所以它似乎没有多大帮助,因为你已经知道你已经阅读了足够多的字符串并且必须处理缓冲区拆分。
在 C 中执行此操作的通常方法是将块读入缓冲区,如果需要复制缓冲区并调整其大小,然后检查读取的最新块是否包含空字节。如果是这样,return 直到空字节的所有内容,然后重新对齐缓冲区,或者如果您喜欢,请继续阅读并将其用作环形缓冲区。 (当然,这只有在您可以将读取的多余数据交还给调用者,或者如果您的平台的 ungetc
允许将大量数据推回文件中时才有效。)
有必要把Python中类似的代码拼出来吗?我很惊讶没有在 io
、ctypes
或 struct
.
中找到任何罐头
文件对象似乎没有办法像 ungetc
一样推回它们的缓冲区,io
模块中的缓冲 I/O 流也没有。
我觉得我一定错过了这里显而易见的东西。我真的宁愿避免逐字节阅读:
def readcstr(f):
buf = bytearray()
while True:
b = f.read(1)
if b is None or b == '[=10=]':
return str(buf)
else:
buf.append(b)
但现在这就是我正在做的事情。
你所拥有的东西有了难以置信的轻微改进(主要是因为它使用了更多的内置插件,在 CPython 中,这些插件是用 C 实现的,通常运行速度更快):
import functools
import itertools
def readcstr(f):
toeof = iter(functools.partial(f.read, 1), '')
return ''.join(itertools.takewhile('[=10=]'.__ne__, toeof))
这个比较难看(而且对文件对象的类型敏感,对returnunicode
的文件对象不起作用),但是把所有的工作都推到了C层.两个参数 iter 确保你在文件耗尽时停止,而 itertools.takewhile
查找(并使用)NUL
终止符但仅此而已; ''.join
然后将读取的字节组合成一个 return 值。
我正在寻找一种干净简单的方法来从 Python 中的文件或类文件对象中读取以 null 结尾的 C 字符串。以一种不会从文件中消耗比它需要的更多输入的方式,或者将它推回到它所使用的任何 file/buffer 上,这样其他代码就可以在空终止字符串之后立即读取数据。
我见过 a bit of rather ugly code 这样做,但我想用的不多。
universal newlines support 仅适用于 open()
ed 文件,不适用于 StringIO 对象等,并且看起来不像它处理非常规的换行符。此外,如果它确实有效,它会导致附加 \n
的字符串,这是不可取的。
struct doesn't look like it supports reading arbitrary-length C strings at all,需要长度作为格式的一部分。
ctypes 有 c_buffer
,它可以从字节字符串构造,并将 return 第一个空终止字符串作为它的 value
。同样,这需要确定必须提前读取多少,并且不区分以 null 终止和未终止的字符串。 c_char_p
也是如此。所以它似乎没有多大帮助,因为你已经知道你已经阅读了足够多的字符串并且必须处理缓冲区拆分。
在 C 中执行此操作的通常方法是将块读入缓冲区,如果需要复制缓冲区并调整其大小,然后检查读取的最新块是否包含空字节。如果是这样,return 直到空字节的所有内容,然后重新对齐缓冲区,或者如果您喜欢,请继续阅读并将其用作环形缓冲区。 (当然,这只有在您可以将读取的多余数据交还给调用者,或者如果您的平台的 ungetc
允许将大量数据推回文件中时才有效。)
有必要把Python中类似的代码拼出来吗?我很惊讶没有在 io
、ctypes
或 struct
.
文件对象似乎没有办法像 ungetc
一样推回它们的缓冲区,io
模块中的缓冲 I/O 流也没有。
我觉得我一定错过了这里显而易见的东西。我真的宁愿避免逐字节阅读:
def readcstr(f):
buf = bytearray()
while True:
b = f.read(1)
if b is None or b == '[=10=]':
return str(buf)
else:
buf.append(b)
但现在这就是我正在做的事情。
你所拥有的东西有了难以置信的轻微改进(主要是因为它使用了更多的内置插件,在 CPython 中,这些插件是用 C 实现的,通常运行速度更快):
import functools
import itertools
def readcstr(f):
toeof = iter(functools.partial(f.read, 1), '')
return ''.join(itertools.takewhile('[=10=]'.__ne__, toeof))
这个比较难看(而且对文件对象的类型敏感,对returnunicode
的文件对象不起作用),但是把所有的工作都推到了C层.两个参数 iter 确保你在文件耗尽时停止,而 itertools.takewhile
查找(并使用)NUL
终止符但仅此而已; ''.join
然后将读取的字节组合成一个 return 值。