06 转义:正则中转义需要注意哪些问题?

06 转义:正则中转义需要注意哪些问题?

06 转义:正则中转义需要注意哪些问题?

你好,我是伟忠。今天我来和你聊聊转义。转义对我们来说都不算陌生,编程的时候,使用到字符串时,双引号里面如果再出现双引号,我们就可以通过转义来解决。就像下面这样:

str = "How do you spell the word \"regex\"?"

虽然转义在日常工作中比较常见,但正则中什么时候需要转义,什么时候不用转义,在真正使用的时候可能会遇到这些麻烦。所以我们很有必要来系统了解一下正则中的转义。

转义字符

首先我们说一下什么是转义字符(Escape Character)。它在维基百科中是这么解释的:

在计算机科学与远程通信中,当转义字符放在字符序列中,它将对它后续的几个字符进行替代并解释。通常,判定某字符是否为转义字符由上下文确定。转义字符即标志着转义序列开始的那个字符。

这么说可能有点不好理解,我再来给你通俗地解释一下。转义序列通常有两种功能。第一种功能是编码无法用字母表直接表示的特殊数据。第二种功能是用于表示无法直接键盘录入的字符(如回车符)。

我们这节课说的就是第二种情况,转义字符自身和后面的字符看成一个整体,用来表示某种含义。最常见的例子是,C语言中用反斜线字符“\”作为转义字符,来表示那些不可打印的ASCII控制符。另外,在URI协议中,请求串中的一些符号有特殊含义,也需要转义,转义字符用的是百分号“%”。之所以把这个字符称为转义字符,是因为它后面的字符,不是原来的意思了。

在日常工作中经常会遇到转义字符,比如我们在shell中删除文件,如果文件名中有*号,我们就需要转义,此时我们能看出,使用了转义字符后,*号就能放进文件名里了。

rm access_log* # 删除当前目录下 access_log 开头的文件

rm access_log\* # 删除当前目录下名字叫 access_log* 的文件

再比如我们在双引号中又出现了双引号,这时候就需要转义了,转义之后才能正常表示双引号,否则会报语法错误。比如下面的示例,引号中的 Hello World! 也是含有引号的。

print "tom said \"Hello World!\" to the crowd."

下面是一些常见的转义字符以及它们的含义。

字符串转义和正则转义

说完了转义字符,我们再来看一下正则中的转义。正则中也是使用反斜杠进行转义的。

一般来说,正则中 \d 代表的是单个数字,但如果我们想表示成 反斜杠和字母d,这时候就需要进行转义,写成 \\d,这个就表示反斜杠后面紧跟着一个字母d。

刚刚的反斜杠和d是连续出现的两个字符,如果你想表示成反斜杠或d,可以用管道符号或中括号来实现,比如 \|d 或 [\d]。

需要注意的是,如果你想用代码来测试这个,在程序中表示普通字符串的时候,我们如果要表示反斜杠,通常需要写成两个反斜杠,因为只写一个会被理解成“转义符号”,而不是反斜杠本身。

下面我给出使用 Python3 来测试的情况,你可以看一下。

>>> import re

