2017-09-08 8 views
0

私のコードはASCIIテキストとASCIIの混在したテキストを含むCSVファイルから行を読み込みます。そして、元のテキストをUTF-8で元に戻そうとしていますが、明らかなものがありません。破損したテキストをPython 3.xのデータとして扱う方法

line = "Tom\303\241\305\241 Vala" #Tomáš Vala 
print(a) 
Tomáš Vala #incorrect 

私は、コマンドラインで手動で行を入力した場合、結果は正しいです:

>>> a = b"Tom\303\241\305\241 Vala" 
>>> a = a.decode("utf-8") 
'Tomáš Vala' # correct 

しかし、どのように、それはバイトに既にあるように、ラインを印刷するには?

>>> a = "Tom\303\241\305\241 Vala" 
>>> print(a) 
Tomáš Vala #incorrect 

>>> b = bytes(a, 'utf=8') 
>>> b.decode('utf=8') 
'Tomáš Vala' #incorrect 
+0

あなた*実際のデータ?それは文字通りのバックスラッシュと3桁です。これはPythonの文字列リテラルで '\ ddd'を使うのと同じことではありません。代わりにあなたの実際のデータのサンプルを与えることができますか? –

+0

あなたのデータは 'r" Tom \ 303 \ 241 \ 305 \ 241 Vala "'としてPython文字列リテラルで定義することができ、先頭に 'r'を付けたり、バックスラッシュを倍にしたりすることができます。 –

+0

ファイルには、 Tom \ 303 \ 241 \ 305 \ 241 Vala、V \ 303 \ 241lec、1.1.1984、 – Panther

答えて

0

すべてのリテラルバックスラッシュエスケープシーケンスを翻訳する必要があります。あなたは、正規表現でこれを行うことができます:

import re 

seq = re.compile(br'\\[0-8]{3}') 
decode_seq = lambda m: bytes([int(m.group()[1:], 8)]) 
def repair(data): 
    return seq.sub(decode_seq, data) 

これはbytesオブジェクト内のデータをデコード:

>>> broken = rb"Tom\303\241\305\241 Vala" 
>>> repair(broken) 
b'Tom\xc3\xa1\xc5\xa1 Vala' 
>>> repair(broken).decode('utf8') 
'Tomáš Vala' 

既存のファイルをラップするために、あなたはあなたのようにバイトを変換するためにio.BufferedIOBase subclassを実装する必要があると思います読み:

import re 
from io import BufferedIOBase 

class OctetEscapeDecodeWrapper(BufferedIOBase): 
    def __init__(self, buffer): 
     # we wrap a buffer, not a raw object, so don't use raw here. 
     self._buffer = buffer 
     self._remainder = b'' 

    def readable(self): 
     return True 

    def detach(self): 
     result, self._buffer = self._buffer, None 
     return result 

    def _decode(self, data, 
       _seq=re.compile(br'\\[0-8]{3}'), 
       _decode=lambda m: bytes([int(m.group()[1:], 8)])): 
     return _seq.sub(_decode, data) 

    def read1(self, size=-1): 
     self._remainder, data = b'', self._remainder + self._buffer.read1(size) 
     trail = data.rfind(b'\\', -3) 
     if trail > -1 and all(48 <= data[i] <= 57 for i in range(trail + 1, len(data))): 
      # data ends \dd or \d, retain until next read so we can decode then 
      self._remainder, data = data[trail:], data[:trail] 
     return self._decode(data) 

    read = read1 

    def readinto1(self, b): 
     data = self.read1(len(b)) 
     b[:len(data)] = data 
     return len(data) 

    readinto = readinto1 

これは、その場で自分のデータを復号化するために、既存のバイナリファイルをラップするために使用することができます。

import csv 
from io import TextIOWrapper 

with open(path_to_file, 'rb') as binary: 
    text = TextIOWrapper(OctetEscapeDecodeWrapper(binary), encoding='utf8') 
    reader = csv.reader(text) 
    for row in reader: 
     # ... 

デモ:*は `\ ddd`配列を含む

>>> from io import BytesIO, TextIOWrapper 
>>> sample = BytesIO(b'Tom\303\241\305\241 Vala, V\303\241lec, 1.1.1984,') 
>>> b = OctetEscapeDecodeWrapper(sample) 
>>> t = TextIOWrapper(b, encoding='utf8') 
>>> import csv 
>>> next(csv.reader(t)) 
['Tomáš Vala', ' Válec', ' 1.1.1984', ''] 
+0

という形式の行が含まれています。しかし、私はPythonとプログラミング自体に新しいので、私はあなたがここで何をしたのかを最初にキャッチしなければなりません。 Thx – Panther

+0

@Panther:その場でファイルデータをデコードするデコードラッパーを追加しました。 –

+0

あなたの寛大な援助のために大変です。私は今日何か新しいことを学んだ。 – Panther

関連する問題