在python中读取空终止字符串

Sey*_*sor 2 python scripting

我正在尝试读取一个以空字符结尾的字符串,但是在解压缩字符并将其与字符串放在一起时遇到问题。

这是代码:

def readString(f):
    str = ''
    while True:
        char = readChar(f)
        str = str.join(char)
        if (hex(ord(char))) == '0x0':
            break           
    return str

def readChar(f):
    char = unpack('c',f.read(1))[0]
    return char
Run Code Online (Sandbox Code Playgroud)

现在这给了我这个错误:

TypeError: sequence item 0: expected str instance, int found
Run Code Online (Sandbox Code Playgroud)

我也在尝试以下方法:

char = unpack('c',f.read(1)).decode("ascii")
Run Code Online (Sandbox Code Playgroud)

但它抛出我: AttributeError: 'tuple' object has no attribute 'decode'

我什至不知道如何读取字符并将其添加到字符串中,有什么正确的方法可以做到这一点?

fdm*_*ion 8

怎么样:

myString = myNullTerminatedString.split("\x00")[0]
Run Code Online (Sandbox Code Playgroud)

例如:

myNullTerminatedString = "hello world\x00\x00\x00\x00\x00\x00"
myString = myNullTerminatedString.split("\x00")[0]
print(myString) # "hello world"
Run Code Online (Sandbox Code Playgroud)

这是通过在空字符上分割字符串来实现的。由于字符串应在第一个空字符处终止,因此我们只需在拆分后获取列表中的第一项即可。split如果分隔符不存在,将返回一项的列表,因此即使根本没有空终止符,它仍然有效。

它还适用于字节字符串:

myByteString = b'hello world\x00'
myStr = myByteString.split(b'\x00')[0].decode('ascii') # "hello world" as normal string
Run Code Online (Sandbox Code Playgroud)

如果您正在读取文件,则可以进行相对较大的读取 - 估计需要读取多少内容才能找到空字符串。这比逐字节读取要快得多。例如:

resultingStr = ''
while True:
    buf = f.read(512)
    resultingStr += buf
    if len(buf)==0: break
    if (b"\x00" in resultingStr):
        extraBytes = resultingStr.index(b"\x00")
        resultingStr = resultingStr.split(b"\x00")[0]
        break
# now "resultingStr" contains the string

f.seek(0 - extraBytes,1) # seek backwards by the number of bytes, now the pointer will be on the null byte in the file
# or f.seek(1 - extraBytes,1) to skip the null byte in the file
Run Code Online (Sandbox Code Playgroud)


Chr*_*amb 6

这是一个(ab)使用 __iter__ 鲜为人知的“哨兵”参数的版本:

with open('file.txt', 'rb') as f:
    val = ''.join(iter(lambda: f.read(1).decode('ascii'), '\x00'))
Run Code Online (Sandbox Code Playgroud)


Cop*_*eld 2

(编辑版本2,最后添加了额外的方法)

\n\n

也许有一些库可以帮助您解决这个问题,但由于我不了解它们,所以让我们用我们所知道的来解决手头的问题。

\n\n

在python 2中,字节和字符串基本上是相同的东西,在python 3中发生了变化,其中字符串是py2中的unicode,而字节是它自己的单独类型,这意味着如果您是,则不需要定义读取字符在 py2 中,因为不需要额外的工作,所以我认为unpack在这种特殊情况下您不需要该函数,考虑到这一点,让我们定义新的readString

\n\n
def readString(myfile):\n    chars = []\n    while True:\n        c = myfile.read(1)\n        if c == chr(0):\n            return "".join(chars)\n        chars.append(c)\n
Run Code Online (Sandbox Code Playgroud)\n\n

就像您的代码一样,我当时读取了一个字符,但我将它们保存在一个列表中,原因是字符串是不可变的,因此执行 str+=char 会导致不必要的副本;当我找到空字符时返回连接字符串。是chr的倒数ord,它会给你给定字符的 ascii 值。这将排除空字符,如果需要,只需移动附加...

\n\n

现在让我们用您的示例文件来测试它

