Почему необработанные строковые литералы Python не могут заканчиваться одним обратным слешем?

179

Технически, любое нечетное количество обратной косой черты, как описано в документации .

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

Кажется, что синтаксический анализатор может просто обрабатывать обратную косую черту в необработанных строках как обычные символы (не в этом ли смысл необработанные строки?), Но я, вероятно, упускаю что-то очевидное.

cdleary
источник
8
Похоже, это теперь часто задаваемые вопросы . возможно, не было, когда вы задали вопрос. я знаю, что документы, которые вы цитировали, в основном говорят об одном и том же, но я просто подумал, что добавлю еще один источник документации.
2010 года

Ответы:

124

Причина объяснена в той части этого раздела, которую я выделил жирным шрифтом:

Строковые кавычки могут быть экранированы обратной косой чертой, но обратная косая черта остается в строке; например, r"\""допустимый строковый литерал, состоящий из двух символов: обратной косой черты и двойной кавычки; r"\"не является допустимым строковым литералом (даже необработанная строка не может заканчиваться нечетным числом обратных косых черт). В частности, необработанная строка не может заканчиваться одним обратным слешем (так как обратный слеш будет экранировать следующий символ кавычки). Также обратите внимание, что одиночная обратная косая черта, за которой следует новая строка, интерпретируется как эти два символа как часть строки, а не как продолжение строки.

Таким образом, необработанные строки не являются на 100% необработанными, все еще существует некоторая элементарная обратная косая обработка.

oefe
источник
21
Ого ... это странно. Хорошо поймал. Имеет смысл, что r '\' '== "\\'", но все же странно, что экранирующий символ имеет эффект без исчезновения.
cledary
2
@ihightower это может работать для путей в файловой системе, но есть и другие варианты использования обратной косой черты. И для путей файловой системы, не жестко кодируйте разделитель. Используйте «os.path.sep» или, что лучше, функции более высокого уровня «os.path». (Или 'pathlib', когда доступно)
oefe
5
Примечание: Обходной путь должен использовать смежную буквальную конкатенацию. r"foo\bar\baz" "\\"(переносить в parens, если он неоднозначный) создаст один литерал во время компиляции, первая часть которого является необработанной, и только последний маленький бит не является необработанным, чтобы обеспечить обратную косую черту.
ShadowRanger
2
ИМО, это просто повторяет вопрос (что разрешено / будет работать, а что нет), не говоря, почему он разработан таким образом. Есть запись FAQ, которая как бы объясняет причину (необработанные строки были разработаны для конкретной цели, и это имеет смысл в контексте этой цели).
ShreevatsaR
3
Какой смысл в сырых строках? Похоже на теневую реализацию концепции.
Мэтью Джеймс Бриггс
101

Весь заблуждение о необработанных строках python заключается в том, что большинство людей считают, что обратный слеш (внутри необработанной строки) является обычным символом, как и все остальные. Это не. Ключ к пониманию - последовательность обучения этого питона:

При наличии префикса ' r ' или ' R ' символ, следующий за обратной косой чертой, включается в строку без изменений, и все обратные косые черты остаются в строке

Таким образом, любой символ после обратной косой черты является частью необработанной строки. Когда синтаксический анализатор вводит необработанную строку (отличную от Unicode) и встречает обратную косую черту, он знает, что есть 2 символа (обратная косая черта и символ после нее).

Сюда:

r'abc \ d ' включает в себя a, b, c, \, d

r'abc \ 'd' включает в себя a, b, c, \, ', d

r'abc \ '' включает в себя a, b, c, \, '

и:

r'abc \ ' содержит a, b, c, \,' но завершающей кавычки сейчас нет.

Последний случай показывает, что в соответствии с документацией теперь парсер не может найти закрывающую кавычку, так как последняя кавычка, которую вы видите выше, является частью строки, то есть обратная косая черта здесь не может быть последней, поскольку она будет «пожирать» закрывающий символ строки.

