没有水晶球,没有来自 OP 的信息,所以在 EPO 网站上翻了一下。找到可免费下载的每周专利信息文件,尽管网站上说它在 2006 年将被 utf8/XML 取代,但仍然可以在 cp500/SGML 中找到 :-)。得到了 2009 年第 27 周的文件。是一个包含 2 个文件 s350927[ab].bin 的 zip。“bin”的意思是“不是 XML”。有规格!看起来“专有代码”实际上是BINARY字段。每条记录都有一个固定的 252 字节标头。前 5 个字节是 EBCDIC 中的记录长度,例如十六进制 F0F2F2F0F8 -> 2208 字节。固定头的最后 2 个字节是后面可变部分的 BINARY 长度(冗余)。中间是几个文本字段、两个 2 字节二进制字段和一个 4 字节二进制字段。二进制字段是组内的序列号,但我看到的都是 1。
示例(来自 s350927b.bin 的最后一条记录):
Record number: 7266
pprint of header text and binary slices:
['EPB102055619 TXT00000001',
1,
' 20090701200927 08013627.8 EP20090528NN ',
1,
1,
' T *lots of spaces snipped*']
Edited version of the rather long SGML:
<PATDOC FILE="08013627.8" CY=EP DNUM=2055619 KIND=B1 DATE=20090701 STATUS=N>
*snip*
<B541>DE<B542>Windschutzeinheit für ein Motorrad
<B541>EN<B542>Windshield unit for saddle-ride type vehicle
<B541>FR<B542>Unité pare-brise pour motocyclette</B540>
*snip*
</PATDOC>
没有头或尾记录,只有这一种记录格式。
所以:如果 OP 的年度文件是这样的,我们也许可以帮助他。
更新:上面是“我的时区凌晨 2 点”版本。这里有更多信息:
OP 说:“在每个文件的开头都有几位 ASCII 数字告诉你文件的长度。” ...将其翻译为“在每条记录的开头, EBCDIC中有五位数字可以准确地告诉您记录的长度”,我们有一个(非常模糊的)匹配!
这是文档页面的 URL:http
://docs.epoline.org/ebd/info.htm 提到的第一个文件是规范。
这是下载每周数据页面的 URL: http: //ebd2.epoline.org/jsp/ebdst35.jsp
观察:我查看的数据在 ST.35 系列中。还可以下载 ST.32,它似乎是仅包含 SGML 内容的并行版本(在“减少的 cp437/850”中,每行一个标签)。这表明ST.35记录的定长头中的字段可能不是很有趣,因此可以跳过,这将大大简化转码任务。
值得一提的是,这是我的(调查性,午夜后写的)代码:
[更新 2:稍微整理了代码;没有功能变化]
from pprint import pprint as pp
import sys
from struct import unpack
HDRSZ = 252
T = '>s' # text
H = '>H' # binary 2 bytes
I = '>I' # binary 4 bytes
hdr_defn = [
6, T,
38, H,
40, T,
94, I,
98, H,
100, T,
251, H, # length of following SGML text
HDRSZ + 1
]
# above positions as per spec, reduce to allow for counting from 1
for i in xrange(0, len(hdr_defn), 2):
hdr_defn[i] -= 1
def records(fname, output_encoding='latin1', debug=False):
xlator=''.join(chr(i).decode('cp500').encode(output_encoding, 'replace') for i in range(256))
# print repr(xlator)
def xlate(ebcdic):
return ebcdic.translate(xlator)
# return ebcdic.decode('cp500') # use this if unicode output desired
f = open(fname, 'rb')
recnum = -1
while True:
# get header
buff = f.read(HDRSZ)
if not buff:
return # EOF
recnum += 1
if debug: print "\nrecnum", recnum
assert len(buff) == HDRSZ
recsz = int(xlate(buff[:5]))
if debug: print "recsz", recsz
# split remainder of header into text and binary pieces
fields = []
for i in xrange(0, len(hdr_defn) - 2, 2):
ty = hdr_defn[i + 1]
piece = buff[hdr_defn[i]:hdr_defn[i+2]]
if ty == T:
fields.append(xlate(piece))
else:
fields.append(unpack(ty, piece)[0])
if debug: pp(fields)
sgmlsz = fields.pop()
if debug: print "sgmlsz: %d; expected: %d - %d = %d" % (sgmlsz, recsz, HDRSZ, recsz - HDRSZ)
assert sgmlsz == recsz - HDRSZ
# get sgml part
sgml = f.read(sgmlsz)
assert len(sgml) == sgmlsz
sgml = xlate(sgml)
if debug: print "sgml", sgml
yield recnum, fields, sgml
if __name__ == "__main__":
maxrecs = int(sys.argv[1]) # dumping out the last `maxrecs` records in the file
fname = sys.argv[2]
keep = [None] * maxrecs
for recnum, fields, sgml in records(fname):
# do something useful here
keep[recnum % maxrecs] = (recnum, fields, sgml)
keep.sort()
for k in keep:
if k:
recnum, fields, sgml = k
print
print recnum
pp(fields)
print sgml