Pyparsing - 从混合的jascii / ascii文本文件解析jascii文本?

时间:2011-05-04 15:36:49

标签: python parsing ascii pyparsing shift-jis

我有一个混合jascii / shift-jis和ascii文本的文本文件。我正在使用pyparsing,我无法对这些字符串进行标记。

以下是一个示例代码:

from pyparsing import *

subrange = r"[\0x%x40-\0x%x7e\0x%x80-\0x%xFC]"
shiftJisChars = u''.join(srange(subrange % (i,i,i,i)) for i in range(0x81,0x9f+1) + range(0xe0,0xfc+1))
jasciistring = Word(shiftJisChars)

jasciistring.parseString(open('shiftjis.txt').read())

我明白了:

Traceback (most recent call last):
  File "test.py", line 7, in 
    jasciistring.parseString(open('shiftjis.txt').read())
  File "C:\python\lib\site-packages\pyparsing.py", line 1100, in parseString
    raise exc pyparsing.ParseException

这是文本文件的内容:

"‚s‚ˆ‚‰‚“@‚‰‚“@‚@‚“‚ˆ‚‰‚†‚”[‚Š‚‰‚“@‚“‚”‚’‚‰‚Ž‚‡B"

(无引号)

3 个答案:

答案 0 :(得分:1)

当您遇到非ASCII字符/字节的问题时,将它们打印到您的控制台并将它们复制/过去到您的问题中是相当无益的。你所看到的往往不是你所拥有的。您应该使用内置的repr()函数[Python 3.x:ascii()]尽可能明确地显示您的数据。

这样做:

python -c "print repr(open('shiftjis.txt', 'rb').read())"

将结果复制/粘贴到编辑问题中。

在等待启示时对数据进行逆向工程:Windows代码页必须是一个好的嫌疑人,cp1252是最常见的。正如@Mark Tolonen所示,cp1252几乎适合,只有一个错误。进一步调查显示,其他cp125x编码会产生2,3或5个错误。只有cp125x编码的AFAIK会将看起来像逗号(实际上是U + 201A SINGLE LOW-9 QUOTATION MARK)的内容映射到shift-jis前导字节\x82。我得出结论,罪犯是cp1252,而且错误是由于运输途中的损坏造成的。

另一种可能性是底层原始编码不是shift-jis,而是日语Windows上使用的超集,即Microsoft cp932。但是,有问题的序列'\x82@'cp932中也无效。在任何情况下,如果您要处理的文件来自日本的Windows机器,最好使用cp932而不是shift-jis

从你的问题和你的代码中你不想明白你想做什么,也不想用字节范围来做,而不是仅仅将数据解码为Unicode。我不使用pyparsing但很可能你提供的子范围很可能是格式错误。