Артур
источник
8
Это на самом деле яснее, чем принятый ответ. Хорошая разбивка.
Безумный физик
4
я также нахожу это значительно более ясным, чем принятый ответ, и я также оказался физиком
xdavidliu
22

Так оно и есть! Я вижу это как один из тех небольших дефектов в Python!

Я не думаю, что для этого есть веская причина, но это определенно не разбирается; действительно легко разобрать необработанные строки с \ в качестве последнего символа.

Уловка в том, что если вы позволите \ быть последним символом в необработанной строке, то вы не сможете поместить «внутри необработанной строки. Похоже, что python согласился с разрешением» вместо разрешения \ в качестве последнего символа.

Однако это не должно вызывать никаких проблем.

Если вы беспокоитесь о том, что не можете легко писать пути к папкам Windows, например, c:\mypath\не беспокойтесь, вы можете представлять их как r"C:\mypath", и, если вам нужно добавить имя подкаталога, не делайте это с конкатенацией строк, так как это не правильный способ сделать это в любом случае! использованиеos.path.join

>>> import os
>>> os.path.join(r"C:\mypath", "subfolder")
'C:\\mypath\\subfolder'
Hasen
источник
2
Хороший вспомогательный материал. :-) Однако адвокат дьявола: иногда вы хотите отличить пути к файлам от путей к каталогам, добавив разделитель пути. Хорошая вещь в os.path.join состоит в том, что он свернет их: assert os.path.join ('/ home / cdleary /', 'foo /', 'bar /') == '/ home / cdleary / foo / bar / '
cledary
Это не имеет (технического) значения, хотя! os.path.isdir скажет вам, является ли определенный путь каталогом (папкой)
hasen
2
Да, просто чтобы кто-то читал код, вы ожидаете, что путь будет каталогом или файлом.
cledary
Соглашение о окнах таково, что файлы всегда имеют расширение. маловероятно (при нормальных обстоятельствах) иметь текстовый файл с таким путем, как c: \ path \ data
hasen
5
... или вы можете представить их как "c: / mypath" и вообще забыть о своих проблемах с обратной косой чертой :-)
Джон Фухи
14

Для того, чтобы вы завершили необработанную строку косой чертой, я предлагаю вам использовать этот трюк:

>>> print r"c:\test"'\\'
test\
Чарльз Битти
источник
14

Другой трюк заключается в использовании chr (92), так как он оценивается как "\".

Недавно мне пришлось очистить строку от обратной косой черты, и следующее сделали свое дело:

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

Я понимаю, что это не заботится о «почему», но поток привлекает многих людей, ищущих решение неотложной проблемы.

Geekworking
источник
Но что, если исходная строка содержит обратную косую черту?
Джозеф Редферн
2
chr (92) ужасно неясен, вероятно, лучше использовать "\\"(необработанная строка с обратной косой чертой)
clemep
9

Поскольку \ "разрешено внутри необработанной строки. Тогда его нельзя использовать для определения конца строкового литерала.

Почему бы не прекратить синтаксический анализ строкового литерала, когда вы встречаете первый "?

Если бы это было так, то \ "не было бы разрешено внутри строкового литерала. Но это так.

Брайан Р. Бонди
источник
1
Именно. Разработчики Python, вероятно, оценили сходство двух альтернатив: двухсимвольная последовательность в \"любом месте строки с двойными кавычками , OR \ в конце строки с двойными кавычками . Статистика использования должна благоприятствовать двухсимвольной последовательности в любом месте по сравнению с односимвольной последовательностью в конце.
варенье
3

Причина r'\'синтаксической неверности заключается в том, что, хотя строковое выражение является необработанным, используемые кавычки (одинарные или двойные) всегда должны быть экранированными, так как в противном случае они будут отмечать конец кавычки. Поэтому, если вы хотите выразить одну кавычку внутри строки в одинарных кавычках, нет другого способа, кроме как использовать \'. То же самое относится к двойным кавычкам.

Но вы можете использовать:

'\\'
гумбо
источник
4
Не отвечает «почему» :-)
cledary
2

