Pourquoi les littéraux de chaîne bruts de Python ne peuvent-ils pas se terminer par une seule barre oblique inverse?

179

Techniquement, tout nombre impair de barres obliques inverses, comme décrit dans la documentation .

>>> r'\'
  File "<stdin>", line 1
    r'\'
       ^
SyntaxError: EOL while scanning string literal
>>> r'\\'
'\\\\'
>>> r'\\\'
  File "<stdin>", line 1
    r'\\\'
         ^
SyntaxError: EOL while scanning string literal

Il semble que l'analyseur pourrait simplement traiter les barres obliques inverses dans les chaînes brutes comme des caractères normaux (n'est-ce pas ce que sont les chaînes brutes?), Mais il me manque probablement quelque chose d'évident.

cdleary
la source
8
ressemble à ceci est maintenant une FAQ . peut-être pas quand vous avez posé la question. Je sais que les documents que vous avez cités disent à peu près la même chose, mais j'ai juste pensé que j'ajouterais une autre source de documentation.
oob

Réponses:

124

La raison est expliquée dans la partie de cette section que j'ai mise en évidence en gras:

Les guillemets de chaîne peuvent être échappés avec une barre oblique inverse, mais la barre oblique inverse reste dans la chaîne; par exemple, r"\""est une chaîne littérale valide composée de deux caractères: une barre oblique inverse et un guillemet double; r"\"n'est pas un littéral de chaîne valide (même une chaîne brute ne peut pas se terminer par un nombre impair de barres obliques inverses). Plus précisément, une chaîne brute ne peut pas se terminer par une seule barre oblique inverse (car la barre oblique inverse échapperait au caractère guillemet suivant). Notez également qu'une seule barre oblique inverse suivie d'une nouvelle ligne est interprétée comme ces deux caractères dans le cadre de la chaîne et non comme une continuation de ligne.

Ainsi, les chaînes brutes ne sont pas 100% brutes, il y a encore un traitement rudimentaire de la barre oblique inverse.

oefe
la source
21
Oh wow ... c'est bizarre. Belle prise. Cela a du sens que r '\' '== "\\'" mais il est toujours étrange que le caractère d'échappement ait un effet sans disparaître.
cdleary
2
@ihightower cela peut fonctionner pour les chemins du système de fichiers, mais il existe d'autres utilisations de la barre oblique inverse. Et pour les chemins du système de fichiers, ne codez pas en dur le séparateur. Utilisez «os.path.sep», ou mieux les fonctionnalités de niveau supérieur de «os.path». (Ou 'pathlib', si disponible)
oefe
5
Remarque: la solution de contournement consiste à utiliser la concatentation littérale adjacente. r"foo\bar\baz" "\\"(wrap in parens si ambiguë) créera un seul littéral au moment de la compilation, dont la première partie est brute, et seul le dernier petit bit est non brut, pour permettre la barre oblique inverse de fin.
ShadowRanger
2
OMI, cela ne fait que reformuler la question (ce qui est autorisé / fonctionnera et quoi non), sans dire pourquoi il est conçu de cette façon. Il y a une entrée de FAQ qui explique en quelque sorte le pourquoi (les chaînes brutes ont été conçues dans un but spécifique, et cela a du sens dans le contexte de cet objectif).
ShreevatsaR
3
Quel est l'intérêt des chaînes brutes alors? Cela ressemble à une mise en œuvre douteuse du concept.
Matthew James Briggs
101

L'idée fausse sur les chaînes brutes de python est que la plupart des gens pensent que la barre oblique inverse (dans une chaîne brute) est juste un caractère régulier comme tous les autres. Ce n'est pas. La clé à comprendre est la séquence de tutoriels de ce python:

Lorsqu'un préfixe « r » ou « R » est présent, un caractère suivant une barre oblique inverse est inclus dans la chaîne sans changement, et toutes les barres obliques inverses sont laissées dans la chaîne

Ainsi, tout caractère suivant une barre oblique inverse fait partie d'une chaîne brute. Une fois que l'analyseur entre une chaîne brute (non Unicode) et rencontre une barre oblique inverse, il sait qu'il y a 2 caractères (une barre oblique inverse et un caractère le suivant).

Par ici:

r'abc \ d ' comprend a, b, c, \, d

r'abc \ 'd' comprend a, b, c, \, ', d

r'abc \ '' comprend a, b, c, \, '

et:

r'abc \ ' comprend a, b, c, \,' mais il n'y a pas de guillemet de fin maintenant.

Le dernier cas montre que selon la documentation, un analyseur ne peut pas trouver de guillemets de fermeture car le dernier guillemet que vous voyez ci-dessus fait partie de la chaîne, c'est-à-dire que la barre oblique inverse ne peut pas être la dernière ici car elle «dévorera» le caractère de fermeture de la chaîne.