>>> re.findall('\\|d', 'a*b+c?\d123d\') # 字符串没转义"反斜杠"

File "", line 1

re.findall('\\|d', 'a*b+c?\d123d\')

^

SyntaxError: EOL while scanning string literal

>>> re.findall('\\|d', 'a*b+c?\\d123d\\')

[]

看到这里,你内心是不是有很多问号?为什么转义了还不行呢?我们来把正则表达式部分精简一下,看看两个反斜杠在正则中是什么意思。

>>> import re

>>> re.findall('\\', 'a*b+c?\\d123d\\')

Traceback (most recent call last):

省去部分信息

re.error: bad escape (end of pattern) at position 0

我们发现,正则部分写的两个反斜杠,Python3 处理的时候会报错,认为是转义字符,即认为是单个反斜杠,如果你再进一步测试在正则中写单个反斜杠,你会发现直接报语法错误,你可以自行尝试。

那如何在正则中正确表示“反斜杠”呢?答案是写四个反斜杠。

>>> import re

>>> re.findall('\\\\', 'a*b+c?\\d123d\\')

['\\', '\\']

你可以想一下,为什么不是三个呢?后面的文本部分,也得要用四个反斜杠表示才是正确的么?到这里,你是不是发现,转义其实没那么简单。

我来给你详细解释一下这里面的过程,在程序使用过程中,从输入的字符串到正则表达式,其实有两步转换过程,分别是字符串转义和正则转义。

在正则中正确表示“反斜杠”具体的过程是这样子:我们输入的字符串,四个反斜杠 \\,经过第一步字符串转义,它代表的含义是两个反斜杠 \;这两个反斜杠再经过第二步正则转义,它就可以代表单个反斜杠 \了。

你可以用这个过程,推导一下两个和三个反斜杠的转换过程,这样你就会明白上面报错的原因了。

那在真正使用的时候,有没有更简单的方法呢?答案是有的,我们尽量使用原生字符串,在 Python 中,可以在正则前面加上小写字母 r 来表示。

>>> import re

>>> re.findall(r'\\', 'a*b+c?\\d123d\\')

['\\', '\\']

这样看起来就简单很多,因为少了上面说的第一次转换。

正则中元字符的转义

在前面的内容中,我们讲了很多元字符,相信你一定都还记得。如果现在我们要查找比如星号(*)、加号(+)、问号(?)本身,而不是元字符的功能,这时候就需要对其进行转义,直接在前面加上反斜杠就可以了。这个转义就比较简单了,下面是一个示例。

>>> import re

>>> re.findall('\+', '+')

['+']

括号的转义

在正则中方括号 [] 和 花括号 {} 只需转义开括号,但圆括号 () 两个都要转义。我在下面给了你一个比较详细的例子。

>>> import re

>>> re.findall('\(\)\[]\{}', '()[]{}')

['()[]{}']

>>> re.findall('\(\)\[\]\{\}', '()[]{}') # 方括号和花括号都转义也可以

['()[]{}']

在正则中,圆括号通常用于分组,或者将某个部分看成一个整体,如果只转义开括号或闭括号,正则会认为少了另外一半,所以会报错。

括号的转义示例,你可以参考这里:https://regex101.com/r/kJfvd6/1。

使用函数消除元字符特殊含义

我们也可以使用编程语言自带的转义函数来实现转义。下面我给出了一个在 Python里转义的例子,你可以看一下。

>>> import re

>>> re.escape('\d') # 反斜杠和字母d转义

'\\\\d'

>>> re.findall(re.escape('\d'), '\d')

['\\d']

>>> re.escape('[+]') # 中括号和加号

'\\[\\+\\]'

>>> re.findall(re.escape('[+]'), '[+]')

['[+]']

这个转义函数可以将整个文本转义,一般用于转义用户输入的内容,即把这些内容看成普通字符串去匹配,但你还是得好好注意一下,如果使用普通字符串查找能满足要求,就不要使用正则,因为它简单不容易出问题。下面是一些其他编程语言对应的转义函数,供你参考。

字符组中的转义

讲完了元字符的转义,我们现在来看看字符组中的转义。书写正则的时候,在字符组中,如果有过多的转义会导致代码可读性差。在字符组里只有三种情况需要转义,下面我来给你讲讲具体是哪三种情况。

字符组中需要转义的有三种情况

脱字符在中括号中,且在第一个位置需要转义:

>>> import re

>>> re.findall(r'[^ab]', '^ab') # 转义前代表"非"

['^']

>>> re.findall(r'[\^ab]', '^ab') # 转义后代表普通字符

['^', 'a', 'b']

中划线在中括号中,且不在首尾位置:

>>> import re

>>> re.findall(r'[a-c]', 'abc-') # 中划线在中间,代表"范围"

['a', 'b', 'c']

>>> re.findall(r'[a\-c]', 'abc-') # 中划线在中间,转义后的

['a', 'c', '-']

>>> re.findall(r'[-ac]', 'abc-') # 在开头,不需要转义

['a', 'c', '-']

>>> re.findall(r'[ac-]', 'abc-') # 在结尾,不需要转义

['a', 'c', '-']

右括号在中括号中,且不在首位:

>>> import re

>>> re.findall(r'[]ab]', ']ab') # 右括号不转义,在首位

[']', 'a', 'b']

>>> re.findall(r'[a]b]', ']ab') # 右括号不转义,不在首位

[] # 匹配不上,因为含义是 a后面跟上b]

>>> re.findall(r'[a\]b]', ']ab') # 转义后代表普通字符

[']', 'a', 'b']

字符组中其它的元字符

一般来说如果我们要想将元字符(.*+?()之类)表示成它字面上本来的意思,是需要对其进行转义的,但如果它们出现在字符组中括号里,可以不转义。这种情况,一般都是单个长度的元字符,比如点号(.)、星号(*)、加号(+)、问号(?)、左右圆括号等。它们都不再具有特殊含义,而是代表字符本身。但如果在中括号中出现 \d 或 \w 等符号时,他们还是元字符本身的含义。

>>> import re

>>> re.findall(r'[.*+?()]', '[.*+?()]') # 单个长度的元字符

['.', '*', '+', '?', '(', ')']

>>> re.findall(r'[\d]', 'd12\\') # \w,\d等在中括号中还是元字符的功能

['1', '2'] # 匹配上了数字,而不是反斜杠\和字母d

下面我来给你简单总结一下字符组中的转义情况,我们提到了三种必须转义的情况,其它情况不转义也能正常工作,但在实际操作过程中,如果遇到在中括号中使用这三个字符原本的意思,你可以都进行转义,剩下其它的元字符都不需要转义。

总结

好了,今天的内容讲完了,我来带你总结回顾一下。

正则中转义有些情况下会比较复杂,从录入的字符串文本,到最终的正则表达式,经过了字符串转义和正则转义两个步骤。元字符的转义一般在前面加反斜杠就行,方括号和花括号的转义一般转义开括号就可以,但圆括号两个都需要转义,我们可以借助编程语言中的转义函数来实现转义。另外我们也讲了字符组中三种需要转义的情况,详细的可以参考下面的脑图。

思考题

通过今天的学习,不知道你对转义掌握的怎么样了呢?再来一个例子加深一下你的理解吧,文本部分是反斜杠,n,换行,反斜杠四个部分组成。正则部分分别是1到4个反斜杠和字母n,我用Python3写了对应的示例,相应的查找过程是这样子的。

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

例子虽然看上去简单,不过你能不能解释出这四个示例中的转义过程呢?

好了,今天的课程就结束了,希望可以帮助到你,也希望你在下方的留言区和我参与讨论,同时欢迎你把这节课分享给你的朋友或者同事,一起交流一下。

精选留言(15)

Robot 👍(26) 💬(1)google到了一篇回答,分享给大家理解课后思考:

Actually regex string specified by string literal is processed by two compilers: programming language compiler and regexp compiler:

Original Compiled Regex compiled

"\n" NL NL

"\\n" '\'+'n' NL

"\\\n" '\'+NL NL

"\\\\n" '\'+'\'+'n' '\'+'n'

https://stackoverflow.com/questions/6967204/how-is-n-and-n-interpreted-by-the-expanded-regular-expression/59192811#59192811

2020-06-24William 👍(11) 💬(3)原字符串中,共包含四个字符,第一个字符是 \,第二个是字母n,第三个是换行符,第四个是 \。

四个正则表达式的构造字符串中,第一个是换行符(正则转义后保持不变,仍然是换行符),第二个是\和n字母(正则转义后是换行符),第三个是\和换行符(正则转义后,单个换行符无意义,只剩下换行符),第四个是\\和换行符(正则转义后为一个斜杠和一个换行符)。

前三个都是找到了换行处,第四个找到了换行符。

测试JavaScript代码:

const str = '\\n\n\\'

const sources = ['\n', '\\n', '\\\n', '\\\\n']

const regs = []

sources.forEach(s => regs.push(new RegExp(s, 'g')))

regs.forEach(reg => {

console.log('[current reg] ', reg)

let once_match = reg.exec(str)

console.log('[result]', once_match)

})

2020-06-24Juntíng 👍(6) 💬(1)1、现将输入字符串正则进行字符串转义和正则转义

1. '\n' 字符转义 \n (换行符), 匹配 '\\n\n\\' 到一个换行符

2. '\\n' 字符转义 '\n'(字符\ 和 n), 正则转义 \n (换行符),匹配 '\\n\n\\' 到一个换行符

3. '\\\n' 字符转义 '\' 和 \n ('\\n' 字符\ 和 \n 换行符),正则转义 \n (换行符),匹配 '\\n\n\\' 到一个换行符

4. '\\\\n' 字符转义 '\' 、'\'、'n' ('\\n' 字符 \、\ 和 n), 正则转义 '\n' (字符 \ 和 n)

> '\\\n' 这个在转义过程中, 字符串转义后 字符'\' 和 \n (换行符) , 在正则转义时,转义一个换行符,结果还是换行符,没有意义,所以这里就没转义效果了。

2020-07-08爱乐之城 👍(4) 💬(1)'\\\n' 经过字符串转义变成反斜杠和\n,再经过正则转义变成换行符\n。前面字符串转义可以理解,但是为什么反斜杠和 \n,经过正则转义会变成 \n 呢?

2020-06-28向死而生 👍(3) 💬(2)入门课这样讲的吗?前面才一个/后面就二三四个。也不带解释一下的,我听的意义何在,还得自己找解释

2021-08-23虹炎 👍(3) 💬(1)()中内容表示匹配到的。

1,re.findall('\n', '\\n\n\\') 匹配到了 \\n(\n)\\ , '\n'就是换行,后面的\\n 表示反斜杠和n ,所以匹配到

第二个\n

2,re.findall('\\n', '\\n\n\\') 匹配到了 \\n(\n)\\ '\\n'经过字符串转义变成反斜杠和n,再经过正则转移变成换行符\n

3,re.findall('\\\n', '\\n\n\\') 匹配到了 \\n(\n)\\ '\\\n' 经过字符串转义变成反斜杠和\n,再经过正则转移变成换行符\n

4,re.findall('\\\\n', '\\n\n\\') 匹配到了 (\\n)\n\\ 经过字符串转义变成\\n,再经过正则转移变成反斜杠和n.

我的答案不知道对不对,请老师指正!

2020-06-24吕伟 👍(2) 💬(1)'\\n\n\\'输出是

\n

\

'\n'为python的换行符,

'\\n'为正则的换行符,

'\\\n'为反斜杠+python换行符,

'\\\\n'为字符\n

2020-07-18谷岳 👍(2) 💬(1)在ECMAScript中,运行结果如下:

'\\n\n\\'.match(/\n/g) 结果:["↵"]

'\\n\n\\'.match(/\\n/g) 结果:["\n"]

'\\n\n\\'.match(/\\\n/g) 结果:null

'\\n\n\\'.match(/\\\\n/g) 结果:null

我的理解是这样的:

'\\n\n\\'字符串转义结果是"\n↵\",对于正则\n自然匹配的是换行符,而\\n,正则编译成\n,所以第2题匹配结果是["\n"],而对于第3、4题,分别被正则编译成'\↵'和'\\n',无匹配返回null.

2020-07-03Jock 👍(2) 💬(1)感谢涂老师的详细讲解,\ 并没有想象的那么简单。

关于思考题的部分,前排留言已经给出了合理解释。

这里我列自己总结的一些知识点:

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

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

展开部分,我放到自己的博客,感兴趣可以去看看:https://blog.csdn.net/qq_27283619/article/details/106948855

2020-06-24ifelse 👍(1) 💬(1)正则中转义有些情况下会比较复杂,从录入的字符串文本,到最终的正则表达式,经过了字符串转义和正则转义两个步骤。--记下来

2022-11-18hai06.wang 👍(0) 💬(2)老师您好 有两个地方不太明白。我的理解如下

1、'\n'、'\\n'转移后最终是 \n 这个应该没问题

2、'\\\n' 首先经过字符串转义成 \\n ('\'+ '\n') ,正则转义后 '\' +' n'

3、'\\\\n'首先经过字符串转义成 \\n ('\'+'\'+'n'),正则转义后 '\' + 'n'

所以,针对2、3,这样理解的问题出在哪呢

2021-02-1667812 👍(0) 💬(1)题目的最后一个没理解,第一次字符串转义应该是“\\n”,那么第二次正则转义应该是“\n”,还是换行符啊

我试过利用循环打印出第一层转义

for c in "\\\\n":

print(c)

结果确实是\ \ n 呀

2020-11-13小乙哥 👍(0) 💬(1)这节课还是挺重要的,尤其对于\转义的理解。这里要建立一个认知,正则表达式也是一种语言,Java也是语言,正则表达式语言应用到Java字符串语言规范的时候,要注意在两层语言下的转义

2020-08-20🐒🐱🐭🐮🐯🐰🐶 👍(0) 💬(1)>>> import re>>>

re.findall('\n', '\\n\n\\')['\n'] # 找到了换行符>>> \n ->换行

re.findall('\\n', '\\n\n\\')['\n'] # 找到了换行符>>> \\=文本\ n=文本n ==>文本\n

re.findall('\\\n', '\\n\n\\')['\n'] # 找到了换行符>>> \\=文本\ \n=换行 ==> 文本 \ +换行

re.findall('\\\\n', '\\n\n\\')['\\n'] # 找到了反斜杠和字母n >>> \\=文本\ \\=文本\ n=文本n ==> 文本\\n

2020-07-10程序员在修行 👍(0) 💬(1)老师 我有个小疑问: 被查询的字符串在开发语言里 双反斜杠会被#字符串#转义成单反斜杠来被regex匹配吗? 四条反斜杠最后被转成单条\, python 输出 里 不太看得懂

>>> import re

>>> re.findall('\\\\', 'a*b+c?\\d123d\\')

['\\', '\\']

2020-07-04

相关推荐

《陰陽師》全新SR式神海忍怎麼樣
365bet娱乐官网

《陰陽師》全新SR式神海忍怎麼樣

10-22 889
布小林辞去内蒙古自治区主席职务,王莉霞任代主席
出门手机放哪儿最安全
日博官网365.tv

出门手机放哪儿最安全

09-04 76