Другой пользователь, который с тех пор удалил свой ответ (не уверен, что хотел бы получить кредит), предположил, что разработчики языка Python могут упростить конструкцию синтаксического анализатора, используя те же правила синтаксического анализа и расширив экранированные символы в необработанную форму в качестве запоздалой мысли. (если литерал был помечен как необработанный).

Я подумал, что это интересная идея, и я включаю ее в качестве сообщества вики для потомков.

cdleary
источник
Но это может позволить вам избежать двух отдельных путей кода string-literal-parser.
cledary
2

Несмотря на свою роль, даже необработанная строка не может заканчиваться одним обратным слешем, потому что обратный слэш экранирует следующий символ кавычки - вы все равно должны экранировать окружающий символ кавычки, чтобы вставить его в строку. То есть r "... \" не является допустимым строковым литералом - необработанная строка не может заканчиваться нечетным числом обратных косых черт.
Если вам нужно завершить необработанную строку одной обратной косой чертой, вы можете использовать две и вырезать вторую.

Павандип Сингх
источник
1

Исходя из C, мне совершенно ясно, что одиночный \ работает как escape-символ, позволяя вам помещать в строки специальные символы, такие как переводы строк, табуляции и кавычки.

Это действительно запрещает \ как последний символ, поскольку он будет избегать "и заставит парсер задохнуться. Но, как указывалось ранее \, является законным.


источник
1
Да, суть проблемы заключалась в том, что необработанные строки обрабатываются как литерал вместо начала escape-последовательности. Странно то, что у него все еще есть экранирующие свойства для цитирования, несмотря на то, что он рассматривается как буквальный символ.
cledary
1

несколько советов:

1) если вам нужно манипулировать обратной косой чертой для пути, тогда стандартный модуль python os.path - ваш друг. например :

os.path.normpath ( 'C: / папка1 /')

2) если вы хотите построить строки с обратной косой чертой в нем, НО без обратной косой черты в конце вашей строки, тогда raw строка является вашим другом (используйте префикс «r» перед вашей литеральной строкой). например :

r'\one \two \three'

3) если вам нужно поставить строку в переменной X с обратной косой чертой, то вы можете сделать это:

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) если вам нужно создать строку с обратной косой чертой в конце, объедините подсказки 2 и 3:

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

теперь lilypond_statement содержит "\DisplayLilyMusic \upper"

да здравствует питон! :)

n3on


источник
1
Ни один из них не отвечает на вопрос «почему», но № 3 и № 4 не должны использоваться. Нарезка и добавление строк обычно является плохой практикой, и вы должны предпочесть r '\ dummy' для # 3 (который работает нормально) и '' .join ([r '\ DisplayLilyMusic', r '\ upper']) перед # 4.
cledary
1
Причина в том, что строки являются неизменяемыми, и каждый фрагмент / конкатенация создает новый неизменный строковый объект, который обычно отбрасывается. Лучше накапливать их все и соединять вместе в один шаг с str.join (компоненты)
cledary
Ой, упс, неправильно понял, что ты имел в виду для # 3. Я думаю, что простое '\\' + X предпочтительнее, чем создание строки только для ее нарезки.
cledary
Просто найдите os.path.normpath, удалит хвостовую обратную косую черту ... Тогда как мне указать имя файла в пути ...
Jing He
0

Я столкнулся с этой проблемой и нашел частичное решение, которое хорошо в некоторых случаях. Несмотря на то, что python не может завершить строку одним обратным слэшем, его можно сериализовать и сохранить в текстовом файле с одним обратным слэшем в конце. Поэтому, если вам нужно сохранить текст с одной обратной косой чертой на вашем компьютере, это возможно:

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)

Кстати, он не работает с json, если вы выгрузите его с помощью библиотеки json в Python.

Наконец, я работаю со Spyder и заметил, что если я открою переменную в текстовом редакторе Spider, дважды щелкнув по ее имени в проводнике переменных, он будет представлен с одной обратной косой чертой и может быть скопирован в буфер обмена таким образом (это не очень полезно для большинства потребностей, но, возможно, для некоторых ..).

Босса-нова
источник