下面是一个如何使用正则表达式标记输入的示例。请注意,pyparsing语法略有不同(\0xff而不是Python的`\ xff')。

代码:

import re, unicodedata

input_bytes = '\x82s\x82\x88\x82\x89\x82\x93@\x82\x89\x82\x93@\x82@\x82\x93\x82\x88\x82\x89\x82\x86\x82\x94[\x82\x8a\x82\x89\x82\x93@\x82\x93\x82\x94\x82\x92\x82\x89\x82\x8e\x82\x87B'

p_ascii = r'[\x00-\x7f]'
p_hw_katakana = r'[\xa1-\xdf]' # half-width Katakana
p_jis208 = r'[\x81-\x9f\xe0-\xef][\x40-\x7e\x80-\xfc]'
p_bad = r'.' # anything else

kinds = ['jis208', 'ascii', 'hwk', 'bad']

re_matcher = re.compile("(" + ")|(".join([p_jis208, p_ascii, p_hw_katakana, p_bad]) + ")")

for mobj in re_matcher.finditer(input_bytes):
    s = mobj.group()
    us = s.decode('shift-jis', 'replace')
    print ("%-6s %-9s %-10r U+%04X %s"
        % (kinds[mobj.lastindex - 1], mobj.span(), s, ord(us), unicodedata.name(us, '<no name>'))
        )

输出:

jis208 (0, 2)    '\x82s'    U+FF34 FULLWIDTH LATIN CAPITAL LETTER T
jis208 (2, 4)    '\x82\x88' U+FF48 FULLWIDTH LATIN SMALL LETTER H
jis208 (4, 6)    '\x82\x89' U+FF49 FULLWIDTH LATIN SMALL LETTER I
jis208 (6, 8)    '\x82\x93' U+FF53 FULLWIDTH LATIN SMALL LETTER S
ascii  (8, 9)    '@'        U+0040 COMMERCIAL AT
jis208 (9, 11)   '\x82\x89' U+FF49 FULLWIDTH LATIN SMALL LETTER I
jis208 (11, 13)  '\x82\x93' U+FF53 FULLWIDTH LATIN SMALL LETTER S
ascii  (13, 14)  '@'        U+0040 COMMERCIAL AT
jis208 (14, 16)  '\x82@'    U+FFFD REPLACEMENT CHARACTER
jis208 (16, 18)  '\x82\x93' U+FF53 FULLWIDTH LATIN SMALL LETTER S
jis208 (18, 20)  '\x82\x88' U+FF48 FULLWIDTH LATIN SMALL LETTER H
jis208 (20, 22)  '\x82\x89' U+FF49 FULLWIDTH LATIN SMALL LETTER I
jis208 (22, 24)  '\x82\x86' U+FF46 FULLWIDTH LATIN SMALL LETTER F
jis208 (24, 26)  '\x82\x94' U+FF54 FULLWIDTH LATIN SMALL LETTER T
ascii  (26, 27)  '['        U+005B LEFT SQUARE BRACKET
jis208 (27, 29)  '\x82\x8a' U+FF4A FULLWIDTH LATIN SMALL LETTER J
jis208 (29, 31)  '\x82\x89' U+FF49 FULLWIDTH LATIN SMALL LETTER I
jis208 (31, 33)  '\x82\x93' U+FF53 FULLWIDTH LATIN SMALL LETTER S
ascii  (33, 34)  '@'        U+0040 COMMERCIAL AT
jis208 (34, 36)  '\x82\x93' U+FF53 FULLWIDTH LATIN SMALL LETTER S
jis208 (36, 38)  '\x82\x94' U+FF54 FULLWIDTH LATIN SMALL LETTER T
jis208 (38, 40)  '\x82\x92' U+FF52 FULLWIDTH LATIN SMALL LETTER R
jis208 (40, 42)  '\x82\x89' U+FF49 FULLWIDTH LATIN SMALL LETTER I
jis208 (42, 44)  '\x82\x8e' U+FF4E FULLWIDTH LATIN SMALL LETTER N
jis208 (44, 46)  '\x82\x87' U+FF47 FULLWIDTH LATIN SMALL LETTER G
ascii  (46, 47)  'B'        U+0042 LATIN CAPITAL LETTER B

注1:您不需要循环并加入O(N ** 2)个字符范围。

如果“jascii”只是意味着“FULLWIDTH LATIN(CAPITAL | SMALL)LETTER [AZ]”(a)你的网络太大了(b)你可以使用UNICODE字符范围而不是BYTE范围轻松地做到这一点(之后)当然解码你的数据。)

答案 1 :(得分:0)

我跳出来的第一件事是你没有将文件打开为二进制文件。我建议使用open('shiftjis.txt', 'rb')之类的代码。您知道该文件包含正常ASCII范围之外的字符,因此通常最好将文件作为二进制文件打开,然后将内容解码为Unicode。也许这样的事情会起作用(假设'shift-jis'是正确的编解码器名称):

text = open('shiftjis.txt', 'rb').read().decode('shift-jis')
jasciistring.parseString(text)

如果parseString()期待str个对象(而不是unicode个对象),那么您可以使用UTF-8更改最后一行以编码text:< / p>

jasciistring.parseString(text.encode('utf-8'))

我唯一的其他建议是验证jasciistring是否包含正确的语法;因为您使用十六进制范围构建它,我希望您首先需要将其视为二进制str,然后将其解码为unicode对象。

答案 2 :(得分:0)

您的“文本文件内容”是mojibake(使用错误的编解码器来解码文件时显示垃圾)。我猜错了编解码器,重新编码文本,用ShiftJIS解码并得到:

# coding: utf8
import codecs
s = u'‚s‚ˆ‚‰‚“@‚‰‚“@‚@‚“‚ˆ‚‰‚†‚”[‚Š‚‰‚“@‚“‚”‚’‚‰‚Ž‚‡B'
s = s.encode('cp1252').decode('shift-jis','replace')
print s

输出

This@is@�shift[jis@stringB

因此默认的美国Windows编解码器不是正确的:^)

您很可能需要做的就是使用shift_jis编解码器读取原始文件:

import codecs
f = codecs.open('shiftjis.txt','rb','shift_jis')
data = f.read()
f.close

data将是包含已解码字符的Unicode字符串。