Vous devez traduire toutes les séquences d'échappement antislash littérales. Vous pouvez le faire avec une expression régulière:
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)
Cette décode les données dans un objet bytes
:
>>> broken = rb"Tom\303\241\305\241 Vala"
>>> repair(broken)
b'Tom\xc3\xa1\xc5\xa1 Vala'
>>> repair(broken).decode('utf8')
'Tomáš Vala'
Pour envelopper un fichier existant, vous auriez à mettre en œuvre un io.BufferedIOBase
subclass traduire octets que vous lire:
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
Cela peut être utilisé pour envelopper un fichier binaire existant pour décoder vos données à la volée:
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:
# ...
Démo:
>>> 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', '']
Votre * données réelles * contient des séquences '\ ddd'? C'est une barre oblique inverse littérale et trois chiffres. Ce n'est pas la même chose que d'utiliser '\ ddd' dans un littéral de chaîne Python, où ces séquences ont un sens. Pouvez-vous donner un échantillon de vos données réelles à la place? –
Vos données peuvent être définies dans un littéral de chaîne Python comme 'r 'Tom \ 303 \ 241 \ 305 \ 241 Vala" ', notez le' r' au début, ou en doublant les antislashs. –
Le fichier contient des lignes au format: Tom \ 303 \ 241 \ 305 \ 241 Vala, V \ 303 \ 241lec, 1.1.1984, – Panther