在Python中解析带有偏移信息的文本和二进制数据文件
我有一个xml文件,里面包含了一些文本元素标签(每个标签里都有对应的二进制元素的十进制偏移值和数据长度),而所有元素的二进制数据则在文件的最后部分。下面是一个例子。
<?xml version="1.0" encoding="UTF-8"?>
<Package>
<element>
<offset>0</offset>
<length>2961181</length>
<checksum>4238515972</checksum>
<format>gzip</format>
</element>
<element>
<offset>2961181</offset>
<length>5442</length>
<checksum>4238515972</checksum>
<format>bin</format>
</element>
</Package>
BINARY_DATA
请注意,偏移值是十进制的,并且是从头部之后的第一个字节开始计算的。请问我该如何用python解析这个文件,根据偏移值抓取对应的元素,解压(如果它的格式是gzip),并将其存储为一个文件呢?
根据OmnipotentEntity和Jakob_B的回复,我写了一个简短的脚本,想看看它是否能处理第一个元素:
import zlib
f = open("file.xml", "r")
text = f.read()
position = text.find("</Package>\n")
headerSize=position+ len("</Package>\n") + 1
offset=0
f.seek(headerSize + offset)
length = 2961181
bin_data = f.read(length)
zipped=1
if (zipped):
ungziped_str = zlib.decompressobj().decompress('x\x9c' + bin_data)
print(ungziped_str)
f.close()
但是,我遇到了以下错误:
Traceback (most recent call last): File "file_parse.py", line 11, in ? ungziped_str = zlib.decompressobj().decompress('x\x9c' + bin_data) zlib.error: Error -3 while decompressing: invalid block type
这是什么问题呢?是输入文件不正确,还是代码有问题?
3 个回答
确定头部大小。
使用一些XML的技巧来获取偏移量和数据长度。
import zlib
python.seek(headerSize+offset)
mydata = python.read(length)
if (zipped):
ungziped_str = zlib.decompressobj().decompress('x\x9c' + mydata)
然后像平常一样写入文件。
关于gunzip的技巧来源可以查看这个链接:http://codingrecipes.com/ungzip-a-string-in-python-gzinflate-in-python
为什么不使用lxml来搜索结束标签呢?当找到结束标签后,就可以用.seek()方法跳到那个位置,然后读取二进制数据。
关键在于要防止XML解析器在处理二进制数据时出错。lxml库允许你逐行输入数据到解析器中,这样你就可以监控最后一个XML标签,并在那时停止:
from lxml import etree
def process(filename):
f = file(filename,"r")
parser = etree.XMLParser()
for l in f:
parser.feed(l)
if l=="</Package>\n":
break
return parser.close()
这样会返回一个
r=process("junk.xml")
<Element Package at 9f14eb4>
这是一个lxml对象,你可以从中提取数据。第二个对象的偏移量在这里:
>>> r[1][0].text
'2961181'
以此类推。这些信息应该足够你制作一个可行的解决方案。不过要注意Package标签的行结束符,可能有更好的方法来处理这个,如果文件的行结束符不同,这种方法可能就不管用了。