这是EBCDIC (CP500)到Latin-1转换的合理方法吗?

-1 投票
6 回答
6997 浏览
提问于 2025-04-15 12:37

我需要把一些很大的文件(最多2GB)从EBCDIC 500编码转换成Latin-1格式。因为我只找到了一些EBCDIC转ASCII的工具(比如dd和recode),而且这些文件里还有一些额外的专有字符代码,所以我决定自己写一个转换器。

我有了字符映射,所以我对技术细节比较感兴趣。

这是我目前的做法:

# char mapping lookup table
EBCDIC_TO_LATIN1 = {
  0xC1:'41', # A
  0xC2:'42', # B
  # and so on...
}

BUFFER_SIZE = 1024 * 64
ebd_file = file(sys.argv[1], 'rb')
latin1_file = file(sys.argv[2], 'wb')

  buffer = ebd_file.read(BUFFER_SIZE)
  while buffer:
    latin1_file.write(ebd2latin1(buffer))
    buffer = ebd_file.read(BUFFER_SIZE)

ebd_file.close()
latin1_file.close()

这是负责转换的函数:

def ebd2latin1(ebcdic):

   result = []
   for ch in ebcdic:
     result.append(EBCDIC_TO_LATIN1[ord(ch)])

   return ''.join(result).decode('hex')

我想知道从工程的角度来看,这样做是否合理。有没有什么严重的设计问题?缓冲区的大小合适吗?等等……

至于那些不相信“专有字符”的人:每个文件里包含了一整年的专利文件,格式是SGML。专利局一直在用EBCDIC,直到2005年才换成Unicode。所以每个文件里有成千上万的文档。它们之间用一些十六进制值分隔,这些值并不在任何IBM的规范中,而是专利局自己加的。此外,每个文件的开头有几个ASCII数字,告诉你文件的长度。我其实不太需要这些信息,但如果我想处理文件,就得处理这些内容。

还有:

$ recode IBM500/CR-LF..Latin1 file.ebc
recode: file.ebc failed: Ambiguous output in step `CR-LF..data'

感谢大家到目前为止的帮助。

6 个回答

1

虽然这对最初提问的人可能已经没有帮助,但我之前发布了一个适用于Python 2.6及以上版本和3.2及以上版本的包。这个包增加了大部分西方的8位主机编码,包括CP1047(法语)和CP1141(德语)。你可以在这里找到它:https://pypi.python.org/pypi/ebcdic。只需使用 import ebcdic 来引入这些编码,然后用 open(..., encoding='cp1047') 来读取或写入文件。

1

如果你把表格设置得正确,那么你只需要做以下操作:

translated_chars = ebcdic.translate(EBCDIC_TO_LATIN1)

这里的 ebcdic 是包含 EBCDIC 字符的,而 EBCDIC_TO_LATIN1 是一个包含 256 个字符的字符串,它把每个 EBCDIC 字符映射到对应的 Latin-1 字符。EBCDIC_TO_LATIN1 中的字符是实际的二进制值,而不是它们的十六进制表示。例如,如果你使用的是 500 号代码页,那么 EBCDIC_TO_LATIN1 的前 16 个字节将是:

'\x00\x01\x02\x03\x37\x2D\x2E\x2F\x16\x05\x25\x0B\x0C\x0D\x0E\x0F'

可以参考 这个链接

3

EBCDIC 500,也叫做代码页500,是Python支持的一种编码方式。不过你提到的cp1047并不属于这个范围。你到底在用哪个呢?无论如何,这个方法适用于cp500(或者你使用的任何其他编码)。

from __future__ import with_statement
import sys
from contextlib import nested

BUFFER_SIZE = 16384
with nested(open(sys.argv[1], 'rb'), open(sys.argv[2], 'wb')) as (infile, outfile):

    while True:
        buffer = infile.read(BUFFER_SIZE)
        if not buffer:
            break
        outfile.write(buffer.decode('cp500').encode('latin1'))

这样你就不需要自己去记这些映射关系了。

撰写回答