\n\n

例如,让我们尝试从中读取“Sword_Wea_Dummy”

\n\n
with open("sword.blendscn","rb") as archi:\n    #lets simulate that some prior processing was made by \n    #moving the pointer of the file\n    archi.seek(6) \n    string=readString(archi)\n    print "string repr:", repr(string)\n    print "string:", string\n    print ""\n    #and the rest of the file is there waiting to be processed\n    print "rest of the file: ", repr(archi.read())\n
Run Code Online (Sandbox Code Playgroud)\n\n

这是输出

\n\n
string repr: \'Sword_Wea_Dummy\'\nstring: Sword_Wea_Dummy\n\nrest of the file:  \'\\xcd\\xcc\\xcc=p=\\x8a4:\\xa66\\xbfJ\\x15\\xc6=\\x00\\x00\\x00\\x00\\xeaQ8?\\x9e\\x8d\\x874$-i\\xb3\\x00\\x00\\x00\\x00\\x9b\\xc6\\xaa2K\\x15\\xc6=;\\xa66?\\x00\\x00\\x00\\x00\\xb8\\x88\\xbf@\\x0e\\xf3\\xb1@ITuB\\x00\\x00\\x80?\\xcd\\xcc\\xcc=\\x00\\x00\\x00\\x00\\xcd\\xccL>\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

其他测试

\n\n
>>> with open("sword.blendscn","rb") as archi:\n        print readString(archi)\n        print readString(archi)\n        print readString(archi)\n\n\nsword\nSword_Wea_Dummy\n\xc3\x8d\xc3\x8c\xc3\x8c=p=\xc5\xa04:\xc2\xa66\xc2\xbfJ\xc3\x86=\n>>> with open("sword.blendscn","rb") as archi:\n        print repr(readString(archi))\n        print repr(readString(archi))\n        print repr(readString(archi))\n\n\n\'sword\'\n\'Sword_Wea_Dummy\'\n\'\\xcd\\xcc\\xcc=p=\\x8a4:\\xa66\\xbfJ\\x15\\xc6=\'\n>>> \n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

现在我想了一下,您提到数据部分是固定大小的,如果所有文件都是如此,并且所有文件的结构如下

\n\n
[unknow size data][know size data]\n
Run Code Online (Sandbox Code Playgroud)\n\n

那么这就是我们可以利用的模式,我们只需要知道文件的大小,我们就可以顺利地获取这两个部分,如下所示

\n\n
import os\n\ndef getDataPair(filename,knowSize):\n    size = os.path.getsize(filename)\n    with open(filename, "rb") as archi:\n        unknown = archi.read(size-knowSize)\n        know    = archi.read()\n        return unknown, know\n
Run Code Online (Sandbox Code Playgroud)\n\n

通过了解数据部分的大小,它的使用很简单(我通过前面的示例得到了这一点)

\n\n
>>> strins_data, data = getDataPair("sword.blendscn", 80)\n>>> string_data, data = getDataPair("sword.blendscn", 80)\n>>> string_data\n\'sword\\x00Sword_Wea_Dummy\\x00\'\n>>> data\n\'\\xcd\\xcc\\xcc=p=\\x8a4:\\xa66\\xbfJ\\x15\\xc6=\\x00\\x00\\x00\\x00\\xeaQ8?\\x9e\\x8d\\x874$-i\\xb3\\x00\\x00\\x00\\x00\\x9b\\xc6\\xaa2K\\x15\\xc6=;\\xa66?\\x00\\x00\\x00\\x00\\xb8\\x88\\xbf@\\x0e\\xf3\\xb1@ITuB\\x00\\x00\\x80?\\xcd\\xcc\\xcc=\\x00\\x00\\x00\\x00\\xcd\\xccL>\'\n>>> string_data.split(chr(0))\n[\'sword\', \'Sword_Wea_Dummy\', \'\']\n>>>          \n
Run Code Online (Sandbox Code Playgroud)\n\n

现在要获取每个字符串,一个简单的分割就足够了,您可以传递包含在中的文件的其余部分data给要处理的适当函数

\n