Warum können Pythons rohe String-Literale nicht mit einem einzigen Backslash enden?

176

Technisch gesehen eine beliebige Anzahl von Backslashes, wie in der Dokumentation beschrieben .

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

Es scheint, als könnte der Parser Backslashes in rohen Zeichenfolgen einfach als reguläre Zeichen behandeln (geht es nicht um rohe Zeichenfolgen?), Aber mir fehlt wahrscheinlich etwas Offensichtliches.

cdleary
quelle
8
sieht so aus, als wäre dies jetzt eine FAQ . Möglicherweise nicht, als Sie die Frage gestellt haben. Ich weiß, dass die von Ihnen zitierten Dokumente so ziemlich dasselbe sagen, aber ich dachte nur, ich würde eine weitere Dokumentationsquelle hinzufügen.
oob

Antworten:

124

Der Grund wird in dem Teil dieses Abschnitts erläutert, den ich fett hervorgehoben habe:

Zeichenfolgenanführungszeichen können mit einem Backslash maskiert werden, der Backslash bleibt jedoch in der Zeichenfolge. Beispiel: r"\""Ein gültiges Zeichenfolgenliteral, das aus zwei Zeichen besteht: einem Backslash und einem doppelten Anführungszeichen. r"\"ist kein gültiges String-Literal (selbst ein roher String kann nicht mit einer ungeraden Anzahl von Backslashes enden). Insbesondere kann eine rohe Zeichenfolge nicht mit einem einzelnen Backslash enden (da der Backslash dem folgenden Anführungszeichen entgehen würde). Beachten Sie auch, dass ein einzelner Backslash gefolgt von einer neuen Zeile als diese beiden Zeichen als Teil der Zeichenfolge und nicht als Zeilenfortsetzung interpretiert wird.

Roh-Strings sind also nicht zu 100% roh, es gibt immer noch eine rudimentäre Backslash-Verarbeitung.

oefe
quelle
19
Oh wow ... das ist komisch. Schöner Fang. Es macht Sinn, dass r '\' '== "\\'", aber es ist immer noch seltsam, dass der Escape-Charakter einen Effekt hat, ohne zu verschwinden.
cdleary
2
@ihightower Dies funktioniert möglicherweise für Dateisystempfade, es gibt jedoch auch andere Verwendungszwecke für den Backslash. Und für Dateisystempfade codieren Sie das Trennzeichen nicht fest. Verwenden Sie 'os.path.sep' oder besser die übergeordneten Funktionen von 'os.path'. (Oder 'pathlib', falls verfügbar)
oefe
5
Hinweis: Die Problemumgehung besteht darin, benachbarte Literalkonzentrationen zu verwenden. r"foo\bar\baz" "\\"(Bei Mehrdeutigkeit in Parens einschließen) erstellt beim Kompilieren ein einzelnes Literal, dessen erster Teil roh und nur das letzte winzige Bit nicht roh ist, um den nachfolgenden Backslash zu ermöglichen.
ShadowRanger
2
IMO wiederholt dies nur die Frage (was erlaubt ist / wird funktionieren und was nicht), ohne zu sagen, warum es so gestaltet ist. Es gibt einen FAQ-Eintrag , der das Warum erklärt (Rohzeichenfolgen wurden für einen bestimmten Zweck entwickelt und sind im Zusammenhang mit diesem Zweck sinnvoll).
ShreevatsaR
3
Was ist der Sinn von rohen Saiten? Scheint eine zwielichtige Umsetzung des Konzepts zu sein.
Matthew James Briggs
99

Das ganze Missverständnis über Pythons rohe Saiten ist, dass die meisten Leute denken, dass Backslash (innerhalb einer rohen Saite) nur ein regulärer Charakter ist wie alle anderen. Es ist nicht. Der Schlüssel zum Verständnis ist die Tutorial-Sequenz dieses Pythons:

Wenn ein Präfix ' r ' oder ' R ' vorhanden ist, wird ein Zeichen nach einem Backslash unverändert in die Zeichenfolge aufgenommen, und alle Backslashes bleiben in der Zeichenfolge

Jedes Zeichen, das einem Backslash folgt, ist Teil der Rohzeichenfolge. Sobald der Parser eine unformatierte Zeichenfolge (keine Unicode-Zeichenfolge) eingibt und auf einen Backslash stößt, weiß er, dass zwei Zeichen vorhanden sind (ein Backslash und ein Zeichen folgen darauf).

Diesen Weg:

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

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

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

und:

r'abc \ ' umfasst a, b, c, \', aber es gibt jetzt kein abschließendes Zitat.

Der letzte Fall zeigt, dass ein Parser laut Dokumentation jetzt kein schließendes Zitat finden kann, da das letzte Zitat, das Sie oben sehen, Teil der Zeichenfolge ist, dh, dass der Backslash hier nicht das letzte sein kann, da er das schließende Zeichen der Zeichenfolge "verschlingt".

Artur
quelle
8
Dies ist tatsächlich klarer als die akzeptierte Antwort. Schöne Aufteilung.
Mad Physicist
4
Ich finde das auch deutlich klarer als die akzeptierte Antwort, und ich bin auch ein Physiker
xdavidliu
22

