Pourquoi 3 barres obliques inverses égalent 4 dans une chaîne Python?

90

Pouvez-vous me dire pourquoi '?\\\?'=='?\\\\?'donne True? Cela me rend fou et je ne trouve pas de réponse raisonnable ...

>>> list('?\\\?')
['?', '\\', '\\', '?']
>>> list('?\\\\?')
['?', '\\', '\\', '?']
kozooh
la source
8
Ce dernier n'échappe à rien alors il finit par s'échapper
Padraic Cunningham
1
Pas besoin d'inclure list()même:>>> '?\\\?' '?\\\\?'
daboross
@PadraicCunningham Il ne "finit pas par s'échapper". Qu'est ce que ça veut dire?
user253751
De manière amusante, la raison en est qu'ils sont tous deux égaux à deux contre-obliques :-)
RemcoGerlich
@immibis, c'est exactement ce qui se passe. Connaissez-vous la différence entre repr et str? Essayez d'imprimer les deux avec une barre oblique inverse dans la chaîne et cela pourrait devenir clair
Padraic Cunningham

Réponses:

84

Fondamentalement, parce que python est légèrement indulgent dans le traitement de la barre oblique inverse. Citant de https://docs.python.org/2.0/ref/strings.html :

Contrairement à la norme C, toutes les séquences d'échappement non reconnues restent inchangées dans la chaîne, c'est-à-dire que la barre oblique inverse est laissée dans la chaîne .

