Por que os literais de strings brutos do Python não podem terminar com uma única barra invertida?

178

Tecnicamente, qualquer número ímpar de barras invertidas, conforme descrito na documentação .

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

Parece que o analisador pode apenas tratar barras invertidas em cadeias brutas como caracteres regulares (não é isso que são as cadeias brutas?), Mas provavelmente estou perdendo algo óbvio.

cdleary
fonte
8
Parece que isso agora é uma pergunta frequente . pode não ter sido quando você fez a pergunta. Eu sei que os documentos que você citou dizem praticamente a mesma coisa, mas eu apenas pensei em adicionar outra fonte de documentação.
oob

Respostas:

124

O motivo é explicado na parte dessa seção que destaquei em negrito:

As aspas da string podem ser escapadas com uma barra invertida, mas a barra invertida permanece na string; por exemplo, r"\""é uma literal de seqüência de caracteres válida que consiste em dois caracteres: uma barra invertida e aspas duplas; r"\"não é uma literal de sequência válida (mesmo uma sequência bruta não pode terminar em um número ímpar de barras invertidas). Especificamente, uma cadeia bruta não pode terminar em uma única barra invertida (uma vez que a barra invertida escaparia do seguinte caractere de citação). Observe também que uma única barra invertida seguida por uma nova linha é interpretada como esses dois caracteres como parte da cadeia, não como uma continuação de linha.

Portanto, as strings brutas não são 100% brutas, ainda há algum processamento rudimentar de barra invertida.

oefe
fonte
20
Oh uau ... isso é estranho. Boa pegada. Faz sentido que r '\' '== "\\'", mas ainda é estranho que o caractere de escape tenha um efeito sem desaparecer.
cdleary
2
@ihightower isso pode funcionar para caminhos do sistema de arquivos, mas existem outros usos da barra invertida. E para os caminhos do sistema de arquivos, não codifique o separador. Use 'os.path.sep', ou melhor, os recursos de nível superior do 'os.path'. (Ou 'pathlib', quando disponível)
oefe
5
Nota: A solução alternativa é usar a concatentação literal adjacente. r"foo\bar\baz" "\\"(quebra automática de parênteses se ambíguo) criará um único literal no momento da compilação, a primeira parte bruta e apenas o último pedacinho não bruto, para permitir a barra invertida à direita.
ShadowRanger #
2
Na IMO, isso apenas reafirma a questão (o que é permitido / vai funcionar e o que não), sem dizer por que foi projetado dessa maneira. Existe uma entrada na FAQ que explica o porquê (as strings brutas foram projetadas para uma finalidade específica, e isso faz sentido no contexto dessa finalidade).
ShreevatsaR
3
Qual é o sentido das strings cruas então? Parece uma implementação sombria do conceito.
Matthew James Briggs
100

Todo o equívoco sobre as strings brutas do python é que a maioria das pessoas pensa que a barra invertida (dentro de uma string bruta) é apenas um caractere regular como todas as outras. Não é. A chave para entender é a sequência de tutoriais deste python:

Quando um prefixo ' r ' ou ' R ' está presente, um caractere após uma barra invertida é incluído na string sem alteração e todas as barras invertidas são deixadas na string

Portanto, qualquer caractere após uma barra invertida faz parte da cadeia bruta. Depois que o analisador entra em uma sequência bruta (não Unicode) e encontra uma barra invertida, ele sabe que existem 2 caracteres (uma barra invertida e um caractere a seguir).

Deste jeito:

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

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

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

e:

r'abc \ ' compreende a, b, c, \,' mas não há uma citação final agora.

O último caso mostra que, de acordo com a documentação, agora um analisador não consegue encontrar a cotação de fechamento, pois a última cotação que você vê acima faz parte da string, ou seja, a barra invertida não pode ser a última aqui, pois 'devorará' o caracter de fechamento da string.