Das ist der Stand der Dinge! Ich sehe es als einen dieser kleinen Fehler in Python!

Ich glaube nicht, dass es einen guten Grund dafür gibt, aber es wird definitiv nicht analysiert. Es ist wirklich einfach, rohe Zeichenfolgen mit \ als letztem Zeichen zu analysieren.

Der Haken ist, wenn Sie zulassen, dass \ das letzte Zeichen in einer Rohzeichenfolge ist, können Sie "nicht in eine Rohzeichenfolge einfügen. Es scheint, dass Python mit dem Zulassen" gegangen ist, anstatt \ als letztes Zeichen zuzulassen.

Dies sollte jedoch keine Probleme verursachen.

Wenn Sie c:\mypath\befürchten , Windows-Ordnerpfade nicht einfach schreiben zu können, wie z. B. nicht, können Sie sie als darstellen r"C:\mypath", und wenn Sie einen Unterverzeichnisnamen anhängen müssen, tun Sie dies nicht mit Zeichenfolgenverkettung, z es ist sowieso nicht der richtige Weg, es zu tun! verwendenos.path.join

>>> import os
>>> os.path.join(r"C:\mypath", "subfolder")
'C:\\mypath\\subfolder'
hasen
quelle
2
Gutes Zusatzmaterial. :-) Devil's Advocate: Manchmal möchten Sie Dateipfade von Verzeichnispfaden unterscheiden, indem Sie das Pfadtrennzeichen anhängen. Das Schöne an os.path.join ist, dass sie zusammenbrechen: os.path.join ('/ home / cdleary /', 'foo /', 'bar /') == '/ home / cdleary / foo / bestätigen bar / '
cdleary
Es macht aber keinen (technischen) Unterschied! os.path.isdir wird Ihnen sagen, ob ein bestimmter Pfad ein Verzeichnis (Ordner) ist
hasen
2
Ja, es soll nur jemandem anzeigen, der den Code liest, ob Sie erwarten, dass ein Pfad ein Verzeichnis oder eine Datei ist.
cdleary
Die Konvention unter Windows ist, dass Dateien immer eine Erweiterung haben. Es ist überhaupt nicht wahrscheinlich (unter normalen Umständen), eine Textdatei mit einem Pfad wie c: \ path \ data zu haben
hasen
5
..oder Sie können sie als "c: / mypath" darstellen und Ihre Backslash-Probleme insgesamt vergessen :-)
John Fouhy
14

Damit Sie eine rohe Zeichenfolge mit einem Schrägstrich beenden können, sollten Sie diesen Trick verwenden:

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

Ein weiterer Trick besteht darin, chr (92) zu verwenden, da es "\" ergibt.

Ich musste kürzlich eine Reihe von Backslashes reinigen und das Folgende hat den Trick gemacht:

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

Mir ist klar, dass sich das nicht um das "Warum" kümmert, aber der Thread zieht viele Leute an, die nach einer Lösung für ein unmittelbares Problem suchen.

Geekworking
quelle
Aber was ist, wenn die ursprüngliche Zeichenfolge Backslashes enthält?
Joseph Redfern
2
chr (92) ist furchtbar dunkel, wahrscheinlich besser zu verwenden "\\"(nicht roher String mit Backslash)
Clemep
9

Da \ "innerhalb der Rohzeichenfolge zulässig ist. Dann kann es nicht verwendet werden, um das Ende des Zeichenfolgenliteral zu identifizieren.

Warum nicht aufhören, das String-Literal zu analysieren, wenn Sie auf das erste stoßen?

Wenn dies der Fall wäre, wäre \ "im Zeichenfolgenliteral nicht zulässig. Aber es ist so.

Brian R. Bondy
quelle
1
Genau. Python-Designer haben wahrscheinlich die Wahrscheinlichkeit der beiden Alternativen bewertet: die aus zwei Zeichen bestehende Sequenz an einer \"beliebigen Stelle innerhalb einer rohen Zeichenfolge mit doppelten Anführungszeichen ODER \ am Ende einer rohen Zeichenfolge mit doppelten Anführungszeichen. Die Nutzungsstatistik muss die Zwei-Zeichen-Sequenz an einer beliebigen Stelle gegenüber der Ein-Zeichen-Sequenz am Ende bevorzugen.
Kochfelder
3

Der Grund für die r'\'syntaktische Falschheit ist, dass die verwendeten Anführungszeichen (einfach oder doppelt), obwohl der Zeichenfolgenausdruck roh ist, immer maskiert werden müssen, da sie sonst das Ende des Anführungszeichens markieren würden. Wenn Sie also ein einfaches Anführungszeichen in einem einfachen Anführungszeichen ausdrücken möchten, gibt es keine andere Möglichkeit als die Verwendung \'. Gleiches gilt für doppelte Anführungszeichen.

Aber Sie könnten verwenden:

'\\'
Gumbo
quelle
4
Antwortet nicht 'warum' :-)
cdleary
2

