在python中读取Null终止的字符串

时间:2016-10-31 00:45:16

标签: 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

现在这给了我这个错误:

TypeError: sequence item 0: expected str instance, int found

我也在尝试以下方法:

char = unpack('c',f.read(1)).decode("ascii")

但它让我失望: AttributeError:'tuple'对象没有属性'decode'

我甚至不知道如何读取字符并将其添加到字符串中,有没有正确的方法来执行此操作?

6 个答案:

答案 0 :(得分:1)

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

也许有一些图书馆可以帮助你解决这个问题,但是由于我不了解它们,我们可以用我们所知道的方式解决问题。

在python中2字节和字符串基本相同,在python 3中更改字符串是py2中的字符串是unicode而字节是它自己的单独类型,这意味着你不需要定义读取char如果你在py2中,因为不需要额外的工作,所以我认为你不需要unpack这个特殊情况的函数,考虑到这一点,我们定义新的readString

def readString(myfile):
    chars = []
    while True:
        c = myfile.read(1)
        if c == chr(0):
            return "".join(chars)
        chars.append(c)

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

现在让我们使用您的sample file

进行测试 例如,让我们尝试阅读" Sword_Wea_Dummy"从它

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

这是输出

string repr: 'Sword_Wea_Dummy'
string: Sword_Wea_Dummy

rest 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>'

其他测试

>>> with open("sword.blendscn","rb") as archi:
        print readString(archi)
        print readString(archi)
        print readString(archi)


sword
Sword_Wea_Dummy
ÍÌÌ=p=Š4:¦6¿JÆ=
>>> with open("sword.blendscn","rb") as archi:
        print repr(readString(archi))
        print repr(readString(archi))
        print repr(readString(archi))


'sword'
'Sword_Wea_Dummy'
'\xcd\xcc\xcc=p=\x8a4:\xa66\xbfJ\x15\xc6='
>>> 

现在我考虑一下,你提到数据部分是固定大小的,如果对所有文件都是如此,那么所有文件的结构如下

[unknow size data][know size data]

然后这是我们可以利用的模式,我们只需要知道文件的大小,我们就可以顺利地获得这两个部分

import os

def getDataPair(filename,knowSize):
    size = os.path.getsize(filename)
    with open(filename, "rb") as archi:
        unknown = archi.read(size-knowSize)
        know    = archi.read()
        return unknown, know

并且通过了解数据部分的大小,它的使用很简单(我通过使用前面的例子来获得)

>>> strins_data, data = getDataPair("sword.blendscn", 80)
>>> string_data, data = getDataPair("sword.blendscn", 80)
>>> string_data
'sword\x00Sword_Wea_Dummy\x00'
>>> data
'\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>'
>>> string_data.split(chr(0))
['sword', 'Sword_Wea_Dummy', '']
>>>          

现在让每个字符串进行简单的拆分就足够了,你可以将data中包含的其余文件传递给适当处理的函数

答案 1 :(得分:0)

这是一个(ab)使用__iter __'鲜为人知的" sentinel"参数:

{{1}}

答案 2 :(得分:0)

这是我写的一个模块,它读取任意终止的行(包括空终止): readline0

它应该比执行单一字符读取的版本快得多。

它也可以在Python 2.x和3.x上运行

我经常使用它。它似乎运作良好。

HTH。

答案 3 :(得分:0)

一次执行一个字符的文件I / O速度非常慢。

现在在pypi:https://pypi.org/project/readline0/上使用readline0。或类似的东西。

在3.x版本中,有一个“换行”参数可以打开,但它似乎不如readline0灵活。

答案 4 :(得分:0)

怎么样:

myString = myNullTerminatedString.split("\x00")[0]

例如:

myNullTerminatedString = "hello world\x00\x00\x00\x00\x00\x00"
myString = myNullTerminatedString.split("\x00")[0]
print(myString) # "hello world"

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

它也适用于字节字符串:

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

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

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

答案 5 :(得分:-1)

这是我的实现方式

import struct

def read_null_str(f):
 r_str = ""
    
    while 1:
        back_offset = f.tell()
        try:
            r_char = struct.unpack("c", f.read(1))[0].decode("utf8")
        except:
            f.seek(back_offset)
            temp_char = struct.unpack("<H", f.read(2))[0]
            r_char = chr(temp_char)
        if ord(r_char) == 0:
            return r_str
        else:
            r_str += r_char