本文全面介绍了 Python 中反斜杆(\)的用法,包括原始字符串和普通字符串,repr()str() ,\ 作为转义符,\ 作为续行符,\ 在字符串转义和正则表达式转义中的过程及注意事项等。阅读本文预计 6 min.

全面了解 Python 中的反斜杆

  • 1. 前言
  • 2. Python 中的 `\`
    • 2.1 `\` 作为转义符
    • 2.2 `\` 作为续行符
    • 2.3 普通字符串 VS 原生字符串
    • 2.4 Python 中 `\` 一些让人头疼的细节
  • 3. 正则表达式字符串中的 `\`
  • 4. 总结
  • 5. 巨人的肩膀

1. 前言

反斜杆 \ 看似非常普通简单,实际上并不简单。以前,自以为掌握了 \ 的用法,近日发现自己并没有真正掌握。即便现在,也不敢说自己真的彻底掌握了 \

意识到这个问题源于自己最近在极客时间上学习 《正则表达式入门课》 专栏中 06 | 转义:正则中转义需要注意哪些问题? 一篇,最后问了一个思考题:

文本部分是反斜杠,n,换行,反斜杠四个部分组成。正则部分分别是 1 到 4 个反斜杠和字母 n。例子虽然看上去简单,不过你能不能解释出这四个示例中的转义过程呢?

>>> import re
>>> re.findall('\n', '\\n\n\\')
['\n']  # 找到了换行符
>>> re.findall('\\n', '\\n\n\\')
['\n']  # 找到了换行符
>>> re.findall('\\\n', '\\n\n\\')
['\n']  # 找到了换行符
>>> re.findall('\\\\n', '\\n\n\\')
['\\n'] # 找到了反斜杠和字母n

我学习这篇文章时,发现 \ 很多行为跟自己预期不一样,所以就是不断地去测试,去翻阅文档,最后也就有了这篇文章。

本文的目的就是总结自己所看的关于 \ 的资料,以及自己测试的过程及个人心得,帮助自己加深对于 \ 的理解。如有不当之处,还请各位读者斧正~

PS:《正则表达式入门课》 专栏讲解的很棒,如果能配合《精通正则表达式》中英文版看的话,更香!

2. Python 中的 \

Python 中 \ 主要有以下作用:

  1. \ 作为转义符。包括表示转义序列、转义转义字符本身,使得转义序列失效。
  2. 作为续行符。

下面我们一一来看。

2.1 \ 作为转义符

在 Python 字符串中,\ 后可以跟各种字符,以表示各种转义序列(escape sequence)。所以通常情况下,Python 解释器会把 \ 当做转义符,把 \ 及其后面的字符当做转义序列处理。如 \n 表示换行符,\t 表示制表符,看下面这个例子:

>>> print('a\nb\tc')
a
b       c

说明:这里 Python 字符串是 a\nb\tc,经过 Python 解释器转义后,\n 会被转义为换行符,\t 会被转义为制表符,所以才有了以上输出。
以上这个例子很好的说明了 Python 解释器是如何把 \ 作为转义字符。

Python 中的转义序列和 C 标准基本一致,有关 Python 转义序列的列表可以参看官方文档 String and Bytes literals。

假如我们就想要字符串中表示 \ 本身,应该怎么做呢?
答案是用 \\,即两个反斜杆,转义转义字符,这样就是表示普通的字符 \ 。测试如下:

>>> print('\\')
\

同理,如果我们想让转义序列失效,只需要在转义序列前面多加一个 \ 即可,如 \n 表示换行符,\\n 表示字符 \ 和 字符 n 两个字符。测试如下:

>>> print('\n')>>> len('\n')
1
>>> print('\\n')
\n
>>> len('\\n')
2

可以看到 \n 是 1 个字符,\\n 是 2 个字符。

至此,我们可以小结一下:

Python 解释器会把字符串中的 \ 当做转义符处理,包括表示转义序列、转义转义字符本身,使得转义序列失效。Python 解释器把 \ 及其后面的字符当做转义序列对待。如果我们想表示 \ 本身,那么需要用 \\。如果我们想让转义序列失效,那么就在原有的转义序列前多加一个 \,如:\\n

PS:转义符设计的目的是为了表示键盘难以键入或者难以表示的信息,如:用 \n 去表示换行。

2.2 \ 作为续行符

在 Python 中 \ 还有一个用法就是当做续行符用。我们知道一行代码太长,不利于阅读。那么如何实现把 1 行代码分割,分散到多行呢?Python 中 \ 可以实现这个功能。

看下面这个小例子:

>>> url = 'https://time.geekbang.org/column/article/\
... 252887?utm_source=geektime&utm_medium=pc&utm_campaign=265\
... &utm_term=pc_interstitial_372'
>>> print(url)
https://time.geekbang.org/column/article/252887?utm_source=geektime&utm_medium=pc&utm_campaign=265&utm_term=pc_interstitial_372

说明:这里 \ 被当做了续行符,用来把多行代码显式拼接成一行代码。通常用于一行代码过长,需要分割到多行的场景。

这里需要注意,\ 作为续行符时,\ 后面不能有任何字符。否则报 SyntaxError: unexpected character after line continuation character
如:

>>> print('hello'\)File "<stdin>", line 1print('hello'\)^
SyntaxError: unexpected character after line continuation character
>>> print('hello'\
... )
hello

2.3 普通字符串 VS 原生字符串

我们通常想写个 \,却要写成 \\,似乎有点麻烦,那有没有什么办法使得 Python 解释器不把 \ 当做转义符呢?

答案就是使用原生字符串(raw string),原生字符串很简单,就是在普通字符串前面加上前缀 rR 即可。如:想打印 \n 两个字符,使用普通字符串需要写成 print('\\n'),而使用原生字符串只需要写成 print(r'\n'),测试如下:

>>> print('\\n')
\n
>>> print(r'\n')
\n
>>> print(R'\n')
\n
>>> len('\\n')
2
>>> len(r'\n')
2

通过上面的例子,可以非常清楚的看到,原生字符串就是使得 \ 转义失效。有点像,加了 rR 前缀就相当于告诉 Python 解释器,这是原生字符串,里面的 \ 不用转义。

有些时候,原生字符串很有用,可以省去我们重复键入 \,如:

>>> print("this\text\is\not\what\it\seems")
this    ext\is
ot\what\it\seems
>>> print("this\\text\is\what\you\\need")
this\text\is\what\you\need
>>> print(r"this\text\is\not\what\it\seems")
this\text\is\not\what\it\seems

看到这里,你是不是觉得,\ 很简单,你列的这些我都会,你这篇文章没啥特别的?别急,继续往下面看。

2.4 Python 中 \ 一些让人头疼的细节

接下来这部分,就是我之前不太清楚或者说没有关注到的部分。

  1. 如果 Python 普通字符串中 \ 后跟的字符不是合法的转义序列,如:\d,Python 会怎么处理?
  2. 为什么我输入的是 '\d',输出的是 '\\d'
  3. Python 普通字符串和原生字符串末尾如果是奇数个 \,会发生什么?
  4. 原生字符串中还可以转义吗?

这部分将围绕这 4 个问题展开。
先看第一个问题,看下面的例子:

>>> s = '\d'
>>> s
'\\d'
>>> print(s)
\d
>>> s = '\\d'
>>> s
'\\d'
>>> print(s)
\d
>>> s = '\\\d'
>>> s
'\\\\d'
>>> print(s)
\\d

为什么 s = '\d' 时, s 得到的结果是 \\d,而 print(s) 得到的结果是 \d?下面的几个又怎么解释?你先自己思考下,看看能想明白每一步如何转义的不?

要解释上面的疑惑,就必须弄明白:

  1. 如果 Python 普通字符串中 \ 后跟的字符是非法的转义序列,如:\d,Python 会怎么处理?
  2. 为什么我输入的是 '\d',输出的是 '\\d'

首先,第 1 个问题,如果 Python 普通字符串中 \ 后跟的字符不是合法的转义序列,如:\d
答案:目前 Python 的做法是把 \ 保留在普通字符串中。 s = '\d 因为 \d 不是转义序列,所以 Python 解释器会把 \d 当做字符 \ 和字符 d 两个字符处理。

这与 C 标准是不一样的,C 语言中,如果 \ 和后面的字符无法构成合法转义序列,则 \ 会被忽略掉。测试如下:

Python 中测试

>>> print('\d')
\d
>>> len('\d')
2
>>> for i in '\d':
...     print(i)
...
\
d

C 语言中 \ 转义测试:

# include <stdio.h>int main(void)
{printf("\d");return 0;
}结果输出:
c_test.c: In function 'main':
c_test.c:5:12: warning: unknown escape sequence: '\d'printf("\d");^~~~
d

注意:Python 官方文档说了,Python 3.6 开始,无法识别的转义序列将产生 DeprecationWarning,最终会变为 SyntaxError,原文如下:

Changed in version 3.6: Unrecognized escape sequences produce a DeprecationWarning. In a future Python version they will be a SyntaxWarning and eventually a SyntaxError.

**所以,如果你想表示 \ 本身,就用 \\,不要用 \d 来表示 \d,因为以后这样做是非法的!

接着我们看第 2 个问题,为什么我输入的是 '\d',输出的是 '\\d'
答案:在 Python 中有两个内置函数 str()repr(),它们分别对应 __str__()__repr__() 魔法方法。简单地说,str() 是对用户友好,repr() 是对编程者或者机器友好。看着有点绕,为了便于理解和说明,我们用原生字符串说明:

>>> r'\d'
'\\d'
>>> r'\\d'
'\\\\d'
>>>

我们可以看到,每个字符 \,在电脑中实际上是 \\,因为 \\ 才表示 \ 本身。这里我们只要明白这一点就可以了。

那么我们如何能够看到,\ 转移后的实际字符串呢?
通过用 print() 函数我们可以看到转移后的实际字符串:

>>> print('\\d')
\d
>>> print('\\\\d')
\\d

这个地方提到这个,只是为了说明一下内容:

>>> s = '\d'
>>> s
'\\d'
>>> print(s)
\d

因为 \d 不是合法的转义序列,故无法转义,所以 s 实际上是 \ 字符和 d 字符组成的字符串。所以输入 s 才得到了 \\d 这个对机器友好的输出,实际上它转义后就是代表 \d。而 print() 的结果是转义后的实际字符串,所以输出是 \d 2 个字符。

>>> s = '\\d'
>>> s
'\\d'
>>> print(s)
\d

这个输出的解释是: s = '\\d',首先 \\d 会被 Python 解释器转义为 \d 并赋值给变量 s,所以 s 实际上也是 \ 字符和 d 字符组成的字符串,剩下的同上解释。

>>> s = '\\\d'
>>> s
'\\\\d'
>>> print(s)
\\d

这个输出的解释是:s = '\\\d',首先 \\\d 会被 Python 解释器转义为 \\d 并赋值给变量 s,实际上是 \ 字符、\ 字符和 d 字符这 3 个字符组成的字符串。对机器友好时,每个 \ 会作为 \\ 输出,所以得到了 \\\\d,print(s)对用户友好,得到的就是 \\d

更多关于 __str__()__repr__() 的区别,可以参考这里:Difference between __str__ and __repr__?

写的有点啰嗦,可能也有点难懂,多看几遍,自己多练习练习,可以帮助我们更好的理解。

接下来我们解决剩下的 2 个问题。

  1. Python 普通字符串和原生字符串末尾如果是奇数个 \,会发生什么?
  2. 原生字符串中还可以转义吗?

针对问题 3,如果 Python 普通字符串和原生字符串末尾如果是奇数个 \,会发生什么?
答案: Python 解释器会报 EOL 语法错误。如下:

>>> print('hello\')File "<stdin>", line 1print('hello\')^
SyntaxError: EOL while scanning string literal
>>> print(r'hello\')File "<stdin>", line 1print(r'hello\')^
SyntaxError: EOL while scanning string literal

原因很简单,因为 Python 中字符串是以引号开始,引号结束的,奇数个 \ 结尾时,\ 会把后面的引号转义,使得引号不再是字符串结束的标识。字符缺乏了闭合引号,所以报错:EOL
我们可以这样测试一下:

>>> print('hello\'')
hello'
>>> print(r'hello\'')
hello\'

因此不管是普通字符串还是原生字符串都不能以奇数个 \ 结尾,否则会报 EOL 错误。

针对问题 4:原生字符串中还可以转义吗?
答案:可以“转义”,但是此时的“转义”有些特别,因为“转义”后 \ 还会保留在字符串中。其实我们上面已经用到了,原生字符串中,我们可以用 \ 转义引号,\等,但是 \ 依旧会保留在字符串中,如:

>>> print(r'\\')
\\
>>> len(r'\\')
2
>>> len(r'a\'b')
4
>>> print(r'a\'b')
a\'b

以上就是我总结的 Python 解释器对于普通字符串和原生字符中 \ 的处理。

这里小结一下:

  1. \ 作为转义符。包括表示转义序列、转义转义字符本身,使得转义序列失效。
  2. 作为续行符。
  3. 目前 Python 中对于无法识别的转义序列,会保留 \ 在字符串中,C 语言中则是会忽略 \。从 Python 3.6 开始,未来,Python 中无法识别的转义序列将报语法错误。因此要表示 \ 本身,请使用 \\
  4. 不管是普通字符串还是原生字符串都不能以奇数个 \ 结尾,否则会报 EOL 错误。
  5. 原生字符串中也可以“转义”,但是此时的“转义”有些特别,因为“转义”后 \ 还会保留在字符串中。

有了前面这么多的铺垫,下面我们可以进入到正则表达式中的转义了。

3. 正则表达式字符串中的 \

正则表达式字符串中的 \ 和 Python 字符串中的 \ 转义类似。他们的区别在于,正则表达式字符串的转义有 2 个水平,第 1 次是 Python 解释器层面的转义,第 2 次是 re 模块正则引擎的转义。

因此正则中要匹配 \ 本身,正则表达式字符串需要写成 '\\\\',如:

>>> re.findall('\\\\', 'a\\b')
['\\']  # 这里的输出结果是对机器友好,下面测试可知
>>> result = re.findall('\\\\', 'a\\b')
>>> print(result[0])
\

上面正则表达式字符串是 '\\\\',第 1 轮是 Python 解释器转义,转义后结果是 \\,再经过 re 正则引擎的转义,转移后结果是 \,所以正则表达式字符串 '\\\\' 最终匹配的是 \ 本身。

是不是觉得这样非常麻烦,而且容易出错,因为有 2 次转义。因此,在写正则表达式字符串时,使用原生字符串是非常推荐的写法!因为这样可以减少 Python 解释器转义这一步,只需要 re 模块正则引擎转义,即可。比如,我们想匹配 \ 本身,那么我们用原生正则表达式字符串就可以写成 r'\\'。简洁多了,测试一下:

>>> re.findall(r'\\', 'a\\b')
['\\']

完美!

使用原生字符串来写正则表达式还有一个好处:因为 Python 字符串转义和正则转义的转义序列是有区别的,比如:\d 在 Python 字符串中是表示 \d 两个字符,而不是转义序列,未来这么写会报错;但是 \d 在正则转移中,是一个转义序列,表示 0-9 任意一个数字。

因此,强烈建议使用原生字符串来写正则,不仅简洁,而且不存在风险!

文章到这里,基本明晰了。最后我们来解决开篇的那个问题吧:

文本部分是反斜杠,n,换行,反斜杠四个部分组成。正则部分分别是 1 到 4 个反斜杠和字母 n。例子虽然看上去简单,不过你能不能解释出这四个示例中的转义过程呢?

>>> import re
>>> re.findall('\n', '\\n\n\\')
['\n']  # 找到了换行符
>>> re.findall('\\n', '\\n\n\\')
['\n']  # 找到了换行符
>>> re.findall('\\\n', '\\n\n\\')
['\n']  # 找到了换行符
>>> re.findall('\\\\n', '\\n\n\\')
['\\n'] # 找到了反斜杠和字母n

首先再次声明,强烈推荐正则表达式用原生字符串写。这里这么写只是为了帮助理解 \ 转义符,以及 \ 在正则表达式字符串中的 2 次转义。这里我列了一个表,来展示上面的转义过程:

初始正则表达式字符串 Python 解释器转义后结果 re 模块正则引擎转义后结果(即匹配的文本)
'\n' 换行符 换行符
'\\n' '\n' 换行符
'\\\n' '\换行符' 换行符
'\\\\n' '\\n' \n\ 字符或 d 字符

里面第 3 个正则一开始我还不太明白,'\换行符' 经过正则引擎转义变成了 换行符,后面发现这个,就把它这样记住就好了,一般不会这么写的!把它和以下对比:

>>> re.findall('\\','\\')
Traceback (most recent call last):File "<stdin>", line 1, in <module>省略...
re.error: bad escape (end of pattern) at position 0

在上面这个例子中,第 1 步 Python 解释器转义肯定没有问题,正则表达式字符串经过第 1 步 Python 解释器转义的结果是, '\'

问题出在第 2 步,re 模块正则引擎转义报错。因为这里经过第 1 步转义后,只剩下一个 '\',正则引擎会尝试把它当做转义符,但是发现后面少了字符,是一个非常的转义序列,所以报错 bad escape

我们多测试几个看看:

>>> re.findall(r'\h','\\h')
Traceback (most recent call last):File "<stdin>", line 1, in <module>省略...raise source.error("bad escape %s" % escape, len(escape))
re.error: bad escape \h at position 0
>>>

这里我用了原生字符串写法,省去了 Python 解释器转义一步,发现,最后报错是 bad escape \h at position 0,也就是说, \h 是非法的转义序列。

因此我们得出结果:在正则引擎转义中,如果 \ 后面跟着非法的转义序列,将报错 bad escape

所以综上,我们写正则表达式时,强烈建议用 r 前缀,能够避免很多不必要的麻烦和难以预测的 Bug。

4. 总结

  1. \ 作为转义符。包括表示转义序列、转义转义字符本身,使得转义序列失效。
  2. \ 作为续行符。
  3. 目前 Python 中对于非法的转义序列,会保留 \ 在字符串中,C 语言中则是会忽略 \。从 Python 3.6 开始,未来,Python 中无法识别的转义序列将报语法错误。因此要表示 \ 本身,请使用 \\
  4. 不管是普通字符串还是原生字符串都不能以奇数个 \ 结尾,否则会报 EOL 错误。
  5. 原生字符串中也可以“转义”,但是此时的“转义”有些特别,因为“转义”后 \ 还会保留在字符串中。
  6. 正则表达式字符串的转义有 2 个水平,第 1 次是 Python 解释器层面的转义,第 2 次是 re 模块正则引擎的转义。
  7. 强烈建议用 r 前缀写正则表达式,省去 Python 解释器的转义。
  8. re 模块正则引擎对于非法的转义序列直接报错 bad escape

知识无穷无尽,点滴总结,聚沙成塔。以上就是分享的全部内容,如果不对之处,恳请斧正~

本文首发于本人公众号 No Bug编程笔记,如有转载请注明出处和作者,谢谢~

5. 巨人的肩膀

知识的学习建立在前人的基础之上,本文的学习总结,来源于以下资料:

  1. re — Regular expression operations
  2. The Backslash Plague
  3. Confused about backslashes in regular expressions
  4. Why do backslashes appear twice?
  5. Can’t escape the backslash with regex?
  6. String and Bytes literals
  7. Difference between __str__ and __repr__?

后记:
我从本硕药学零基础转行计算机,自学路上,走过很多弯路,也庆幸自己喜欢记笔记,把知识点进行总结,帮助自己成功实现转行。
2020下半年进入职场,深感自己的不足,所以2021年给自己定了个计划,每日学一技,日积月累,厚积薄发。
如果你想和我一起交流学习,欢迎大家关注我的微信公众号每日学一技,扫描下方二维码或者搜索每日学一技关注。
这个公众号主要是分享和记录自己每日的技术学习,不定期整理子类分享,主要涉及 C – > Python – > Java,计算机基础知识,机器学习,职场技能等,简单说就是一句话,成长的见证!

全面了解 Python 中的反斜杆相关推荐

  1. python中斜杠加引号什么意思_如何在Python中转义反斜杠和单引号或双引号?

    你是怎么做到的 如果你的"长字符串"是从文件中读取的(正如你在评论中提到的),那么你的问题是误导性的.因为你显然不完全理解逃逸的工作原理,所以你写下的问题可能与你真正的问题不同. ...

  2. Python中取反的理解

    转载大牛的链接,理解原码.补码之间的关系 https://blog.csdn.net/luolaifa000/article/details/83010807 首先,理解python中,计算机只存储整 ...

  3. Python中关于反斜杠(\)用法的总结

    首先,我们需要知道的是,在python中/所代表的是正斜杠,而\代表的是反斜杠.关于反斜杠的使用,我认为转义二字就是其用法的本质.转义,我的理解就是通过对反斜杠的使用,使字符.格式等与其原来意义不同, ...

  4. python中如何反解函数_PyTorch中反卷积的用法详解

    pytorch中的 2D 卷积层 和 2D 反卷积层 函数分别如下: class torch.nn.Conv2d(in_channels, out_channels, kernel_size, str ...

  5. python反爬虫破解_python中绕过反爬虫的方法总结

    我们在登山的途中,有不同的路线可以到达终点.因为选择的路线不同,上山的难度也有区别.就像最近几天教大家获取数据的时候,断断续续的讲过header.地址ip等一些的方法.具体的爬取方法相信大家已经掌握住 ...

  6. python反爬虫与绕过_python中绕过反爬虫的方法总结

    我们在登山的途中,有不同的路线可以到达终点.因为选择的路线不同,上山的难度也有区别.就像最近几天教大家获取数据的时候,断断续续的讲过header.地址ip等一些的方法.具体的爬取方法相信大家已经掌握住 ...

  7. 【Pytorch】取反操作符~在Pytorch和python中使用的比较

    python中的取反操作 在python中取反操作符~是对数字的二进制位进行取反,然后返回取反结果对应的十进制数字.这个可以参考:Python3运算符 使用案例如下: num = 60 # 查看二进制 ...

  8. python中转义是什么意思_python中的转义

    当需要在字符串中使用特殊字符时,我们需要用到python中的反斜杠()转义字符.下面介绍几个餐饮的转义符: \:反斜杠符号 print('2.Amy got up late, and she said ...

  9. python中的特殊用法

    一.python中的Ellipsis对象 提起Ellipsis对象,很多刚入门或者入门不久的同学肯定会两眼发蒙,还有这个东西?平时心细的同学或许在源码中看到过,但不一定知道他的python学名. El ...

最新文章

  1. 《BI那点儿事》数据流转换——排序
  2. BRIEF 特征描述子
  3. hdu1394线段树点修改,区间求和
  4. 创智播客微服务_【传智播客】JavaEE在职加薪课
  5. boost::qvm::deduce_vec相关的测试程序
  6. What are TCHAR, WCHAR, LPSTR, LPWSTR, LPCTSTR (etc.)?
  7. oracle数据集成产品,甲骨文推出Oracle数据集成产品
  8. OS开发之Objective-C与JavaScript的交互
  9. 软件测试:功能测试(1)----测试范围和测试策略
  10. python小波图像去噪_小波去噪
  11. JavaScript打开窗口
  12. 2023年天津农学院专升本停招专业的备考建议?
  13. i5 12400f参数 i512400f评测
  14. 【nowcoder 110246】Dima and Salad
  15. 虚幻引擎(UE4) UMG 创建菜单
  16. 提高篇 第四部分 数据结构 第1章 树状树组
  17. php画梯形,利用css来画出各种样式不同的梯形,html中梯形外框怎么做
  18. [转]常用电平标准(TTL、CMOS、LVTTL、LVCMOS、ECL、PECL、LVPECL、RS232)
  19. 巨噬细胞膜包覆的负载二氧化锰MnO2和顺铂Pt的仿生纳米粒(齐岳)
  20. 如何计算字符串的字节长度

热门文章

  1. Windows Enabler
  2. java 访问百度_使用Java调用百度搜索
  3. 学习电脑知识的一些网站
  4. 蒙哥马利基2的Python算法实现(大数模乘)
  5. cpu架构----通俗理解
  6. TI CC1310 sub1G的SDK开发之唯一识别号MAC地址读取
  7. vue-super-flow流程图展示(简易版)
  8. 接口分析--今日头条天气数据接口
  9. linux 对比文件awk,Linux之awk工具、printf如何格式化输出?diff如何进行文件对比?...
  10. 扫描型PDF转成可搜索可复制的文字型PDF,使用PDF24 OCR 程序报“下载需要的文件时出现一个错误”