Ein anderer Benutzer, der seitdem seine Antwort gelöscht hat (nicht sicher, ob er gutgeschrieben werden möchte), schlug vor, dass die Python-Sprachdesigner das Parser-Design möglicherweise vereinfachen können, indem sie dieselben Parsing-Regeln verwenden und nachträglich maskierte Zeichen in Rohform erweitern (wenn das Literal als roh markiert wurde).

Ich fand es eine interessante Idee und füge sie als Community-Wiki für die Nachwelt hinzu.

cdleary
quelle
Möglicherweise können Sie jedoch vermeiden, dass zwei separate Codepfade für String-Literal-Parser vorhanden sind.
cdleary
2

Trotz seiner Rolle kann selbst eine rohe Zeichenfolge nicht mit einem einzelnen Backslash enden, da der Backslash dem folgenden Anführungszeichen entgeht. Sie müssen dennoch das umgebende Anführungszeichen maskieren, um es in die Zeichenfolge einzubetten. Das heißt, r "... \" ist kein gültiges Zeichenfolgenliteral - eine Rohzeichenfolge kann nicht mit einer ungeraden Anzahl von Backslashes enden.
Wenn Sie eine rohe Zeichenfolge mit einem einzelnen Backslash beenden müssen, können Sie zwei verwenden und die zweite abschneiden.

pawandeep singh
quelle
1

Wenn ich von C komme, ist mir ziemlich klar, dass ein einzelnes \ als Escape-Zeichen fungiert, sodass Sie Sonderzeichen wie Zeilenumbrüche, Tabulatoren und Anführungszeichen in Zeichenfolgen einfügen können.

Das verbietet in der Tat \ als letztes Zeichen, da es dem "entgeht und den Parser ersticken lässt. Aber wie bereits erwähnt, ist \ legal.


quelle
1
Ja - das Herzstück des Problems war, dass rohe Zeichenfolgen \ als Literal behandeln und nicht als Beginn einer Escape-Sequenz. Das Seltsame ist, dass es immer noch Escape-Eigenschaften zum Zitieren hat, obwohl es als wörtlicher Charakter behandelt wird.
cdleary
1

einige Hinweise :

1) Wenn Sie den Backslash für den Pfad bearbeiten müssen, ist das Standard-Python-Modul os.path Ihr Freund. zum Beispiel :

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

2) Wenn Sie Zeichenfolgen mit Backslash erstellen möchten, ABER ohne Backslash am Ende Ihrer Zeichenfolge, ist die Rohzeichenfolge Ihr Freund (verwenden Sie das Präfix 'r' vor Ihrer Literalzeichenfolge). zum Beispiel :

r'\one \two \three'

3) Wenn Sie einer Zeichenfolge in einer Variablen X einen Backslash voranstellen müssen, können Sie Folgendes tun:

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) Wenn Sie eine Zeichenfolge mit einem Backslash am Ende erstellen müssen, kombinieren Sie Tipp 2 und 3:

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

jetzt enthält lilypond_statement "\DisplayLilyMusic \upper"

Es lebe Python! :) :)

n3on


quelle
1
Keiner von diesen beantwortet die Frage nach dem "Warum", aber # 3 und # 4 sollten nicht verwendet werden. Das Schneiden und Hinzufügen von Zeichenfolgen ist im Allgemeinen eine schlechte Praxis, und Sie sollten r '\ dummy' für # 3 (was gut funktioniert) und '' .join ([r '\ DisplayLilyMusic', r '\ Upper']) # 4 vorziehen.
CDLeary
1
Der Grund dafür ist, dass Zeichenfolgen unveränderlich sind und jedes Slice / jede Verkettung ein neues unveränderliches Zeichenfolgenobjekt erstellt, das normalerweise verworfen wird. Besser, sie alle zu akkumulieren und sie in einem Schritt mit str.join (Komponenten) zusammenzufügen
cdleary
Oh, whoops - habe falsch verstanden, was du für # 3 gemeint hast. Ich denke, dort wird ein einfaches '\\' + X dem Erstellen eines Strings vorgezogen, nur um ihn zu schneiden.
cdleary
Nur finden os.path.normpathwird den Tailing Backslash entfernen ... Wie soll ich dann den Dateinamen in den Pfad einbinden ...
Jing He
0

Ich bin auf dieses Problem gestoßen und habe eine Teillösung gefunden, die in einigen Fällen gut ist. Obwohl Python einen String nicht mit einem einzigen Backslash beenden kann, kann er serialisiert und in einer Textdatei mit einem einzigen Backslash am Ende gespeichert werden. Wenn Sie also einen Text mit einem einzigen Backslash auf Ihrem Computer speichern müssen, ist Folgendes möglich:

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)

Übrigens funktioniert es nicht mit json, wenn Sie es mit der json-Bibliothek von Python sichern.

Schließlich arbeite ich mit Spyder und habe festgestellt, dass beim Öffnen der Variablen im Texteditor von Spider durch Doppelklicken auf ihren Namen im Variablen-Explorer ein einzelner Backslash angezeigt wird und auf diese Weise in die Zwischenablage kopiert werden kann (dies ist nicht der Fall) sehr hilfreich für die meisten Bedürfnisse, aber vielleicht für einige ..).

BossaNova
quelle