(Souligné dans l'original)

Par conséquent, en python, ce n'est pas que trois barres obliques inverses sont égales à quatre, c'est que lorsque vous suivez une barre oblique inverse avec un caractère comme ?, les deux ensemble apparaissent comme deux caractères, car ce \?n'est pas une séquence d'échappement reconnue.

Daniel Martin
la source
6
C'est le contraire de clément. Le comportement indulgent est le comportement de la plupart des autres: "si vous faites une contre-oblique d'un caractère qui n'en a pas besoin, la contre-oblique ne fait rien". Avec une autre convention (que les alphanumériques antislash peuvent les rendre spéciaux, mais que la ponctuation antislash les rend toujours non spéciaux), vous obtenez la très belle propriété que vous pouvez en toute sécurité défang une chaîne en annulant toute la ponctuation, sans avoir à savoir quels caractères sont spécialement interpeted - une propriété qui manque à Python.
hobbs
24
Non, le contraire de clément serait de générer une erreur lorsque vous utilisez un échappement de barre oblique inverse non reconnu. (Comme presque tous les langages compilés. Rappelez-vous que le traitement des chaînes de Python est fondamentalement "comme C, sauf que nous n'échappons pas lorsque nous passons des échappements anti-slash invalides") De plus, dans une chaîne quel que soit le langage, il n'y a que deux caractères à échapper - tout ce que vous utilisez comme délimiteur et la barre oblique inverse elle-même. Je ne comprends pas l'argument selon lequel il est difficile de se souvenir de ces deux éléments.
Daniel Martin
@DanielMartin il y a quelques langages où le délimiteur fonctionne comme son propre caractère d'échappement (par exemple 'escape''d'). Vous n'avez même pas besoin de vous souvenir des autres personnages!
SztupY
1
Oh attendez, je suppose que pascal standard a également utilisé ce système - voir nyx.net/~gthompso/self_pasc.txt
Daniel Martin
1
@DanielMartin SQL aussi.
Random832
30

Cela est dû au fait que la barre oblique inverse agit comme un caractère d'échappement pour le ou les caractères qui le suivent immédiatement, si la combinaison représente une séquence d'échappement valide. La douzaine de séquences d'échappement sont répertoriées ici . Ils incluent les plus évidents tels que le saut de ligne \n, la tabulation horizontale \t, le retour chariot \ret les plus obscurs tels que les caractères Unicode nommés utilisant \N{...}, par exemple, \N{WAVY DASH}qui représentent le caractère Unicode \u3030. Le point clé est cependant que si la séquence d'échappement n'est pas connue, la séquence de caractères est laissée telle quelle dans la chaîne.

Une partie du problème peut également être que la sortie de l'interpréteur Python vous induit en erreur. Cela est dû au fait que les contre-obliques sont échappées lors de l'affichage. Cependant, si vous imprimez ces chaînes, vous verrez les barres obliques inverses supplémentaires disparaître.

>>> '?\\\?'
'?\\\\?'
>>> print('?\\\?')
?\\?
>>> '?\\\?' == '?\\?'    # I don't know why you think this is True???
False
>>> '?\\\?' == r'?\\?'   # but if you use a raw string for '?\\?'
True
>>> '?\\\\?' == '?\\\?'  # this is the same string... see below
True

Pour vos exemples spécifiques, dans le premier cas '?\\\?', le premier \échappe la deuxième barre oblique inverse en laissant une seule barre oblique inverse, mais la troisième barre oblique inverse reste en tant que barre oblique inverse car ce \?n'est pas une séquence d'échappement valide. Par conséquent, la chaîne résultante est ?\\?.

Dans le second cas '?\\\\?', la première barre oblique inverse échappe à la seconde, et la troisième barre oblique inverse échappe à la quatrième qui donne la chaîne ?\\?.

C'est pourquoi trois contre-obliques sont identiques à quatre:

>>> '?\\\?' == '?\\\\?'
True

Si vous souhaitez créer une chaîne avec 3 barres obliques inverses, vous pouvez échapper à chaque barre oblique inverse:

>>> '?\\\\\\?'
'?\\\\\\?'
>>> print('?\\\\\\?')
?\\\?

ou vous pourriez trouver les chaînes "brutes" plus compréhensibles:

>>> r'?\\\?'
'?\\\\\\?'
>>> print(r'?\\\?')
?\\\?

Cela active le traitement de la séquence d'échappement pour le littéral de chaîne. Voir Littéraux de chaîne pour plus de détails.

mhawke
la source
Vous avez raison '?\\\?'=='?\\?'donne False, je l'ai mal tapé. Cela devrait être '?\\\?'=='?\\\\?'comme l'indique la question, je l'ai corrigé.
kozooh
13

Parce que \xdans une chaîne de caractères, quand xest pas l' un des personnages backslashable spéciaux comme n, r, t, 0, etc., à une chaîne avec une barre oblique inverse, puis un x.

>>> '\?'
'\\?'
le paul
la source
7

Depuis la page d'analyse lexicale python sous les littéraux de chaîne à l' adresse : https://docs.python.org/2/reference/lexical_analysis.html

Il existe un tableau qui répertorie toutes les séquences d'échappement reconnues.

\\ est une séquence d'échappement qui est === \

\? n'est pas une séquence d'échappement et est === \?

donc '\\\\' est '\\' suivi de '\\' qui est '\\' (deux échappés \)

et '\\\' est '\\' suivi de '\' qui est aussi '\\' (un échappé \ et un brut \)

aussi, il convient de noter que python ne fait pas la distinction entre les guillemets simples et doubles entourant une chaîne littérale, contrairement à certains autres langages.

Donc 'String' et 'String' sont exactement la même chose en python, ils n'affectent pas l'interprétation des séquences d'échappement.

rkh
la source
1

La réponse de mhawke le couvre à peu près, je veux juste le reformuler sous une forme plus concise et avec des exemples minimaux qui illustrent ce comportement.

Je suppose qu'une chose à ajouter est que le traitement d'échappement se déplace de gauche à droite, de sorte que \n trouve d'abord la barre oblique inverse, puis cherche un caractère à échapper, puis le trouve net l'échappe; \\ntrouve la première barre oblique inverse, trouve la seconde et l'échappe, puis la trouve net la voit comme un littéral n; \?trouve une barre oblique inverse et recherche un caractère à échapper, trouve ?qui ne peut pas être échappé, et donc le traite \comme une barre oblique inverse littérale.

Comme l'a noté mhawke, la clé ici est que l'interpréteur interactif échappe la barre oblique inverse lors de l'affichage d'une chaîne. J'imagine que la raison en est de garantir que les chaînes de texte copiées de l'interpréteur dans l'éditeur de code sont des chaînes python valides. Cependant, dans ce cas, cette tolérance pour la commodité est source de confusion.

>>> print('\?') # \? is not a valid escape code so backslash is left as-is
\?
>>> print('\\?') # \\ is a valid escape code, resulting in a single backslash
'\?'

>>> '\?' # same as first example except that interactive interpreter escapes the backslash
\\?
>>> '\\?' # same as second example, backslash is again escaped
\\?
Pluvieux
la source