Artur
fonte
8
Isso é realmente mais claro que a resposta aceita. Bom colapso.
Physicsist
4
Eu também encontrar este significativamente mais claro que a resposta aceita, e eu também acontecerá a ser um físico
xdavidliu
22

É assim que é! Eu vejo isso como um desses pequenos defeitos em python!

Eu não acho que haja uma boa razão para isso, mas definitivamente não está analisando; é realmente fácil analisar cadeias brutas com \ como último caractere.

O problema é que, se você permitir que \ seja o último caractere de uma sequência bruta, não poderá colocar "dentro de uma sequência bruta. Parece que o python foi com a permissão" em vez de permitir \ como o último caractere.

No entanto, isso não deve causar nenhum problema.

Se você está preocupado em não conseguir escrever facilmente os caminhos das pastas do Windows, como c:\mypath\não se preocupe, pois pode representá-los como r"C:\mypath"e, se precisar acrescentar um nome de subdiretório, não faça isso com concatenação de cadeias, por de qualquer maneira, não é o caminho certo! usaros.path.join

>>> import os
>>> os.path.join(r"C:\mypath", "subfolder")
'C:\\mypath\\subfolder'
hasen
fonte
2
Bom material auxiliar. :-) O advogado do diabo, no entanto: às vezes você quer diferenciar os caminhos dos arquivos dos diretórios, acrescentando o separador de caminhos. O bom do os.path.join é que ele será recolhido: assista os.path.join ('/ home / cdleary /', 'foo /', 'bar /') == '/ home / cdleary / foo / bar / '
cdleary 15/03/09
Mas não faz diferença (técnica)! os.path.isdir irá dizer-lhe se um determinado caminho é um diretório (pasta)
Hasen
2
Sim, é apenas para indicar a alguém que está lendo o código se você espera que um caminho seja um diretório ou um arquivo.
cdleary
A convenção no Windows é que os arquivos sempre têm uma extensão. não é provável em todos (em circunstâncias normais) para ter um arquivo de texto com um caminho como C: \ caminho \ data
Hasen
5
..ou você pode representá-los como "c: / mypath" e esquecer suas aflições barra invertida completamente :-)
John Fouhy
14

Para terminar uma string não processada com uma barra, sugiro que você use este truque:

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

Outro truque é usar o chr (92) enquanto ele avalia "\".

Recentemente, tive que limpar uma série de barras invertidas e o seguinte foi o seguinte:

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

Percebo que isso não cuida do "porquê", mas o segmento atrai muitas pessoas que procuram uma solução para um problema imediato.

Geekworking
fonte
Mas e se a string original contiver barras invertidas?
Joseph Redfern
2
chr (92) é muito obscura, provavelmente melhor uso "\\"(string não-raw com barra invertida)
clemep
9

Como \ "é permitido dentro da cadeia bruta. Então não pode ser usado para identificar o final da cadeia literal.

Por que não parar de analisar a string literal quando você encontra o primeiro "?

Se fosse esse o caso, \ "não seria permitido dentro da string literal. Mas é.

Brian R. Bondy
fonte
1
Exatamente. Os projetistas de Python provavelmente avaliaram a probabilidade das duas alternativas: a sequência de dois caracteres em \"qualquer lugar dentro de uma cadeia bruta de aspas duplas, OU \ no final da cadeia bruta de aspas duplas. As estatísticas de uso devem favorecer a sequência de dois caracteres em qualquer lugar vs. a sequência de um caractere no final.
Placas
3

A razão pela qual a r'\'sintaxe está incorreta é que, embora a expressão da string seja bruta, as aspas usadas (simples ou duplas) sempre precisam ser escapadas, pois, caso contrário, elas marcariam o final da cotação. Portanto, se você deseja expressar uma única citação dentro de uma cadeia de caracteres entre aspas simples, não há outra maneira senão usar \'. O mesmo se aplica a aspas duplas.

Mas você pode usar:

'\\'
quiabo
fonte
4
Não responde 'porque' :-)
cdleary
2

