Perché i letterali di stringa grezzi di Python non possono finire con una singola barra rovesciata?


179

Tecnicamente, qualsiasi numero dispari di barre rovesciate, come descritto nella documentazione .

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

Sembra che il parser possa semplicemente trattare le barre rovesciate come stringhe normali (non è quello che sono le stringhe grezze?), Ma probabilmente mi manca qualcosa di ovvio.


8
sembra che ora sia una domanda frequente . potrebbe non essere stato quando hai posto la domanda. so che i documenti che hai citato dicono praticamente la stessa cosa, ma ho pensato di aggiungere un'altra fonte di documentazione.
Oob

Risposte:


124

Il motivo è spiegato nella parte di quella sezione che ho evidenziato in grassetto:

Le virgolette di stringa possono essere salvate con una barra rovesciata, ma la barra rovesciata rimane nella stringa; per esempio, r"\""è una stringa valida letterale composta da due caratteri: una barra rovesciata e una virgoletta doppia; r"\"non è una stringa valida letterale (anche una stringa non elaborata non può terminare con un numero dispari di barre rovesciate). In particolare, una stringa non elaborata non può terminare in una singola barra rovesciata (poiché la barra rovesciata sfuggirebbe al seguente carattere di virgolette). Si noti inoltre che una singola barra rovesciata seguita da una nuova riga viene interpretata come quei due caratteri come parte della stringa, non come una continuazione di riga.

Quindi le stringhe grezze non sono grezze al 100%, c'è ancora qualche rudimentale elaborazione della barra rovesciata.


21
Oh wow ... è strano. Bella presa. Ha senso che r '\' '== "\\'" ma è comunque strano che il personaggio di escape abbia un effetto senza scomparire.
cdleary,

2
@ihightower può funzionare per i percorsi del file system, ma ci sono altri usi della barra rovesciata. E per i percorsi del file system, non codificare il separatore. Utilizzare "os.path.sep" o meglio le funzionalità di livello superiore di "os.path". (O "pathlib", se disponibile)
oefe,

5
Nota: la soluzione alternativa consiste nell'utilizzare la concatentazione letterale adiacente. r"foo\bar\baz" "\\"(avvolgere tra parentesi se ambiguo) creerà un singolo valore letterale al momento della compilazione, la cui prima parte è raw e solo l'ultimo bit non è raw, per consentire la barra rovesciata finale.
ShadowRanger,

2
IMO questo ribadisce la domanda (cosa è permesso / funzionerà e cosa no), senza dire perché è stato progettato in questo modo. C'è una voce FAQ che in qualche modo spiega il perché (le stringhe non elaborate sono state progettate per uno scopo specifico e ha senso nel contesto di tale scopo).
ShreevatsaR,

3
Qual è il punto delle stringhe grezze allora? Sembra un'implementazione losca del concetto.
Matthew James Briggs,

101

L'intero malinteso sulle stringhe grezze di Python è che la maggior parte delle persone pensa che la barra rovesciata (all'interno di una stringa non elaborata) sia solo un personaggio normale come tutte le altre. Non è. La chiave per capire è la sequenza tutorial di questo pitone:

Quando è presente un prefisso ' r ' o ' R ', un carattere che segue una barra rovesciata viene incluso nella stringa senza modifiche e tutte le barre rovesciate vengono lasciate nella stringa

Quindi qualsiasi carattere che segue una barra rovesciata fa parte della stringa non elaborata. Una volta che il parser inserisce una stringa non elaborata (non una Unicode) e incontra una barra rovesciata, sa che ci sono 2 caratteri (una barra rovesciata e un carattere che la segue).

Per di qua:

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

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

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

e:

r'abc \ ' comprende a, b, c, \,' ma non ci sono virgolette adesso.

L'ultimo caso mostra che secondo la documentazione ora un parser non riesce a trovare la citazione di chiusura poiché l'ultima citazione che vedi sopra fa parte della stringa, cioè la barra rovesciata non può essere l'ultima qui perché divorerà il carattere di chiusura della stringa.


8
Questo è in realtà più chiaro della risposta accettata. Bella ripartizione.
Fisico pazzo,

