unicode_escape
ne fonctionne pas en général
Il s'avère que la solution string_escape
ou unicode_escape
ne fonctionne pas en général - en particulier, elle ne fonctionne pas en présence d'Unicode réel.
Si vous pouvez être sûr que chaque caractère non ASCII sera échappé (et rappelez-vous que tout ce qui dépasse les 128 premiers caractères n'est pas ASCII), unicode_escape
fera ce qu'il faut pour vous. Mais s'il y a déjà des caractères littéraux non ASCII dans votre chaîne, les choses iront mal.
unicode_escape
est fondamentalement conçu pour convertir des octets en texte Unicode. Mais dans de nombreux endroits - par exemple, le code source Python - les données source sont déjà du texte Unicode.
La seule façon dont cela peut fonctionner correctement est de coder d'abord le texte en octets. UTF-8 est le codage sensé pour tout le texte, donc cela devrait fonctionner, non?
Les exemples suivants sont en Python 3, de sorte que les littéraux de chaîne sont plus propres, mais le même problème existe avec des manifestations légèrement différentes sur Python 2 et 3.
>>> s = 'naïve \\t test'
>>> print(s.encode('utf-8').decode('unicode_escape'))
naïve test
Eh bien, c'est faux.
La nouvelle façon recommandée d'utiliser les codecs qui décodent du texte en texte est d'appeler codecs.decode
directement. Est ce que ça aide?
>>> import codecs
>>> print(codecs.decode(s, 'unicode_escape'))
naïve test
Pas du tout. (De plus, ce qui précède est une erreur UnicodeError sur Python 2.)
Le unicode_escape
codec, malgré son nom, s'avère supposer que tous les octets non-ASCII sont dans le codage Latin-1 (ISO-8859-1). Vous devriez donc le faire comme ceci:
>>> print(s.encode('latin-1').decode('unicode_escape'))
naïve test
Mais c'est terrible. Cela vous limite aux 256 caractères Latin-1, comme si l'Unicode n'avait jamais été inventé du tout!
>>> 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)
Ajout d'une expression régulière pour résoudre le problème
(Étonnamment, nous n'avons pas actuellement deux problèmes.)
Ce que nous devons faire, c'est n'appliquer le unicode_escape
décodeur qu'aux choses dont nous sommes certains d'être du texte ASCII. En particulier, nous pouvons nous assurer de ne l'appliquer qu'aux séquences d'échappement Python valides, qui sont garanties comme du texte ASCII.
Le plan est de trouver des séquences d'échappement à l'aide d'une expression régulière et d'utiliser une fonction comme argument pour re.sub
les remplacer par leur valeur sans échappement.
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)
Et avec cela:
>>> print(decode_escapes('Ernő \\t Rubik'))
Ernő Rubik
'spam'+"eggs"+'''some'''+"""more"""
soit traitée?