从文件中读取以空字符结尾的(C 样式)字符串的干净方法?

Cra*_*ger 5 python string null c-strings python-2.x

我正在寻找一种干净简单的方法来从 Python 中的文件或类文件对象读取以空字符结尾的 C 字符串。以一种不会从文件中消耗比它需要更多的输入的方式,或者将它推回到它所使用的任何文件/缓冲区,以便其他代码可以在以空字符结尾的字符串之后立即读取数据。

我已经看到了一些相当丑陋的代码来做到这一点,但我想使用的代码并不多。

通用换行支持仅适用于open()ed 文件,不适用于 StringIO 对象等,并且看起来不像处理非常规换行。此外,如果它确实有效,则会导致\n附加的字符串,这是不可取的。

struct 看起来根本不支持读取任意长度的 C 字符串,需要将长度作为格式的一部分。

ctypes has c_buffer,它可以从一个字节字符串构造,并将返回第一个空终止字符串作为它的value. 同样,这需要提前确定必须读取多少内容,并且不区分以空字符结尾的字符串和未结尾的字符串。也是如此c_char_p。所以它似乎没有多大帮助,因为你已经知道你已经阅读了足够多的字符串并且必须处理缓冲区拆分。

在 C 中执行此操作的常用方法是将块读取到缓冲区中,如果需要,复制并调整缓冲区大小,然后检查最新读取的块是否包含空字节。如果是,则返回空字节之前的所有内容并重新对齐缓冲区,或者如果您喜欢,请继续阅读并将其用作环形缓冲区。(当然,这仅在您可以将读取的多余数据交给调用者时才有效,或者如果您的平台ungetc允许将大量数据推回到文件中。)

是否有必要在 Python 中拼出类似的代码?我很惊讶没有发现任何罐头ioctypes或者struct

文件对象似乎没有办法推回到它们的缓冲区,比如ungetcio模块中的缓冲 I/O 流也没有。

我觉得我一定错过了这里显而易见的东西。我真的宁愿避免逐字节读取:

def readcstr(f):
    buf = bytearray()
    while True:
        b = f.read(1)
        if b is None or b == '\0':
            return str(buf)
        else:
            buf.append(b)
Run Code Online (Sandbox Code Playgroud)

但现在这就是我正在做的。

Sha*_*ger 6

对你所拥有的东西进行了令人难以置信的温和改进(主要是因为它使用了更多的内置函数,在 CPython 中,是用 C 实现的,通常运行速度更快):

import functools
import itertools

def readcstr(f):
    toeof = iter(functools.partial(f.read, 1), '')
    return ''.join(itertools.takewhile('\0'.__ne__, toeof))
Run Code Online (Sandbox Code Playgroud)

这相对丑陋(并且对文件对象的类型很敏感;它不适用于返回 的文件对象unicode),但会将所有工作推到 C 层。两个 arg iter 确保您在文件耗尽时停止,而itertools.takewhile查找(并消耗)NUL终止符但仅此而已;''.join然后将读取的字节组合成单个返回值。