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

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

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

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

14 ответов

Решение

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

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

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

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

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

Таким образом, любой символ после обратной косой черты является частью необработанной строки. Когда синтаксический анализатор вводит необработанную строку (отличную от юникода) и встречает обратную косую черту, он знает, что есть 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, \,' но завершающей кавычки сейчас нет.

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

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

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

Уловка в том, что если вы позволите \ быть последним символом в необработанной строке, то вы не сможете поместить "внутри необработанной строки. Кажется, что Python пошел с разрешением" вместо разрешения "\" в качестве последнего символа.

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

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

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

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

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

Еще одна хитрость заключается в использовании chr(92), поскольку оно оценивается как "\".

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

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

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

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

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

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

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

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

'\\'

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

Хотя обходные пути (такие какr'C:\some\path' '\\'уступающий'C:\\some\\path\\'(в нотации Python) илиC:\some\path\(дословно)) просты, необходимость в них нелогична. Для сравнения давайте взглянем на C++ и Perl.


В C++ мы можем напрямую использовать синтаксис необработанных строковых литералов.

      #include <iostream>

int main() {
    std::cout << R"(Hello World!)" << std::endl;
    std::cout << R"(Hello World!\)" << std::endl;
    std::cout << R"(Hello World!\\)" << std::endl;
    std::cout << R"(Hello World!\\\)" << std::endl;
}

чтобы получить следующий вывод:

      Hello World!
Hello World!\
Hello World!\\
Hello World!\\\

Если мы хотим использовать закрывающий разделитель (выше:)) внутри строкового литерала мы можем даже расширить синтаксис специальным образом, чтобыR"delimiterString(quotedMaterial)delimiterString". Например,R"asdf(some random delimiters: ( } [ ] { ) < > just for fun)asdf"производит строкуsome random delimiters: ( } [ ] { ) < > just for funна выходе. (Разве это не хорошее использование «asdf»!)


В Perl этот код

      my $str = q{This is a test.\\};
print ($str);
print ("This is another test.\n");

выведет следующее:This is a test.\This is another test.

Замена первой строки на

      my $str = q{This is a test.\};

приведет к сообщению об ошибке:Can't find string terminator "}" anywhere before EOF at main.pl line 1.

Однако Perl, рассматривающий предварительный разделитель как escape-символ, не препятствует тому, чтобы пользователь имел нечетное количество обратных косых черт в конце результирующей строки; например, поставить 3 обратной косой черты\\\в конце$str, просто завершите код 6 обратными косыми чертами:my $str = q{This is a test.\\\\\\};. Важно отметить, что, несмотря на то, что нам нужно удвоить обратную косую черту во входных данных, нет никакого синтаксического ограничения, похожего на Python.


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

  • Python: запрещает нечетное количество обратных косых черт непосредственно перед закрывающим разделителем; простой обходной путьr'stringWithoutFinalBackslash' '\\'
  • C++: разрешает по существу¹ все, что находится между разделителями
  • Perl: позволяет по существу² все, что находится между разделителями, но обратную косую черту необходимо постоянно удваивать.

¹ ОбычайdelimiterStringсам по себе не может быть длиннее 16 символов, но вряд ли это ограничение.

² Если вам нужен сам разделитель, просто экранируйте его с помощью\.

Однако, чтобы быть справедливым в сравнении с Python, мы должны признать, что (1) C++ не имел таких строковых литералов до C++11 и, как известно, его сложно анализировать, и (2) Perl еще труднее анализировать.

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

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

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

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

Наивные необработанные строки

Наивная идея необработанной строки

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

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

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

Реальные необработанные строки (вариант 1)

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

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

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

      '
"
'''
"""

С таким количеством возможностей разделителя почти все можно заставить работать. Единственное исключение было бы, если строковый литерал должен содержать полный список всех разрешенных кавычек Python.

Необработанные строки реального мира (вариант 2, как в Python)

Python, однако, идет другим путем, используя расширенную версию приведенного выше правила. Он эффективно заявляет

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

Таким образом, подход Python в некотором смысле даже более либерален, чем вариант 1 выше, но он имеет побочный эффект «неправильной» интерпретации закрывающей кавычки как части строки, если последний предполагаемый символ строки является обратным слэшем .

Вариант 2 не помогает:

  • Если мне нужна кавычка в моей строке, но не обратная косая черта, допустимая версия моего строкового литерала не будет тем, что мне нужно.
    Однако, учитывая, что в моем распоряжении есть еще три разных типа цитат, я, вероятно, просто выберу одну из них, и моя проблема будет решена, так что это не проблематичный случай.
  • Проблемный случай заключается в следующем: если я хочу, чтобы моя строка заканчивалась обратной косой чертой, я теряюсь. Мне нужно прибегнуть к объединению неисходного строкового литерала, содержащего обратную косую черту.

Заключение

После написания этого я присоединяюсь к нескольким другим плакатам, которые вариант 1 было бы легче понять и принять и, следовательно, более питоническим. Такова жизнь!

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

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

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

Я столкнулся с этой проблемой и нашел частичное решение, которое хорошо для некоторых случаев. Несмотря на то, что 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, дважды щелкнув ее имя в проводнике переменных, она будет отображаться с одной обратной косой чертой и может быть скопирована в буфер обмена таким образом (это не очень полезно для большинства потребностей, но может быть для некоторых..).

Другие вопросы по тегам