为什么Python的原始字符串文字不能以单个反斜杠结尾?


177

从技术上讲,文档中描述了任意数量的反斜杠。

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

似乎解析器可以将原始字符串中的反斜杠视为常规字符(这不是原始字符串的全部含义吗?),但是我可能缺少明显的东西。


8
看来这是现在的常见问题。当您问这个问题时可能不是。我知道您引用的文档说的差不多,但是我只是想我会添加另一个文档来源。
oob 2010年

Answers:


124

我在该部分中以粗体突出显示了原因:

字符串引号可以使用反斜杠转义,但反斜杠仍保留在字符串中;例如,r"\""是由两个字符组成的有效字符串文字:反斜杠和双引号;r"\"不是有效的字符串文字(即使是原始字符串也不能以奇数个反斜杠结尾)。特别是,原始字符串不能以单个反斜杠结尾(因为反斜杠会转义以下引号字符)。还请注意,单个反斜杠后跟换行符将被解释为这两个字符是字符串的一部分,而不是换行符。

因此,原始字符串不是100%原始的,仍然存在一些基本的反斜杠处理。


19
哇,真奇怪。不错的收获。r'\''==“ \\'”很有道理,但是转义字符有效而没有消失仍然很奇怪。
cdleary

2
@ihightower这可能适用于文件系统路径,但是反斜杠还有其他用途。对于文件系统路径,请不要对分隔符进行硬编码。使用“ os.path.sep”,或者更好的使用“ os.path”的高级功能。(或“ pathlib”,如果可用)
oefe

5
注意:解决方法是使用相邻的文字合并。r"foo\bar\baz" "\\"(如果模棱两可,请包裹在括号中)将在编译时创建一个文字,其第一部分是原始的,只有最后一点是非原始的,以允许使用反斜杠。
ShadowRanger

2
IMO只是在重申问题(允许/将起作用,什么不起作用),而没有说为什么采用这种方式设计。有一个FAQ条目,其中解释了原因(原始字符串是为特定目的而设计的,并且在该目的的上下文中是有意义的)。
ShreevatsaR

3
那么原始字符串有什么意义呢?似乎是该概念的幕后实现。
马修·詹姆斯·布里格斯

99

关于python原始字符串的整个误解是,大多数人都认为反斜杠(在原始字符串内)与其他所有字符一样都是常规字符。它不是。要了解的关键是此python的教程序列:

当存在' r '或' R '前缀时,字符串中包含反斜杠后的字符而无需更改,并且所有反斜杠都保留在字符串中

因此,反斜杠后面的任何字符都是原始字符串的一部分。解析器输入原始字符串(非Unicode字符串)并遇到反斜杠后,便知道存在2个字符(紧随其后的是反斜杠和char)。

这条路:

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'

2
好的辅助材料。:-)但是,Devil的拥护者:有时您希望通过附加路径分隔符来将文件路径与目录路径区分开。os.path.join的好处是它将它们折叠起来:assert os.path.join('/ home / cdleary /','foo /','bar /')=='/ home / cdleary / foo / bar /'
cdleary

但是,这并没有(技术上的)差异!os.path.isdir会告诉您某个路径是否是目录(文件夹)
Hasen

2
是的,仅是向正在阅读代码的人指示您是否希望路径是目录还是文件。
cdleary

Windows上的约定是文件总是带有扩展名。(在正常情况下)根本不可能拥有带有c:\ path \ data这样的路径的文本文件
Hasen

5
..或者您可以将它们表示为“ c:/ mypath”,而完全忘记了您的反斜杠困境:-)
John Fouhy

14

为了使您的原始字符串以斜杠结尾,我建议您可以使用以下技巧:

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

14

另一个技巧是在计算结果为“ \”时使用chr(92)。

最近,我不得不清理一串反斜线,而以下方法可以解决问题:

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

我意识到这并不能解决“为什么”的问题,但是线程吸引了许多人寻找解决当前问题的方法。


但是,如果原始字符串包含反斜杠怎么办?
约瑟夫·雷德芬

2
chr(92)非常晦涩,可能更好使用"\\"(带反斜杠的非原始字符串)
clemep

9

由于原始字符串中允许使用\“。因此不能用于标识字符串文字的结尾。

为什么在遇到第一个“”时不停止解析字符串文字?

如果真是这样,那么在字符串文字中将不允许使用“”。


1
究竟。Python设计人员可能会评估这两种选择的相似性:\"双引号原始字符串中任意位置的两个字符序列,或双引号原始字符串末尾的\。使用情况统计信息必须偏向于任何地方的两个字符序列,而不是最后的一个字符序列。
滚刀

3

r'\'语法错误的原因是,尽管字符串表达式是原始的,但使用的引号(单引号或双引号)始终必须转义,否则它们会标记引号的结尾。因此,如果您想在单引号引起来的字符串中表达单引号,则没有其他方法可以使用\'。同样适用于双引号。

但是您可以使用:

'\\'

4
不回答“为什么” :-)
cdleary

2

此后删除了答案的另一位用户(不确定是否要记入他们的答案)建议,Python语言设计人员可以通过使用相同的解析规则并将转义的字符扩展为原始格式来简化解析器设计。 (如果文字被标记为原始)。

我认为这是一个有趣的想法,并将其作为后代社区Wiki包含在内。


但是,这可能会让您避免拥有两个单独的字符串文字解析器代码路径。
cdleary

2

尽管其作用很大,但即使是原始字符串也不能以单个反斜杠结尾,因为反斜杠会转义以下引号字符—您仍必须先转义周围的引号字符才能将其嵌入到字符串中。也就是说,r“ ... \”不是有效的字符串文字-原始字符串不能以奇数个反斜杠结尾。
如果需要用单个反斜杠结束原始字符串,则可以使用两个反斜杠。


1

从C来看,对我来说很清楚,单个\用作转义符,允许您将特殊字符(例如换行符,制表符和引号)放入字符串中。

确实确实不允许\作为最后一个字符,因为它将逃脱“并使解析器阻塞。但是如前所述,\是合法的。


1
是的-问题的核心是原始字符串将\视为文字,而不是转义序列的开始。奇怪的是,尽管它被视为文字字符,但它仍然具有转义属性以供引用。
cdleary

1

一些技巧 :

1)如果您需要为路径操纵反斜杠,则标准python模块os.path是您的朋友。例如 :

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

2)如果您要构建的字符串中带有反斜杠,但字符串末尾没有反斜杠,那么原始字符串就是您的朋友(在文字字符串前使用'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。切片和添加字符串通常是不好的做法,对于#3,您应该首选r'\ dummy'(效果很好),对于#4,您更希望使用'.join([r'\ DisplayLilyMusic',r'\ upper'])。
cdleary

1
原因是字符串是不可变的,并且每个切片/串联都会创建一个新的不可变的字符串对象,该对象通常被丢弃。最好将它们全部积累起来,并使用str.join(components)一步将它们连接在一起
教父2009年

哦,糟糕-误解了您对#3的含义。我认为有一个简单的'\\'+ X优先于创建仅用于分割字符串的字符串。
cdleary

只是发现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)

顺便说一句,如果您使用python的json库转储它,它就不能与json一起使用。

最后,我使用了Spyder,我注意到,如果我在Spider的文本编辑器中通过在变量资源管理器中双击其名称来打开该变量,则该变量将带有一个反斜杠,并且可以通过这种方式复制到剪贴板(不是对大多数需求都非常有帮助,但也许对某些人很有帮助。)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.