unicode_escape
non funziona in generale
Si scopre che la soluzione string_escape
o unicode_escape
non funziona in generale, in particolare, non funziona in presenza di Unicode effettivo.
Se puoi essere sicuro che ogni carattere non ASCII verrà sottoposto a escape (e ricorda, qualsiasi cosa oltre i primi 128 caratteri non è ASCII), unicode_escape
farà la cosa giusta per te. Ma se nella stringa sono già presenti caratteri letterali non ASCII, le cose andranno storte.
unicode_escape
è progettato fondamentalmente per convertire i byte in testo Unicode. Ma in molti punti, ad esempio il codice sorgente Python, i dati di origine sono già testo Unicode.
L'unico modo in cui questo può funzionare correttamente è se codifichi prima il testo in byte. UTF-8 è la codifica sensata per tutto il testo, quindi dovrebbe funzionare, giusto?
I seguenti esempi sono in Python 3, in modo che le stringhe letterali siano più pulite, ma lo stesso problema esiste con manifestazioni leggermente diverse sia su Python 2 che su 3.
>>> s = 'naïve \\t test'
>>> print(s.encode('utf-8').decode('unicode_escape'))
naïve test
Beh, è sbagliato.
Il nuovo modo consigliato per utilizzare i codec che decodificano il testo in testo è chiamare codecs.decode
direttamente. Questo aiuta?
>>> import codecs
>>> print(codecs.decode(s, 'unicode_escape'))
naïve test
Affatto. (Inoltre, quanto sopra è un UnicodeError su Python 2.)
Il unicode_escape
codec, nonostante il nome, risulta presumere che tutti i byte non ASCII siano nella codifica Latin-1 (ISO-8859-1). Quindi dovresti farlo in questo modo:
>>> print(s.encode('latin-1').decode('unicode_escape'))
naïve test
Ma è terribile. Questo ti limita a 256 caratteri Latin-1, come se Unicode non fosse mai stato inventato!
>>> print('Ernő \\t Rubik'.encode('latin-1').decode('unicode_escape'))
UnicodeEncodeError: 'latin-1' codec can't encode character '\u0151'
in position 3: ordinal not in range(256)
Aggiunta di un'espressione regolare per risolvere il problema
(Sorprendentemente, ora non abbiamo due problemi.)
Quello che dobbiamo fare è applicare il unicode_escape
decodificatore solo a cose che siamo certi saranno testo ASCII. In particolare, possiamo assicurarci di applicarlo solo a sequenze di escape Python valide, che sono garantite come testo ASCII.
Il piano è che troveremo sequenze di escape utilizzando un'espressione regolare e utilizzeremo una funzione come argomento re.sub
per sostituirle con il loro valore senza caratteri di escape.
import re
import codecs
ESCAPE_SEQUENCE_RE = re.compile(r'''
( \\U........ # 8-digit hex escapes
| \\u.... # 4-digit hex escapes
| \\x.. # 2-digit hex escapes
| \\[0-7]{1,3} # Octal escapes
| \\N\{[^}]+\} # Unicode characters by name
| \\[\\'"abfnrtv] # Single-character escapes
)''', re.UNICODE | re.VERBOSE)
def decode_escapes(s):
def decode_match(match):
return codecs.decode(match.group(0), 'unicode-escape')
return ESCAPE_SEQUENCE_RE.sub(decode_match, s)
E con quello:
>>> print(decode_escapes('Ernő \\t Rubik'))
Ernő Rubik
'spam'+"eggs"+'''some'''+"""more"""
venga elaborata?