【问题标题】:What exactly is a "raw string regex" and how can you use it?究竟什么是“原始字符串正则表达式”以及如何使用它?
【发布时间】:2012-10-03 23:27:24
【问题描述】:

来自regex 上的python 文档,关于'\' 字符:

解决方案是使用 Python 的原始字符串表示法 表达模式;反斜杠不以任何特殊方式处理 以'r' 为前缀的字符串文字。所以r"\n"是一个两个字符的字符串 包含'\''n',而"\n" 是一个字符的字符串 包含换行符。通常模式将用 Python 表示 使用此原始字符串表示法的代码。

这个原始字符串表示法是什么?如果您使用原始字符串格式,这是否意味着 "*" 被视为文字字符而不是零个或多个指示符?这显然是不对的,否则正则表达式将完全失去作用。但是如果它是一个原始字符串,如果"\n" 字面上是一个反斜杠和一个"n",它如何识别换行符?

我不关注。

编辑赏金:

我试图了解原始字符串正则表达式如何匹配换行符、制表符和字符集,例如\w 用于单词或\d 用于数字或所有诸如此类的东西,如果原始字符串模式不能将反斜杠识别为普通字符。我真的可以举一些很好的例子。

【问题讨论】:

标签: python regex python-module rawstring


【解决方案1】:

原始字符串不会影响 python 正则表达式中的特殊序列,例如 \w、\d。它只影响转义序列,例如 \n。所以大多数时候我们在前面写r都没有关系。

我认为这是大多数初学者正在寻找的答案。

