Python 替换和原始字符串中的正则表达式转义运算符 \
Python Regex escape operator \ in substitutions & raw strings
我不明白 python 正则表达式中的 scape 运算符 \ 以及原始字符串 r' 的运行逻辑。
感谢您的帮助。
代码:
import re
text=' esto .es 10 . er - 12 .23 with [ and.Other ] here is more ; puntuation'
print('text0=',text)
text1 = re.sub(r'(\s+)([;:\.\-])', r'', text)
text2 = re.sub(r'\s+\.', '\.', text)
text3 = re.sub(r'\s+\.', r'\.', text)
print('text1=',text1)
print('text2=',text2)
print('text3=',text3)
理论说:
反斜杠字符 ('\') 表示特殊形式或允许使用特殊字符而不调用它们的特殊含义。
根据本题末尾提供的link解释,r'表示原始字符串,即符号没有特殊含义,保持原样。
所以在上面的正则表达式中,我希望 text2 和 text3 不同,因为替换文本是“.”在文本 2 中,即句号,而(原则上)文本 3 中的替换文本是 r'.'这是一个原始字符串,即应该出现的字符串、反斜杠和句点。但它们的结果是一样的:
结果是:
text0= esto .es 10 . er - 12 .23 with [ and.Other ] here is more ; puntuation
text1= esto.es 10. er- 12.23 with [ and.Other ] here is more; puntuation
text2= esto\.es 10\. er - 12\.23 with [ and.Other ] here is more ; puntuation
text3= esto\.es 10\. er - 12\.23 with [ and.Other ] here is more ; puntuation
#text2=text3 but substitutions are not the same r'\.' vs '\.'
在我看来,r' 在替换部分和反斜杠中的工作方式不同。另一方面,我的直觉告诉我这里遗漏了一些东西。
编辑 1:
在@Wiktor Stribiżew 评论之后。
他指出(在他的 link 之后):
import re
print(re.sub(r'(.)(.)(.)(.)(.)(.)', 'ab', '123456'))
print(re.sub(r'(.)(.)(.)(.)(.)(.)', r'ab', '123456'))
# in my example the substitutions were not the same and the result were equal
# here indeed r' changes the results
给出:
ab
a6b
这让我更加困惑。
注意:
我读了 this stack overflow question about raw strings 这是超级完整的。尽管如此,它并没有谈到替代
来自doc(我的重点):
re.sub(pattern, repl, string, count=0, flags=0)
Return the string
obtained by replacing the leftmost non-overlapping occurrences of
pattern in string by the replacement repl. If the pattern isn’t found,
string is returned unchanged. repl can be a string or a function; if
it is a string, any backslash escapes in it are processed. That is, \n
is converted to a single newline character, \r is converted to a
carriage return, and so forth. Unknown escapes of ASCII letters are
reserved for future use and treated as errors. Other unknown escapes
such as \& are left alone. Backreferences, such as , are replaced
with the substring matched by group 6 in the pattern.
repl 参数不仅仅是纯文本。它也可以是函数的名称或引用组中的位置(例如 \g<quote>
、\g<1>
、</code>)。</p>
<p>此外,来自 <a href="https://docs.python.org/3/reference/lexical_analysis.html" rel="nofollow noreferrer">here</a>:</p>
<blockquote>
<p>Unlike Standard C, all unrecognized escape sequences are left in the
string unchanged, i.e., the backslash is left in the result.</p>
</blockquote>
<p>因为<code>.
不是特殊的转义字符,所以'\.'
等同于r'\.\
。
首先,
replacement patterns ≠ regular expression patterns
我们使用 正则表达式模式 来搜索匹配项,我们使用 替换模式 来替换使用正则表达式找到的匹配项。
注意:替换模式中唯一的特殊字符是反斜杠、\
。只有反斜杠必须加倍。
Python
中的替换模式语法
re.sub
docs 令人困惑,因为它们提到了可用于替换模式的字符串转义序列(如 \n
、\r
)和正则表达式转义序列(</code>) 以及可同时用作正则表达式和字符串转义序列的那些 (<code>\&
).
我正在使用术语 regex 转义序列 来表示由文字反斜杠 + 字符组成的转义序列,即 '\X'
或 r'\X'
,和一个 字符串转义序列 来表示 \
的序列和一个字符或一些序列,它们共同构成一个有效的 string escape sequence. They are only recognized in regular string literals。在原始字符串文字中,您只能转义 "
(这就是为什么您不能以 \"
结束原始字符串文字的原因,但反冲仍然是字符串的一部分)。
因此,在替换模式中,您可以使用反向引用:
re.sub(r'\D(\d)\D', r'', 'a1b') # => 1
re.sub(r'\D(\d)\D', '\1', 'a1b') # => 1
re.sub(r'\D(\d)\D', '\g<1>', 'a1b') # => 1
re.sub(r'\D(\d)\D', r'\g<1>', 'a1b') # => 1
您可能会看到 r''
和 '\1'
是相同的替换模式,</code>。如果您使用 <code>''
,它将被解析为 字符串转义序列 ,一个具有八进制值 001
的字符。如果您忘记将 r
前缀与明确的反向引用一起使用,则没有问题,因为 \g
不是有效的字符串转义序列,并且 \
转义字符保留在字符串中。阅读我链接到的文档:
Unlike Standard C, all unrecognized escape sequences are left in the string unchanged, i.e., the backslash is left in the result.
因此,当您将 '\.'
作为替换字符串传递时,您实际上发送了 \.
two-char 组合作为替换字符串,这就是您得到 \.
的原因在结果中。
\
是 Python 替换模式中的特殊字符
如果您使用 re.sub(r'\s+\.', r'\.', text)
,您将得到与 text2
和 text3
情况相同的结果,请参阅 this demo。
发生这种情况是因为 \
,两个文字反斜杠,表示替换模式中的单个反斜杠。如果您的正则表达式模式中没有第 2 组,但在替换中传递 r''
以实际替换为 \
和 2
字符组合,您将收到错误消息。
因此,当您有动态的 user-defined 替换模式时,您需要将替换模式中的所有反斜杠加倍,这些反斜杠将作为文字字符串传递:
re.sub(some_regex, some_replacement.replace('\', '\\'), input_string)
解决所有这些字符串转义问题的一个简单方法是使用 function/lambda 作为 repl
参数,而不是字符串。例如:
output = re.sub(
pattern=find_pattern,
repl=lambda _: replacement,
string=input,
)
根本不会解析替换字符串,只是替换匹配项。
我不明白 python 正则表达式中的 scape 运算符 \ 以及原始字符串 r' 的运行逻辑。 感谢您的帮助。
代码:
import re
text=' esto .es 10 . er - 12 .23 with [ and.Other ] here is more ; puntuation'
print('text0=',text)
text1 = re.sub(r'(\s+)([;:\.\-])', r'', text)
text2 = re.sub(r'\s+\.', '\.', text)
text3 = re.sub(r'\s+\.', r'\.', text)
print('text1=',text1)
print('text2=',text2)
print('text3=',text3)
理论说: 反斜杠字符 ('\') 表示特殊形式或允许使用特殊字符而不调用它们的特殊含义。
根据本题末尾提供的link解释,r'表示原始字符串,即符号没有特殊含义,保持原样。
所以在上面的正则表达式中,我希望 text2 和 text3 不同,因为替换文本是“.”在文本 2 中,即句号,而(原则上)文本 3 中的替换文本是 r'.'这是一个原始字符串,即应该出现的字符串、反斜杠和句点。但它们的结果是一样的:
结果是:
text0= esto .es 10 . er - 12 .23 with [ and.Other ] here is more ; puntuation
text1= esto.es 10. er- 12.23 with [ and.Other ] here is more; puntuation
text2= esto\.es 10\. er - 12\.23 with [ and.Other ] here is more ; puntuation
text3= esto\.es 10\. er - 12\.23 with [ and.Other ] here is more ; puntuation
#text2=text3 but substitutions are not the same r'\.' vs '\.'
在我看来,r' 在替换部分和反斜杠中的工作方式不同。另一方面,我的直觉告诉我这里遗漏了一些东西。
编辑 1: 在@Wiktor Stribiżew 评论之后。 他指出(在他的 link 之后):
import re
print(re.sub(r'(.)(.)(.)(.)(.)(.)', 'ab', '123456'))
print(re.sub(r'(.)(.)(.)(.)(.)(.)', r'ab', '123456'))
# in my example the substitutions were not the same and the result were equal
# here indeed r' changes the results
给出:
ab
a6b
这让我更加困惑。
注意: 我读了 this stack overflow question about raw strings 这是超级完整的。尽管如此,它并没有谈到替代
来自doc(我的重点):
re.sub(pattern, repl, string, count=0, flags=0) Return the string obtained by replacing the leftmost non-overlapping occurrences of pattern in string by the replacement repl. If the pattern isn’t found, string is returned unchanged. repl can be a string or a function; if it is a string, any backslash escapes in it are processed. That is, \n is converted to a single newline character, \r is converted to a carriage return, and so forth. Unknown escapes of ASCII letters are reserved for future use and treated as errors. Other unknown escapes such as \& are left alone. Backreferences, such as , are replaced with the substring matched by group 6 in the pattern.
repl 参数不仅仅是纯文本。它也可以是函数的名称或引用组中的位置(例如 \g<quote>
、\g<1>
、</code>)。</p>
<p>此外,来自 <a href="https://docs.python.org/3/reference/lexical_analysis.html" rel="nofollow noreferrer">here</a>:</p>
<blockquote>
<p>Unlike Standard C, all unrecognized escape sequences are left in the
string unchanged, i.e., the backslash is left in the result.</p>
</blockquote>
<p>因为<code>.
不是特殊的转义字符,所以'\.'
等同于r'\.\
。
首先,
replacement patterns ≠ regular expression patterns
我们使用 正则表达式模式 来搜索匹配项,我们使用 替换模式 来替换使用正则表达式找到的匹配项。
注意:替换模式中唯一的特殊字符是反斜杠、\
。只有反斜杠必须加倍。
Python
中的替换模式语法re.sub
docs 令人困惑,因为它们提到了可用于替换模式的字符串转义序列(如 \n
、\r
)和正则表达式转义序列(</code>) 以及可同时用作正则表达式和字符串转义序列的那些 (<code>\&
).
我正在使用术语 regex 转义序列 来表示由文字反斜杠 + 字符组成的转义序列,即 '\X'
或 r'\X'
,和一个 字符串转义序列 来表示 \
的序列和一个字符或一些序列,它们共同构成一个有效的 string escape sequence. They are only recognized in regular string literals。在原始字符串文字中,您只能转义 "
(这就是为什么您不能以 \"
结束原始字符串文字的原因,但反冲仍然是字符串的一部分)。
因此,在替换模式中,您可以使用反向引用:
re.sub(r'\D(\d)\D', r'', 'a1b') # => 1
re.sub(r'\D(\d)\D', '\1', 'a1b') # => 1
re.sub(r'\D(\d)\D', '\g<1>', 'a1b') # => 1
re.sub(r'\D(\d)\D', r'\g<1>', 'a1b') # => 1
您可能会看到 r''
和 '\1'
是相同的替换模式,</code>。如果您使用 <code>''
,它将被解析为 字符串转义序列 ,一个具有八进制值 001
的字符。如果您忘记将 r
前缀与明确的反向引用一起使用,则没有问题,因为 \g
不是有效的字符串转义序列,并且 \
转义字符保留在字符串中。阅读我链接到的文档:
Unlike Standard C, all unrecognized escape sequences are left in the string unchanged, i.e., the backslash is left in the result.
因此,当您将 '\.'
作为替换字符串传递时,您实际上发送了 \.
two-char 组合作为替换字符串,这就是您得到 \.
的原因在结果中。
\
是 Python 替换模式中的特殊字符
如果您使用 re.sub(r'\s+\.', r'\.', text)
,您将得到与 text2
和 text3
情况相同的结果,请参阅 this demo。
发生这种情况是因为 \
,两个文字反斜杠,表示替换模式中的单个反斜杠。如果您的正则表达式模式中没有第 2 组,但在替换中传递 r''
以实际替换为 \
和 2
字符组合,您将收到错误消息。
因此,当您有动态的 user-defined 替换模式时,您需要将替换模式中的所有反斜杠加倍,这些反斜杠将作为文字字符串传递:
re.sub(some_regex, some_replacement.replace('\', '\\'), input_string)
解决所有这些字符串转义问题的一个简单方法是使用 function/lambda 作为 repl
参数,而不是字符串。例如:
output = re.sub(
pattern=find_pattern,
repl=lambda _: replacement,
string=input,
)
根本不会解析替换字符串,只是替换匹配项。