全面了解 Python 中的反斜杆
本文全面介绍了 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 中 \
主要有以下作用:
\
作为转义符。包括表示转义序列、转义转义字符本身,使得转义序列失效。- 作为续行符。
下面我们一一来看。
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),原生字符串很简单,就是在普通字符串前面加上前缀 r
或 R
即可。如:想打印 \
和 n
两个字符,使用普通字符串需要写成 print('\\n')
,而使用原生字符串只需要写成 print(r'\n')
,测试如下:
>>> print('\\n')
\n
>>> print(r'\n')
\n
>>> print(R'\n')
\n
>>> len('\\n')
2
>>> len(r'\n')
2
通过上面的例子,可以非常清楚的看到,原生字符串就是使得 \
转义失效。有点像,加了 r
或 R
前缀就相当于告诉 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 中 \
一些让人头疼的细节
接下来这部分,就是我之前不太清楚或者说没有关注到的部分。
- 如果 Python 普通字符串中
\
后跟的字符不是合法的转义序列,如:\d
,Python 会怎么处理? - 为什么我输入的是
'\d'
,输出的是'\\d'
? - Python 普通字符串和原生字符串末尾如果是奇数个
\
,会发生什么? - 原生字符串中还可以转义吗?
这部分将围绕这 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
?下面的几个又怎么解释?你先自己思考下,看看能想明白每一步如何转义的不?
要解释上面的疑惑,就必须弄明白:
- 如果 Python 普通字符串中
\
后跟的字符是非法的转义序列,如:\d
,Python 会怎么处理? - 为什么我输入的是
'\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 个问题。
- Python 普通字符串和原生字符串末尾如果是奇数个
\
,会发生什么? - 原生字符串中还可以转义吗?
针对问题 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 解释器对于普通字符串和原生字符中 \
的处理。
这里小结一下:
\
作为转义符。包括表示转义序列、转义转义字符本身,使得转义序列失效。- 作为续行符。
- 目前 Python 中对于无法识别的转义序列,会保留
\
在字符串中,C 语言中则是会忽略\
。从 Python 3.6 开始,未来,Python 中无法识别的转义序列将报语法错误。因此要表示\
本身,请使用\\
。 - 不管是普通字符串还是原生字符串都不能以奇数个
\
结尾,否则会报EOL
错误。 - 原生字符串中也可以“转义”,但是此时的“转义”有些特别,因为“转义”后
\
还会保留在字符串中。
有了前面这么多的铺垫,下面我们可以进入到正则表达式中的转义了。
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. 总结
\
作为转义符。包括表示转义序列、转义转义字符本身,使得转义序列失效。\
作为续行符。- 目前 Python 中对于非法的转义序列,会保留
\
在字符串中,C 语言中则是会忽略\
。从 Python 3.6 开始,未来,Python 中无法识别的转义序列将报语法错误。因此要表示\
本身,请使用\\
。 - 不管是普通字符串还是原生字符串都不能以奇数个
\
结尾,否则会报EOL
错误。 - 原生字符串中也可以“转义”,但是此时的“转义”有些特别,因为“转义”后
\
还会保留在字符串中。 - 正则表达式字符串的转义有 2 个水平,第 1 次是 Python 解释器层面的转义,第 2 次是
re
模块正则引擎的转义。 - 强烈建议用
r
前缀写正则表达式,省去 Python 解释器的转义。 re
模块正则引擎对于非法的转义序列直接报错bad escape
。
知识无穷无尽,点滴总结,聚沙成塔。以上就是分享的全部内容,如果不对之处,恳请斧正~
本文首发于本人公众号 No Bug编程笔记
,如有转载请注明出处和作者,谢谢~
5. 巨人的肩膀
知识的学习建立在前人的基础之上,本文的学习总结,来源于以下资料:
- re — Regular expression operations
- The Backslash Plague
- Confused about backslashes in regular expressions
- Why do backslashes appear twice?
- Can’t escape the backslash with regex?
- String and Bytes literals
- Difference between
__str__
and__repr__
?
后记:
我从本硕药学零基础转行计算机,自学路上,走过很多弯路,也庆幸自己喜欢记笔记,把知识点进行总结,帮助自己成功实现转行。
2020下半年进入职场,深感自己的不足,所以2021年给自己定了个计划,每日学一技,日积月累,厚积薄发。
如果你想和我一起交流学习,欢迎大家关注我的微信公众号每日学一技
,扫描下方二维码或者搜索每日学一技
关注。
这个公众号主要是分享和记录自己每日的技术学习,不定期整理子类分享,主要涉及 C – > Python – > Java,计算机基础知识,机器学习,职场技能等,简单说就是一句话,成长的见证!
全面了解 Python 中的反斜杆相关推荐
- python中斜杠加引号什么意思_如何在Python中转义反斜杠和单引号或双引号?
你是怎么做到的 如果你的"长字符串"是从文件中读取的(正如你在评论中提到的),那么你的问题是误导性的.因为你显然不完全理解逃逸的工作原理,所以你写下的问题可能与你真正的问题不同. ...
- Python中取反的理解
转载大牛的链接,理解原码.补码之间的关系 https://blog.csdn.net/luolaifa000/article/details/83010807 首先,理解python中,计算机只存储整 ...
- Python中关于反斜杠(\)用法的总结
首先,我们需要知道的是,在python中/所代表的是正斜杠,而\代表的是反斜杠.关于反斜杠的使用,我认为转义二字就是其用法的本质.转义,我的理解就是通过对反斜杠的使用,使字符.格式等与其原来意义不同, ...
- python中如何反解函数_PyTorch中反卷积的用法详解
pytorch中的 2D 卷积层 和 2D 反卷积层 函数分别如下: class torch.nn.Conv2d(in_channels, out_channels, kernel_size, str ...
- python反爬虫破解_python中绕过反爬虫的方法总结
我们在登山的途中,有不同的路线可以到达终点.因为选择的路线不同,上山的难度也有区别.就像最近几天教大家获取数据的时候,断断续续的讲过header.地址ip等一些的方法.具体的爬取方法相信大家已经掌握住 ...
- python反爬虫与绕过_python中绕过反爬虫的方法总结
我们在登山的途中,有不同的路线可以到达终点.因为选择的路线不同,上山的难度也有区别.就像最近几天教大家获取数据的时候,断断续续的讲过header.地址ip等一些的方法.具体的爬取方法相信大家已经掌握住 ...
- 【Pytorch】取反操作符~在Pytorch和python中使用的比较
python中的取反操作 在python中取反操作符~是对数字的二进制位进行取反,然后返回取反结果对应的十进制数字.这个可以参考:Python3运算符 使用案例如下: num = 60 # 查看二进制 ...
- python中转义是什么意思_python中的转义
当需要在字符串中使用特殊字符时,我们需要用到python中的反斜杠()转义字符.下面介绍几个餐饮的转义符: \:反斜杠符号 print('2.Amy got up late, and she said ...
- python中的特殊用法
一.python中的Ellipsis对象 提起Ellipsis对象,很多刚入门或者入门不久的同学肯定会两眼发蒙,还有这个东西?平时心细的同学或许在源码中看到过,但不一定知道他的python学名. El ...
最新文章
- 《BI那点儿事》数据流转换——排序
- BRIEF 特征描述子
- hdu1394线段树点修改,区间求和
- 创智播客微服务_【传智播客】JavaEE在职加薪课
- boost::qvm::deduce_vec相关的测试程序
- What are TCHAR, WCHAR, LPSTR, LPWSTR, LPCTSTR (etc.)?
- oracle数据集成产品,甲骨文推出Oracle数据集成产品
- OS开发之Objective-C与JavaScript的交互
- 软件测试:功能测试(1)----测试范围和测试策略
- python小波图像去噪_小波去噪
- JavaScript打开窗口
- 2023年天津农学院专升本停招专业的备考建议?
- i5 12400f参数 i512400f评测
- 【nowcoder 110246】Dima and Salad
- 虚幻引擎(UE4) UMG 创建菜单
- 提高篇 第四部分 数据结构 第1章 树状树组
- php画梯形,利用css来画出各种样式不同的梯形,html中梯形外框怎么做
- [转]常用电平标准(TTL、CMOS、LVTTL、LVCMOS、ECL、PECL、LVPECL、RS232)
- 巨噬细胞膜包覆的负载二氧化锰MnO2和顺铂Pt的仿生纳米粒(齐岳)
- 如何计算字符串的字节长度
热门文章
- Windows Enabler
- java 访问百度_使用Java调用百度搜索
- 学习电脑知识的一些网站
- 蒙哥马利基2的Python算法实现(大数模乘)
- cpu架构----通俗理解
- TI CC1310 sub1G的SDK开发之唯一识别号MAC地址读取
- vue-super-flow流程图展示(简易版)
- 接口分析--今日头条天气数据接口
- linux 对比文件awk,Linux之awk工具、printf如何格式化输出?diff如何进行文件对比?...
- 扫描型PDF转成可搜索可复制的文字型PDF,使用PDF24 OCR 程序报“下载需要的文件时出现一个错误”