Artur
la source
8
C'est en fait plus clair que la réponse acceptée. Belle ventilation.
Mad Physicist
4
Je trouve aussi cela beaucoup plus clair que la réponse acceptée, et il se trouve que je suis aussi physicien
xdavidliu
22

C'est comme ça! Je le vois comme l'un de ces petits défauts de python!

Je ne pense pas qu'il y ait une bonne raison à cela, mais ce n'est certainement pas une analyse; il est vraiment facile d'analyser les chaînes brutes avec \ comme dernier caractère.

Le hic, c'est que si vous autorisez \ à être le dernier caractère d'une chaîne brute, vous ne pourrez pas mettre "dans une chaîne brute. Il semble que python ait accepté" au lieu d'autoriser \ comme dernier caractère.

Cependant, cela ne devrait poser aucun problème.

Si vous craignez de ne pas pouvoir écrire facilement des chemins de dossier Windows tels que c:\mypath\alors ne vous inquiétez pas, car vous pouvez les représenter comme r"C:\mypath", et, si vous devez ajouter un nom de sous-répertoire, ne le faites pas avec la concaténation de chaînes, car ce n'est pas la bonne façon de le faire de toute façon! utilisationos.path.join

>>> import os
>>> os.path.join(r"C:\mypath", "subfolder")
'C:\\mypath\\subfolder'
hasen
la source
2
Bon matériel auxiliaire. :-) L'avocat du diable, cependant: parfois vous voulez différencier les chemins de fichiers des chemins de répertoires en ajoutant le séparateur de chemin. La bonne chose à propos de os.path.join est qu'il les réduira: assert os.path.join ('/ home / cdleary /', 'foo /', 'bar /') == '/ home / cdleary / foo / bar / '
cdleary
Cela ne fait pas de différence (technique) cependant! os.path.isdir vous dira si un certain chemin est un répertoire (dossier)
hasen
2
Oui, c'est juste pour indiquer à quelqu'un qui lit le code si vous vous attendez à ce qu'un chemin soit un répertoire ou un fichier.
cdleary
La convention sur Windows est que les fichiers ont toujours une extension. il est peu probable (dans des circonstances normales) d'avoir un fichier texte avec un chemin tel que c: \ path \ data
hasen
5
..ou vous pouvez les représenter comme "c: / mypath" et oublier complètement vos malheurs de backslash :-)
John Fouhy
14

Pour que vous puissiez terminer une chaîne brute par une barre oblique, je vous suggère d'utiliser cette astuce:

>>> print r"c:\test"'\\'
test\
Charles Beattie
la source
14

Une autre astuce consiste à utiliser chr (92) car il évalue à "\".

J'ai récemment dû nettoyer une chaîne de barres obliques inverses et ce qui suit a fait l'affaire:

CleanString = DirtyString.replace(chr(92),'')

Je me rends compte que cela ne s'occupe pas du «pourquoi» mais le fil attire de nombreuses personnes à la recherche d'une solution à un problème immédiat.

Geekworking
la source
Mais que se passe-t-il si la chaîne d'origine contient des barres obliques inverses?
Joseph Redfern
2
chr (92) est terriblement obscur, probablement préférable à utiliser "\\"(chaîne non brute avec barre oblique inverse)
clemep
9

Puisque \ "est autorisé dans la chaîne brute. Ensuite, il ne peut pas être utilisé pour identifier la fin du littéral de chaîne.

Pourquoi ne pas arrêter l'analyse de la chaîne littérale lorsque vous rencontrez le premier "?

Si tel était le cas, alors \ "ne serait pas autorisé dans la chaîne littérale. Mais c'est le cas.

Brian R. Bondy
la source
1
Exactement. Les concepteurs de Python ont probablement évalué la probabilité des deux alternatives: la séquence de deux caractères \"n'importe où dans une chaîne brute entre guillemets, OR \ à la fin de la chaîne brute entre guillemets. Les statistiques d'utilisation doivent favoriser la séquence de deux caractères n'importe où par rapport à la séquence d'un caractère à la fin.
plaques de cuisson
3

La raison pour laquelle la r'\'syntaxe est incorrecte est que, bien que l'expression de chaîne soit brute, les guillemets utilisés (simples ou doubles) doivent toujours être d'échappement car ils marqueraient la fin du guillemet sinon. Donc, si vous souhaitez exprimer un guillemet simple dans une chaîne entre guillemets simples, il n'y a pas d'autre moyen que d'utiliser \'. Il en va de même pour les guillemets doubles.

Mais vous pouvez utiliser:

'\\'
Gombo
la source
4
Ne répond pas `` pourquoi '' :-)
cdleary
2

