我正在尝试读取一个以空字符结尾的字符串,但是在解压缩字符并将其与字符串放在一起时遇到问题。
这是代码:
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'
我什至不知道如何读取字符并将其添加到字符串中,有什么正确的方法可以做到这一点?
怎么样:
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)
这是一个(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)
(编辑版本2,最后添加了额外的方法)
\n\n也许有一些库可以帮助您解决这个问题,但由于我不了解它们,所以让我们用我们所知道的来解决手头的问题。
\n\n在python 2中,字节和字符串基本上是相同的东西,在python 3中发生了变化,其中字符串是py2中的unicode,而字节是它自己的单独类型,这意味着如果您是,则不需要定义读取字符在 py2 中,因为不需要额外的工作,所以我认为unpack在这种特殊情况下您不需要该函数,考虑到这一点,让我们定义新的readString
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)\nRun Code Online (Sandbox Code Playgroud)\n\n就像您的代码一样,我当时读取了一个字符,但我将它们保存在一个列表中,原因是字符串是不可变的,因此执行 str+=char 会导致不必要的副本;当我找到空字符时返回连接字符串。是chr的倒数ord,它会给你给定字符的 ascii 值。这将排除空字符,如果需要,只需移动附加...
现在让我们用您的示例文件来测试它
\n\n例如,让我们尝试从中读取“Sword_Wea_Dummy”
\n\nwith 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())\nRun Code Online (Sandbox Code Playgroud)\n\n这是输出
\n\nstring 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>\'\nRun 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>>> \nRun Code Online (Sandbox Code Playgroud)\n\n现在我想了一下,您提到数据部分是固定大小的,如果所有文件都是如此,并且所有文件的结构如下
\n\n[unknow size data][know size data]\nRun Code Online (Sandbox Code Playgroud)\n\n那么这就是我们可以利用的模式,我们只需要知道文件的大小,我们就可以顺利地获取这两个部分,如下所示
\n\nimport 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\nRun 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>>> \nRun Code Online (Sandbox Code Playgroud)\n\n现在要获取每个字符串,一个简单的分割就足够了,您可以传递包含在中的文件的其余部分data给要处理的适当函数