【问题标题】:Why can't Python's raw string literals end with a single backslash?为什么 Python 的原始字符串文字不能以单个反斜杠结尾?
【发布时间】:2010-10-13 11:07:10
【问题描述】:

从技术上讲,任何奇数个反斜杠,如 the documentation 中所述。

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

似乎解析器可以将原始字符串中的反斜杠视为常规字符(这不就是原始字符串的全部内容吗?),但我可能遗漏了一些明显的东西。

【问题讨论】:

  • 看起来现在是faq。当你问这个问题时可能不是。我知道你引用的文档说的差不多,但我只是想我会添加另一个文档来源。
  • @oob 并且该文档清楚地解释了它们主要用于正则表达式(不应以反斜杠结尾)而不是 Windows 路径,它应该。

标签: python string literals rawstring


【解决方案1】:

关于 python 的原始字符串的全部误解是,大多数人认为反斜杠(在原始字符串中)和其他所有字符一样只是一个常规字符。它不是。理解的关键是这个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, \, ' 但现在没有终止引号。

最后一个案例表明,根据文档,现在解析器找不到结束引号,因为您在上面看到的最后一个引号是字符串的一部分,即反斜杠不能在此处最后,因为它会“吞噬”字符串结束字符。

【讨论】:

  • 这实际上比接受的答案更清楚。很好的崩溃。
  • 我也发现这比公认的答案要清楚得多,而且我也恰好是物理学家
  • 所以我了解机制,但为什么呢?为什么这样实施?我看不出这背后的理性。所以上面的解释告诉我们,本质上原始字符串使引号内的所有内容都作为自身,但反斜杠不能不作为最后一个字符出现。所以为什么?这样就可以确保它不能用作文件路径字符串????
  • 当我进一步阅读页面时,我发现它的目的是在字符串中加引号,然后,为什么我不能只放一个引号但我必须放一个在它前面设置反斜杠?我想这一定是有原因的,可能与正则表达式有关?
  • 我认为如果它与正则表达式无关,这是一个设计缺陷,因为还有其他选项可以采用,例如双引号,例如在大多数 .csv 文件中使用 "" for " 。 x = r"I have ""an apple"""代表I have "an apple"。一个问题是python允许a="a""b"a="a" "b"导致a="ab"。所以要使用双引号,python需要禁止a="a""b"的用例。
【解决方案2】:

我遇到了这个问题,并找到了部分解决方案,这对某些情况很有用。尽管 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,我注意到如果我通过在变量资源管理器中双击变量名称在蜘蛛的文本编辑器中打开变量,它会显示一个反斜杠,并且可以通过这种方式复制到剪贴板(它对大多数需求不是很有帮助,但可能对某些人有帮助..)。

【讨论】:

    【解决方案3】:

    尽管有其作用,但即使是原始字符串也不能以单个结尾 反斜杠,因为反斜杠转义了以下引号 字符——您仍然必须将周围的引号字符转义为 将其嵌入字符串中。也就是说, r"...\" 不是一个有效的字符串 文字 - 原始字符串不能以奇数个反斜杠结尾。
    如果您需要以单个反斜杠结束原始字符串,您可以使用 两个并切掉第二个。

    【讨论】:

    【解决方案4】:

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

    我最近不得不清理一串反斜杠,以下是成功的方法:

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

    我意识到这并没有解决“为什么”,但该主题吸引了许多人来寻找直接问题的解决方案。

    【讨论】:

    • 但是如果原始字符串包含反斜杠怎么办?
    • chr(92) 非常晦涩,最好使用"\\"(带有反斜杠的非原始字符串)
    【解决方案5】:

    为了让你用斜杠结束原始字符串,我建议你可以使用这个技巧:

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

    【讨论】:

      【解决方案6】:

      一些提示:

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

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

      2) 如果您想在其中构建带有反斜杠但在字符串的 END 处没有反斜杠的字符串,那么原始字符串是您的朋友(在您的文字字符串之前使用 '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

      【讨论】:

      • 这些都不能回答“为什么”的问题,但不应使用#3 和#4。切片和添加字符串通常是不好的做法,您应该更喜欢 r'\dummy' 用于#3(效果很好)和 ' '.join([r'\DisplayLilyMusic', r'\upper']) 到 #4。
      • 原因是字符串是不可变的,每个切片/连接都会创建一个新的不可变字符串对象,该对象通常会被丢弃。最好将它们全部累积起来,并通过 str.join(components) 一步将它们连接在一起
      • 哦,哎呀——误解了你对 #3 的意思。我认为有一个简单的 '\\' + X 比创建一个字符串来分割它更可取。
      • 只需找到os.path.normpath 将删除尾部反斜杠...那么我应该如何将文件名连接到路径中...
      【解决方案7】:

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

      这确实不允许 \ 作为最后一个字符,因为它会转义 " 并使解析器阻塞。但正如前面指出的, \ 是合法的。

      【讨论】:

      • 是的——问题的核心是原始字符串将 \ 视为文字而不是转义序列的开始。奇怪的是,它仍然具有用于引用的转义属性,尽管被视为文字字符。
      【解决方案8】:

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

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

      如果是这种情况,则字符串文字中不允许使用 \"。但它是。

      【讨论】:

      • 没错。 Python 设计者可能评估了这两种选择的可能性:双引号原始字符串中任意位置的两个字符序列\",或双引号原始字符串末尾的 \。使用统计信息必须支持任意位置的两个字符序列而不是末尾的一个字符序列。
      【解决方案9】:

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

      我认为这是一个有趣的想法,并将其作为社区 wiki 供后代使用。

      【讨论】:

      • 但它可能会让您避免拥有两个单独的字符串文字解析器代码路径。
      【解决方案10】:

      原来如此!我认为这是 python 中的小缺陷之一!

      我认为没有充分的理由,但它绝对不是解析;用 \ 作为最后一个字符来解析原始字符串真的很容易。

      要注意的是,如果您允许 \ 成为原始字符串中的最后一个字符,那么您将无法将 " 放入原始字符串中。似乎 python 允许 " 而不是允许 \ 作为最后一个字符.

      不过,这应该不会造成任何麻烦。

      如果您担心无法轻松编写 Windows 文件夹路径,例如 c:\mypath\,请不要担心,因为您可以将它们表示为 r"C:\mypath",如果您需要附加子目录名称,请不要不要用字符串连接来做,因为无论如何这都不是正确的方法!使用os.path.join

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

      【讨论】:

      • 良好的辅助材料。 :-) 但魔鬼的拥护者:有时您想通过附加路径分隔符来区分文件路径和目录路径。 os.path.join 的好处是它会折叠它们: assert os.path.join('/home/cdleary/', 'foo/', 'bar/') == '/home/cdleary/foo/酒吧/'
      • 它并没有产生(技术)差异! os.path.isdir 会告诉你某个路径是否是目录(文件夹)
      • 是的,这只是为了向阅读代码的人表明您希望路径是目录还是文件。
      • ..或者您可以将它们表示为“c:/mypath”并完全忘记您的反斜杠问题:-)
      • 当然 Python 开发人员没有听说过以 \\?\ 开头的 Windows UNC 路径,并且 os.path.join 不支持。
      【解决方案11】:

      r'\' 语法不正确的原因是,尽管字符串表达式是原始的,但使用的引号(单引号或双引号)总是必须转义,否则它们会标记引号的结尾。所以如果你想在单引号字符串中表达单引号,除了使用\'之外别无他法。同样适用于双引号。

      但你可以使用:

      '\\'
      

      【讨论】:

        【解决方案12】:

        原因在我以粗体突出显示的那部分的部分中进行了解释:

        字符串引号可以用a转义 反斜杠,,但反斜杠仍然存在 在字符串中;例如,r"\"" 是 由两个组成的有效字符串文字 字符:反斜杠和双引号 引用; r"\" 不是有效字符串 文字(即使是原始字符串也不能结束 在奇数个反斜杠中)。 具体来说,原始字符串不能结束 在一个反斜杠中(因为 反斜杠会转义以下内容 引号字符)。另请注意,一个 单个反斜杠后跟换行符 被解释为这两个字符 作为字符串的一部分,而不是作为一行 继续。

        所以原始字符串不是 100% 原始的,还有一些基本的反斜杠处理。

        【讨论】:

        • 哦哇...这很奇怪。不错的收获。 r'\'' == "\\'" 是有道理的,但是转义字符有效果而不消失仍然很奇怪。
        • @ihightower 这可能适用于文件系统路径,但反斜杠还有其他用途。对于文件系统路径,不要硬编码分隔符。使用 'os.path.sep',或更好的 'os.path' 的更高级别的功能。 (或“pathlib”,如果可用)
        • 注意:解决方法是使用相邻的文字连接。 r"foo\bar\baz" "\\"(如果有歧义,用括号括起来)将在编译时创建一个单一的文字,其中的第一部分是原始的,只有最后一点是非原始的,以允许尾部反斜杠。
        • IMO 这只是重申了这个问题(什么是允许/将起作用,什么不是),而没有说明为什么要这样设计。有一个 FAQ entry 可以解释原因(原始字符串是为特定目的而设计的,在该目的的上下文中是有意义的)。
        • 那么原始字符串有什么意义呢?似乎是这个概念的阴暗实现。
        猜你喜欢
        • 2012-06-25
        • 2022-01-25
        • 2020-08-08
        • 1970-01-01
        • 1970-01-01
        • 2015-07-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多