Outro usuário que excluiu sua resposta (não tem certeza se gostaria de receber créditos) sugeriu que os designers da linguagem Python possam simplificar o design do analisador usando as mesmas regras de análise e expandindo os caracteres de escape para a forma bruta como uma reflexão tardia (se o literal foi marcado como bruto).

Eu pensei que era uma ideia interessante e a incluo como wiki da comunidade para a posteridade.

cdleary
fonte
Mas isso pode evitar que você tenha dois caminhos de código separados do analisador literal de cadeia.
cdleary
2

Apesar de sua função, mesmo uma sequência não processada não pode terminar em uma única barra invertida, porque a barra invertida escapa ao seguinte caractere de citação - você ainda deve escapar do caractere de citação circundante para incorporá-lo à string. Ou seja, r "... \" não é uma literal de cadeia válida - uma cadeia bruta não pode terminar em um número ímpar de barras invertidas.
Se você precisar finalizar uma sequência bruta com uma única barra invertida, poderá usar duas e cortar a segunda.

pawandeep singh
fonte
1

Vindo de C, ficou bem claro para mim que um único \ funciona como caractere de escape, permitindo que você coloque caracteres especiais, como novas linhas, tabulações e aspas, em strings.

Isso de fato desaprova \ como último caractere, pois escapará do "e fará o analisador sufocar. Mas, como apontado anteriormente, \ é legal.


fonte
1
Sim - o cerne da questão era que as seqüências brutas tratam \ como um literal, em vez do início de uma sequência de escape. O estranho é que ele ainda possui propriedades de escape para citar, apesar de ser tratado como um caractere literal.
cdleary
1

algumas dicas :

1) se você precisar manipular barra invertida para o caminho, o módulo python padrão os.path é seu amigo. por exemplo :

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

2) se você deseja criar strings com barra invertida, mas sem barra invertida no final da sua string, a string não processada é sua amiga (use o prefixo 'r' antes da string literal). por exemplo :

r'\one \two \three'

3) se você precisar prefixar uma string em uma variável X com uma barra invertida, poderá fazer o seguinte:

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 você precisar criar uma string com uma barra invertida no final, combine as dicas 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

agora lilypond_statement contém "\DisplayLilyMusic \upper"

viva o python! :)

n3on


fonte
1
Nada disso responde à pergunta "por que", mas os itens 3 e 4 não devem ser usados. Fatiar e adicionar seqüências de caracteres geralmente é uma prática ruim, e você deve preferir r '\ dummy' para # 3 (que funciona bem) e '' .join ([r '\ DisplayLilyMusic', r '\ upper']) a # 4.
cdleary
1
A razão é que as strings são imutáveis ​​e cada fatia / concatenação cria um novo objeto de string imutável que geralmente é descartado. Melhor para acumular todos eles e juntá-las em uma única etapa com str.join (componentes)
cdleary
Oh, whoops - entendeu mal o que você quis dizer com # 3. Eu acho que há um simples '\\' + X é preferível a criar uma string apenas para cortá-la.
cdleary
Basta encontrar os.path.normpathirá remover a barra invertida tailing ... Então, como eu deveria concat o nome do arquivo para o caminho ...
Jing Ele
0

Encontrei esse problema e encontrei uma solução parcial que é boa para alguns casos. Apesar de o python não conseguir finalizar uma sequência com uma única barra invertida, ela pode ser serializada e salva em um arquivo de texto com uma única barra invertida no final. Portanto, se o que você precisa é salvar um texto com uma única barra invertida no seu computador, é possível:

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, ele não está funcionando com o json se você o despejar usando a biblioteca json do python.

Por fim, trabalho com o Spyder e notei que se eu abrir a variável no editor de texto do spider clicando duas vezes em seu nome no explorador de variáveis, ele será apresentado com uma única barra invertida e poderá ser copiado para a área de transferência dessa maneira (não é muito útil para a maioria das necessidades, mas talvez para algumas ..).

Bossa nova
fonte