4
lo trovo anche molto più chiaro della risposta accettata, e mi capita anche di essere un fisico
xdavidliu,

22

È così che va! Lo vedo come uno di quei piccoli difetti in Python!

Non penso che ci sia una buona ragione per questo, ma sicuramente non sta analizzando; è davvero facile analizzare stringhe non elaborate con \ come ultimo carattere.

Il problema è che se permetti a \ di essere l'ultimo carattere in una stringa non elaborata, non sarai in grado di inserire "all'interno di una stringa non elaborata. Sembra che Python abbia accettato" invece di consentire \ come ultimo carattere.

Tuttavia, ciò non dovrebbe causare alcun problema.

Se sei preoccupato di non essere in grado di scrivere facilmente percorsi di cartelle di Windows come c:\mypath\allora non preoccuparti, perché puoi rappresentarli come r"C:\mypath"e, se devi aggiungere un nome di sottodirectory, non farlo con la concatenazione di stringhe, per non è il modo giusto di farlo comunque! usoos.path.join

>>> import os
>>> os.path.join(r"C:\mypath", "subfolder")
'C:\\mypath\\subfolder'

2
Buon materiale accessorio. :-) L'avvocato del diavolo, però: a volte vuoi differenziare i percorsi dei file dai percorsi delle directory aggiungendo il separatore dei percorsi. La cosa bella di os.path.join è che li farà crollare: asserire os.path.join ('/ home / cdleary /', 'foo /', 'bar /') == '/ home / cdleary / foo / bar / '
cdleary

Tuttavia, non fa alcuna differenza (tecnica)! os.path.isdir ti dirà se un determinato percorso è una directory (cartella)
hasen

2
Sì, è solo per indicare a qualcuno che legge il codice se ti aspetti che un percorso sia una directory o un file.
cdleary,

La convenzione su Windows è che i file hanno un'estensione, sempre. non è affatto probabile (in circostanze normali) avere un file di testo con un percorso come c: \ path \ data
hasen

5
..oppure puoi rappresentarli come "c: / mypath" e dimenticare del tutto i tuoi guai della barra rovesciata :-)
John Fouhy

14

Per terminare una stringa non elaborata con una barra, ti suggerisco di usare questo trucco:

>>> print r"c:\test"'\\'
test\

14

Un altro trucco è usare chr (92) mentre valuta "\".

Di recente ho dovuto pulire una serie di barre rovesciate e il trucco è stato il seguente:

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

Mi rendo conto che questo non si occupa del "perché", ma il filo attira molte persone alla ricerca di una soluzione a un problema immediato.


Ma cosa succede se la stringa originale contiene barre rovesciate?
Joseph Redfern,

2
chr (92) è terribilmente oscuro, probabilmente meglio usare "\\"(stringa non cruda con barra rovesciata)
clemep

9

Poiché \ "è consentito all'interno della stringa non elaborata. Quindi non può essere utilizzato per identificare la fine della stringa letterale.

Perché non smettere di analizzare letteralmente la stringa quando incontri il primo "?

Se così fosse, allora \ "non sarebbe permesso all'interno della stringa letterale. Ma lo è.


1
Esattamente. I progettisti di Python hanno probabilmente valutato la probabilità delle due alternative: la sequenza di due caratteri in \"qualsiasi punto all'interno di una stringa grezza tra virgolette doppie, OR \ alla fine della stringa grezza tra virgolette doppie. Le statistiche di utilizzo devono favorire la sequenza di due caratteri ovunque rispetto alla sequenza di un carattere alla fine.
Piani cottura

3

Il motivo per cui r'\'è sintatticamente errato è che sebbene l'espressione di stringa sia grezza, le virgolette usate (singole o doppie) devono sempre sfuggire poiché altrimenti segnerebbero la fine della citazione. Quindi, se vuoi esprimere una singola virgoletta all'interno di una singola stringa tra virgolette, non c'è altro modo che usare \'. Lo stesso vale per le doppie virgolette.

Ma potresti usare:

'\\'

4
Non risponde "perché" :-)
cdleary,

2