Un autre utilisateur qui a depuis supprimé sa réponse (ne sachant pas s'il aimerait être crédité) a suggéré que les concepteurs du langage Python pourraient être en mesure de simplifier la conception de l'analyseur en utilisant les mêmes règles d'analyse et en développant les caractères échappés sous forme brute après coup. (si le littéral a été marqué comme brut).

J'ai trouvé que c'était une idée intéressante et je l'inclus en tant que wiki communautaire pour la postérité.

cdleary
la source
Mais cela peut vous éviter d'avoir deux chemins de code distincts pour l'analyseur de chaîne littérale.
cdleary
2

Malgré son rôle, même une chaîne brute ne peut pas se terminer par une seule barre oblique inverse, car la barre oblique inverse échappe le caractère guillemet suivant - vous devez toujours échapper le caractère guillemet environnant pour l'incorporer dans la chaîne. Autrement dit, r "... \" n'est pas un littéral de chaîne valide - une chaîne brute ne peut pas se terminer par un nombre impair de barres obliques inverses.
Si vous devez terminer une chaîne brute avec une seule barre oblique inverse, vous pouvez en utiliser deux et couper la seconde.

pawandeep singh
la source
1

En venant de C, il me semble assez clair qu'un seul \ fonctionne comme caractère d'échappement vous permettant de mettre des caractères spéciaux tels que des retours à la ligne, des tabulations et des guillemets dans des chaînes.

Cela interdit en effet \ comme dernier caractère car il échappera au "et fera étouffer l'analyseur. Mais comme indiqué précédemment, \ est légal.


la source
1
Ouais - le cœur du problème était que les chaînes brutes traitent \ comme un littéral au lieu du début d'une séquence d'échappement. Ce qui est étrange, c'est qu'il a toujours des propriétés d'échappement pour les guillemets, bien qu'il soit traité comme un caractère littéral.
cdleary
1

quelques conseils :

1) si vous avez besoin de manipuler la barre oblique inverse pour le chemin, alors le module python standard os.path est votre ami. par exemple :

os.path.normpath ('c: / dossier1 /')

2) si vous voulez construire des chaînes avec une barre oblique inverse MAIS sans barre oblique inverse à la FIN de votre chaîne, la chaîne brute est votre ami (utilisez le préfixe «r» avant votre chaîne littérale). par exemple :

r'\one \two \three'

3) si vous avez besoin de préfixer une chaîne dans une variable X avec une barre oblique inverse, vous pouvez le faire:

X='dummy'
bs=r'\ ' # don't forget the space after backslash or you will get EOL error
X2=bs[0]+X  # X2 now contains \dummy

4) si vous devez créer une chaîne avec une barre oblique inverse à la fin, combinez les astuces 2 et 3:

voice_name='upper'
lilypond_display=r'\DisplayLilyMusic \ ' # don't forget the space at the end
lilypond_statement=lilypond_display[:-1]+voice_name

maintenant lilypond_statement contient "\DisplayLilyMusic \upper"

vive le python! :)

n3on


la source
1
Aucune de ces réponses ne répond à la question «pourquoi», mais les numéros 3 et 4 ne doivent pas être utilisés. Le découpage et l'ajout de chaînes sont généralement une mauvaise pratique, et vous devriez préférer r '\ dummy' pour # 3 (ce qui fonctionne bien) et '' .join ([r '\ DisplayLilyMusic', r '\ upper']) à # 4.
cdleary
1
La raison étant que les chaînes sont immuables et que chaque tranche / concaténation crée un nouvel objet chaîne immuable qui est généralement ignoré. Mieux vaut les accumuler tous et les assembler en une seule étape avec str.join (components)
cdleary
Oh, oups - vous avez mal compris ce que vous vouliez dire pour # 3. Je pense que là, un simple '\\' + X est préférable à la création d'une chaîne juste pour la découper.
cdleary
Il suffit de trouver os.path.normpathsupprimera la barre oblique inverse de fin ... Alors, comment dois-je concaténer le nom de fichier dans le chemin ...
Jing He
0

J'ai rencontré ce problème et j'ai trouvé une solution partielle qui convient à certains cas. Bien que python ne puisse pas terminer une chaîne avec une seule barre oblique inverse, elle peut être sérialisée et enregistrée dans un fichier texte avec une seule barre oblique inverse à la fin. Par conséquent, si vous avez besoin d'enregistrer un texte avec une seule barre oblique inverse sur votre ordinateur, il est possible:

x = 'a string\\' 
x
'a string\\' 

# Now save it in a text file and it will appear with a single backslash:

with open("my_file.txt", 'w') as h:
    h.write(x)

BTW, il ne fonctionne pas avec json si vous le videz en utilisant la bibliothèque json de python.

Enfin, je travaille avec Spyder, et j'ai remarqué que si j'ouvre la variable dans l'éditeur de texte d'araignée en double-cliquant sur son nom dans l'explorateur de variables, elle est présentée avec une seule barre oblique inverse et peut être copiée dans le presse-papiers de cette façon (ce n'est pas très utile pour la plupart des besoins mais peut-être pour certains ..).

BossaNova
la source