【讨论】:

    【解决方案2】:

    \n 是 Python 中的转义序列

    \w 是 (Python) 正则表达式中的特殊序列

    他们看起来像是在同一个家庭,但实际上并非如此。原始字符串表示法会影响转义序列,但不会影响正则表达式特殊序列。

    有关转义序列的更多信息 搜索“\换行符” https://docs.python.org/3/reference/lexical_analysis.html

    有关特殊序列的更多信息: 搜索“\号码” https://docs.python.org/3/library/re.html

    【讨论】:

      【解决方案3】:

      Zarkonnen 的回答确实回答了您的问题,但没有直接回答。让我试着更直接一些,看看我能不能从 Zarkonnen 那里抢到赏金。

      如果您停止使用术语“原始字符串正则表达式”和“原始字符串模式”,您可能会发现这更容易理解。这些术语将两个独立的概念混为一谈:Python 源代码中特定字符串的表示形式,以及该字符串表示的正则表达式。

      事实上,将它们视为两种不同的编程语言是有帮助的,每种语言都有自己的语法。 Python 语言有源代码,其中包括构建具有特定内容的字符串,并调用正则表达式系统。正则表达式系统具有驻留在字符串对象中并匹配字符串的源代码。两种语言都使用反斜杠作为转义字符。

      首先,要了解字符串是一个字符序列(即字节或 Unicode 代码点;这里的区别并不重要)。在 Python 源代码中有多种表示字符串的方法。 原始字符串只是这些表示之一。如果两种表示产生相同的字符序列,它们会产生相同的行为。

      想象一个 2 字符的字符串,由 反斜杠 字符后跟 n 字符组成。如果您知道 反斜杠 的字符值为 92,而 n 的字符值为 110,那么这个表达式将生成我们的字符串:

      s = chr(92)+chr(110)
      print len(s), s
      
      2 \n
      

      传统的 Python 字符串表示法 "\n" 不会生成此字符串。相反,它会生成一个带有换行符的单字符字符串。 Python docs 2.4.1. String literals 说,“反斜杠 (\) 字符用于转义具有特殊含义的字符,例如换行符、反斜杠本身或引号字符。”

      s = "\n"
      print len(s), s
      
      1 
       
      

      (请注意,在这个例子中换行符是不可见的,但是如果你仔细看,你会在“1”之后看到一个空行。)

      为了得到我们的两个字符的字符串,我们必须使用另一个反斜杠字符来转义原始反斜杠字符的特殊含义:

      s = "\\n"
      print len(s), s
      
      2 \n
      

      如果您想表示其中包含许多 反斜杠 字符的字符串怎么办? Python docs 2.4.1. String literals 继续,“字符串文字可以选择以字母 'r' 或 'R' 作为前缀;此类字符串称为 原始字符串,并使用不同的规则来解释反斜杠转义序列。”这是我们的两个字符的字符串,使用原始字符串表示:

      s = r"\n"
      print len(s), s
      
      2 \n
      

      所以我们有三种不同的字符串表示,都给出相同的字符串或字符序列:

      print chr(92)+chr(110) == "\\n" == r"\n"
      True
      

      现在,让我们转向正则表达式。 Python docs, 7.2. reRegular expression operations 说,“正则表达式使用反斜杠字符 ('\') 来表示特殊形式或允许使用特殊字符而不调用它们的特殊含义。这与 Python 出于相同目的使用相同字符相冲突字符串字面量..."

      如果你想要一个匹配换行符的 Python 正则表达式对象,那么你需要一个 2 字符的字符串,由 反斜杠 字符后跟 n 字符组成.以下代码行都将 prog 设置为识别换行符的正则表达式对象:

      prog = re.compile(chr(92)+chr(110))
      prog = re.compile("\\n")
      prog = re.compile(r"\n")
      

      那么为什么是"Usually patterns will be expressed in Python code using this raw string notation."?因为正则表达式通常是静态字符串,可以方便地表示为字符串文字。从可用的不同字符串文字符号中,当正则表达式包含 反斜杠 字符时,原始字符串是一个方便的选择。

      问题

      re.compile(r"\s\tWord")这个表达式呢? A:将字符串从正则表达式编译中分离出来,分别理解,更容易理解。

      s = r"\s\tWord"
      prog = re.compile(s)
      

      字符串s包含八个字符:一个反斜杠、一个s、一个反斜杠、一个t,然后是四个字符 Word

      :制表符和空格字符会怎样? A:在 Python 语言级别,字符串 s 没有 制表符空格 字符。它以四个字符开头:反斜杠s反斜杠t。同时,正则表达式系统将该字符串视为正则表达式语言中的源代码,这意味着“匹配一个由空格字符、制表符和四个字符Word组成的字符串。

      :如果这被视为 backlash-s 和 backslash-t,你如何匹配它们? A:如果“you”和“that”这两个词更具体,问题可能会更清楚:正则表达式系统如何匹配表达式 backlash-s 和 backslash-t?作为“任何空白字符”和“制表符字符”。

      :或者如果你有 3 个字符的字符串反斜杠换行符怎么办? A:在 Python 语言中,3 个字符的字符串 backslash-n-newline 可以表示为常规字符串"\\n\n",或原始加常规字符串r"\n" "\n",或其他方式。正则表达式系统在找到任意两个连续的 newline 字符时匹配 3 个字符的字符串 backslash-n-newline。

      注意所有示例和文档引用均指向 Python 2.7。

      更新:合并了@Vladislav Zorov 和@m.buettner 的回答以及@Aerovistae 的后续问题的澄清。

      【讨论】:

      • re.compile(r"\s\tWord") 怎么样?制表符和空格字符会发生什么?如果将其视为 backlash-s 和 backslash-t,您如何匹配它们?或者,如果您有 3 个字符的字符串反斜杠换行符怎么办?然后呢?
      • @Aerovistae 它在编译字符串时被处理反斜杠 s,反斜杠 t。这四个字符被传递给正则表达式引擎,它解析字符串并知道它必须匹配一个空格和一个制表符。如果您使用的是普通(非原始)字符串,\s 可能会在字符串中以s 结尾,而\t 将成为一个制表符。现在只有两个字符被交给正则表达式引擎。虽然引擎可能仍然能够匹配制表符,但它现在会尝试匹配前面的 s
      • ord(92) 只会引发TypeError,因为92 不是字符串。你的意思可能是chr(92)(或者unichr(92))?
      • 谢谢,@abarnert!我测试了代码,发现我输入的是 ord() 而不是 chr()。我想我未能将该更正移回答案。更正了我的答案。
      • 嘿@JimDeLaHunt 我想说一年后我回来读了这篇文章,终于在不同的上下文中理解了这个原始字符串的东西,我现在可以看到你的解释是真的清除。我想当时我只是对它有某种巨大的心理障碍……现在我教一门关于它的课!再次感谢。
      【解决方案4】:

      使用普通字符串编写包含\ 的正则表达式的问题是您最终不得不为每个\ 编写\\。所以字符串文字 "stuff\\things"r"stuff\things" 产生相同的字符串。如果您想编写与反斜杠匹配的正则表达式,这将特别有用。

      使用普通字符串,匹配字符串\ 的正则表达式将是"\\\\"

      为什么?因为我们必须对\ 进行两次转义:一次用于正则表达式语法,一次用于字符串语法。

      您可以使用三引号来包含换行符,如下所示:

      r'''stuff\
      things'''
      

      请注意,通常,python 会将\-newline 视为行继续,但在原始字符串中并非如此。另请注意,反斜杠仍会转义原始字符串中的引号,但会保留在其自身中。所以原始字符串文字r"\"" 产生字符串\"。这意味着您不能以反斜杠结束原始字符串文字。

      更多信息请参见the lexical analysis section of the Python documentation

      【讨论】:

      • 对于每个 \ 都不是真正的 \\。 '\d' 被解释为后跟 d 的反斜杠(?)。
      • @Aerovistae:使用r'''something<enter>onnewline'''<enter> 表示回车。不完全漂亮,所以也许你可以在这里使用字符串连接?
      • 实际上,由于原始字符串的处理方式,r"stuff\" 是一个错误。
      • @IgnacioVazquez-Abrams & nhahtdh 解决这个问题!
      • @Aerovistae re.match(r'1\n2', string) 表示re.match('1\\n2', string),因此\n 不是由python 解释,而是由正则表达式解析器解释 - 它实际上给出与简单的re.match('1\n2', string) 相同的结果,因为正则表达式解析器将处理python 给出的未转义换行符很好(至少在我的 Python 3 测试中)
      【解决方案5】:

      相关的 Python 手册部分(“字符串和字节文字”)对原始字符串文字有明确的解释:

      字符串和字节文字都可以选择前缀 字母“r”或“R”;这样的字符串称为原始字符串并对待 反斜杠作为文字字符。结果,在字符串文字中, 未对原始字符串中的 '\U' 和 '\u' 转义进行特殊处理。给定 Python 2.x 的原始 unicode 文字的行为与 Python 不同 不支持 3.x 的 'ur' 语法。

      3.3 版中的新功能:原始字节文字的“rb”前缀已 添加为“br”的同义词。

      3.3 版中的新功能:支持 unicode 传统文字 (u'value') 重新引入以简化双 Python 2.x 的维护和 3.x 代码库。有关详细信息,请参阅 PEP 414。

      在三引号字符串中,允许使用未转义的换行符和引号 (并且被保留),除了连续三个未转义的引号 终止字符串。 (“引号”是用来打开 字符串,即 ' 或 ".)

      除非存在“r”或“R”前缀,否则字符串中的转义序列 根据与标准使用的规则类似的规则进行解释 C. 识别的转义序列是:

      转义序列含义注释

      \newline 反斜杠和换行符被忽略
      \ 反斜杠 ()
      \' 单引号 (')
      \" 双引号 (")
      \a ASCII 贝尔 (BEL)
      \b ASCII 退格 (BS)
      \f ASCII 换页 (FF)
      \n ASCII 换行 (LF)
      \r ASCII 回车 (CR)
      \t ASCII 水平制表符 (TAB) \v ASCII 垂直制表符 (VT)
      \ooo 八进制字符 ooo (1,3)
      \xhh 十六进制值 hh (2,3) 的字符

      仅在字符串文字中识别的转义序列是:

      转义序列含义注释\N{name} Unicode 数据库 (4) \uxxxx 具有 16 位十六进制值 xxxx 的字符 (5) \Uxxxxxxxx 具有 32 位十六进制值的字符 xxxxxxxx (6)

      注意事项:

      1. 与标准 C 一样,最多接受三个八进制数字。

      2. 与标准 C 不同,需要两个十六进制数字。

      3. 在字节文字中,十六进制和八进制转义表示具有给定值的字节。在字符串文字中,这些转义表示 具有给定值的 Unicode 字符。

      4. 3.3 版更改:添加了对名称别名 [1] 的支持。

      5. 可以使用此转义序列对构成代理对的各个代码单元进行编码。正好四个十六进制数字是 需要。

      6. 任何 Unicode 字符都可以这样编码,但基本多语言平面 (BMP) 之外的字符将使用 如果 Python 被编译为使用 16 位代码单元( 默认)。需要八位十六进制数字。

      与标准 C 不同,所有无法识别的转义序列都留在 字符串不变,即反斜杠留在字符串中。 (这 调试时行为很有用:如果转义序列输入错误, 结果输出更容易被识别为损坏。)它也是 需要注意的是,转义序列只能在字符串中识别 文字属于无法识别的字节转义类别 文字。

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

      【讨论】:

        【解决方案6】:

        这些问题中的大多数都有很多词,也许很难找到您特定问题的答案。

        如果您使用常规字符串并将“\t”之类的模式传递给 RegEx 解析器,Python 会将该文字转换为包含制表符字节 (0x09) 的缓冲区。

        如果你使用一个原始字符串,并且你将 r"\t" 这样的模式传递给 RegEx 解析器,Python 不会做任何解释,它会创建一个包含两个字节的缓冲区:'\' 和 ' t'。 (0x5c, 0x74)。

        RegEx 解析器知道如何处理序列 '\t' - 它会将其与制表符匹配。它还知道如何处理 0x09 字符——它也匹配一个制表符。在大多数情况下,结果将无法区分。

        因此,了解正在发生的事情的关键是认识到这里使用了两个解析器。第一个是 Python 解析器,它将您的字符串文字(或原始字符串文字)转换为字节序列。第二个是 Python 的正则表达式解析器,它将字节序列转换为编译好的正则表达式。

        【讨论】:

          【解决方案7】:

          您似乎很难理解 RegEx 不是 Python 的一部分,而是一种具有自己的解析器和编译器的不同编程语言。原始字符串可帮助您将 RegEx 的“源代码”安全地提供给 RegEx 解析器,然后解析器将为 \d\w\n 等字符序列分配含义...

          问题的存在是因为 Python 和 RegExps 使用 \ 作为转义字符,顺便说一下,这是一个巧合 - 有些语言带有其他转义字符(例如“`n”表示换行符,但即使有在正则表达式中使用“\n”)。优点是您不需要区分这些语言中的原始字符串和非原始字符串,它们不会同时尝试转换文本并销毁它,因为它们对不同的转义序列做出反应。

          【讨论】:

            猜你喜欢
            • 2011-05-03
            • 1970-01-01
            • 2021-11-12
            相关资源
            最近更新 更多