Un altro utente che da allora ha eliminato la risposta (non è sicuro di voler essere accreditato) ha suggerito che i progettisti del linguaggio Python potrebbero essere in grado di semplificare la progettazione del parser utilizzando le stesse regole di analisi ed espandendo i caratteri di escape in formato raw come ripensamento (se il valore letterale è stato contrassegnato come non elaborato).

Ho pensato che fosse un'idea interessante e la sto includendo come wiki della comunità per i posteri.


Ma potrebbe evitare di avere due percorsi di codice separati per stringa-letterale-parser.
cdleary,

2

Nonostante il suo ruolo, anche una stringa non elaborata non può terminare in una singola barra rovesciata, poiché la barra rovesciata sfugge al seguente carattere di virgoletta: è comunque necessario sfuggire al carattere di virgolette circostante per incorporarlo nella stringa. Cioè, r "... \" non è un valore letterale di stringa valido: una stringa non elaborata non può terminare con un numero dispari di barre rovesciate.
Se devi terminare una stringa non elaborata con una singola barra rovesciata, puoi usarne due e tagliare la seconda.


1

Provenendo da C, mi è abbastanza chiaro che un singolo \ funziona come carattere di escape, permettendoti di mettere in stringhe caratteri speciali come newline, tab e virgolette.

Ciò in effetti non consente \ come ultimo carattere poiché sfuggirà al "e farà soffocare il parser. Ma come sottolineato in precedenza \ è legale.


1
Sì, il nocciolo del problema era che le stringhe non elaborate trattano \ letteralmente anziché l'inizio di una sequenza di escape. La cosa strana è che ha ancora proprietà di fuga da citare, nonostante sia trattato come un personaggio letterale.
cdleary,

1

alcuni suggerimenti :

1) se hai bisogno di manipolare la barra rovesciata per il percorso, allora il modulo python standard os.path è tuo amico. per esempio :

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

2) se vuoi costruire stringhe con la barra rovesciata MA MA senza la barra rovesciata alla fine della tua stringa, allora la stringa grezza è tua amica (usa il prefisso 'r' prima della tua stringa letterale). per esempio :

r'\one \two \three'

3) se devi precedere una stringa in una variabile X con una barra rovesciata, puoi farlo:

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) se devi creare una stringa con una barra rovesciata alla fine, combina i suggerimenti 2 e 3:

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

ora contiene lilypond_statement "\DisplayLilyMusic \upper"

viva il pitone! :)

n3on


1
Nessuno di questi risponde alla domanda "perché", ma i numeri 3 e 4 non devono essere usati. Affettare e aggiungere stringhe è generalmente una cattiva pratica e dovresti preferire r '\ dummy' per # 3 (che funziona benissimo) e '' .join ([r '\ DisplayLilyMusic', r '\ upper']) a # 4.
cdleary,

1
Il motivo è che le stringhe sono immutabili e ogni slice / concatenazione crea un nuovo oggetto stringa immutabile che viene generalmente scartato. Meglio accumularli tutti e unirli insieme in un solo passaggio con str.join (componenti)
cdleary,

Oh, whoops - ho capito male cosa volevi dire per # 3. Penso che sia preferito un semplice '\\' + X per creare una stringa solo per tagliarla.
cdleary,

Basta trovare os.path.normpathrimuoverà la barra rovesciata ... Quindi come dovrei concatenare il nome del file nel percorso ...
Jing He

0

Ho riscontrato questo problema e ho trovato una soluzione parziale che è buona in alcuni casi. Nonostante Python non sia in grado di terminare una stringa con una singola barra rovesciata, può essere serializzato e salvato in un file di testo con una singola barra rovesciata alla fine. Pertanto, se è necessario salvare un testo con una singola barra rovesciata sul computer, è possibile:

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)

A proposito, non funziona con JSON se lo scarichi usando la libreria JSON di Python.

Infine, lavoro con Spyder e ho notato che se apro la variabile nell'editor di testo di spider facendo doppio clic sul suo nome nell'esploratore di variabili, viene presentato con una singola barra rovesciata e può essere copiato negli appunti in questo modo (non è molto utile per la maggior parte delle esigenze ma forse per alcuni ..).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.