168 votes

Processus de séquences d'échappement dans une chaîne de caractères en Python

Parfois quand je reçois d'entrée à partir d'un fichier ou de l'utilisateur, - je obtenir une chaîne de caractères avec des séquences d'échappement en elle. Je voudrais traiter les séquences d'échappement de la même manière que Python processus de séquences d'échappement dans les chaînes de caractères littérales.

Par exemple, disons myString est défini comme:

>>> myString = "spam\\neggs"
>>> print(myString)
spam\neggs

Je veux une fonction (je l'appellerai process) qui fait cela:

>>> print(process(myString))
spam
eggs

Il est important que la fonction peut traiter toutes les séquences d'échappement en Python (énumérés dans un tableau dans le lien ci-dessus).

Python ont une fonction pour faire cela?

178voto

Jerub Points 17488

La bonne chose à faire est d'utiliser la "chaîne-escape' code pour décoder la chaîne.

>>> myString = "spam\\neggs"
>>> decoded_string = bytes(myString, "utf-8").decode("unicode_escape") # python3 
>>> decoded_string = myString.decode('string_escape') # python2
>>> print(decoded_string)
spam
eggs

N'utilisez pas l'AST ou eval. À l'aide de la chaîne de codecs est beaucoup plus sûr.

164voto

rspeer Points 425

unicode_escape ne fonctionne pas en général

Il s'avère que l' string_escape ou unicode_escape solution ne fonctionne pas en général-en particulier, il ne fonctionne pas en la présence réelle de l'Unicode.

Si vous pouvez être sûr que tous les caractères non-ASCII sera échappé (et n'oubliez pas, quelque chose au-delà les 128 premiers caractères non-ASCII), unicode_escape va faire la bonne chose pour vous. Mais s'il y a des littérale des caractères non-ASCII déjà dans votre chaîne, les choses iront mal.

unicode_escape est fondamentalement conçu pour convertir des octets dans le texte Unicode. Mais dans de nombreux endroits, par exemple, le code source Python -- la source de données est déjà de texte Unicode.

La seule façon cela ne peut fonctionner correctement si vous encoder le texte en octets de la première. UTF-8 est le sensible, codant pour l'ensemble du texte, de sorte que 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 légèrement différentes manifestations 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 mal.

La nouvelle est recommandé d'utiliser des codecs qui décoder le texte dans le texte de l'appel codecs.decode directement. Cela vous aide?

>>> import codecs
>>> print(codecs.decode(s, 'unicode_escape'))
naïve   test

Pas du tout. (Aussi, le ci-dessus est un UnicodeError sur Python 2.)

L' unicode_escape codec, en dépit de son nom, s'avère de supposer que tous les non-octets ASCII sont en Latin-1 (ISO-8859-1) codage. Donc, vous devez faire comme ceci:

>>> print(s.encode('latin-1').decode('unicode_escape'))
naïve    test

Mais c'est terrible. Cela vous limite à 256 caractères Latin-1, comme si Unicode n'avait jamais été inventé à tous!

>>> 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)

L'ajout d'une expression régulière pour résoudre le problème

(Étonnamment, nous n'avons deux problèmes.)

Ce que nous devons faire est seulement d'appliquer l' unicode_escape décodeur de choses que nous sommes certains d'être en texte ASCII. En particulier, nous pouvons assurez-vous seulement de l'appliquer à Python valide les séquences d'échappement, qui sont garantis d'être de texte ASCII.

Le plan est, nous allons trouver des séquences d'échappement à l'aide d'une expression régulière, et d'utiliser une fonction comme argument d' re.sub pour les remplacer par leur non échappés de la valeur.

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 ça:

>>> print(decode_escapes('Ernő \\t Rubik'))
Ernő     Rubik

11voto

Greg Hewgill Points 356191

L' ast.literal_eval fonction est proche, mais il faudra attendre la chaîne pour être correctement cité en premier.

De cours Python interprétation de séquences d'échappement dépend de la façon dont la chaîne est cité ("" vs r"" vs u"", entre guillemets triples, etc) de sorte que vous pouvez envelopper l'entrée de l'utilisateur dans des citations appropriées et passer à l' literal_eval. En l'enveloppant dans des citations permettra également d'éviter l' literal_eval de retour d'un nombre, tuple, dictionnaire, etc.

Les choses pourraient encore être délicat si l'utilisateur types de sociétés non cotées citations du type que vous avez l'intention de s'enrouler autour de la chaîne.

-2voto

Nas Banov Points 7293

Si vous faites confiance à la source de données, juste mettre des guillemets autour d'elle et la fonction eval() il?

>>> myString = 'spam\\neggs'
>>> print eval('"' + myString.replace('"','') + '"')
spam
eggs

PS. ajouté le mal-code-exec de contre-mesure - maintenant, il supprime tous " avant eval-ing

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X