写点什么

正则表达式(一):纠结的转义

  • 2011-01-21
  • 本文字数:2495 字

    阅读完需:约 8 分钟

【编者按】正则表达式一直是开发者工具箱中趁手的利器,但很多人对此仍是一知半解。这次 InfoQ 中文站有幸邀请到来自盛大创新院的余晟,开辟《正则表达式》的专栏,为读者讲解正则表达式的一些技巧和概念。他还是《精通正则表达式》和《技术领导之路》的译者。


用过正则表达式的人都知道,正则表达式中有一类叫做“元字符(meta-character)”的特殊符号,它们并不匹配自身对应的字符,而具有其他的含义。比如脱字符『^』表示“定位到字符串 / 行的开头”,加号『+』表示“之前的元素重现 1 次以上。如果需要匹配这些字符本身,需要用反斜线来转义,匹配『^』就应该用\^,匹配『+』就应该用\+。

看起来有点麻烦,但这样的元字符并不多:^$()*+?.[\{|

元字符

说明

举例

^

匹配整个字符串的起始位置,或者行的起始位置,如果在字符组内部,则表示排除型(negative)字符组

^Start

$

匹配整个字符串的结束位置,或者行的结束位置

End$

()

分组,提供反向引用 (gourp1) \1 或多选分支

(ab)+

* + ?

量词,限定之前元素出现的次数

a+ (ab)+

.

默认情况下匹配换行符之外的任意字符,在多行模式下可以匹配换行符

复制代码
\[

字符组的起始符号

[0-9]

\

反斜线用来表示转义序列,或去掉元字符的转义

\1

{

重现限定符的开始

{2, 6}

|

划分多选分支(括号没有出现时,可以想象括号出现在整个表达式最外层)

Tom|Jerry

你或许注意到了,这些元字符并不是“对称”出现的,比如与开方括号 [ 对应的闭方括号 ],与开花括号 { 对应的闭花括号 } ,这两个字符是否元字符,需要依据具体正则表达式的情况确定,我们以闭方括号] 的情况为例(}的情况与此类似):如果之前能找到与之对应的元字符开方括号 [,则] 作为元字符出现,否则,作为普通字符出现。

字符串 \ 表达式

[ab]]

ab]

a]

复制代码
ab\]

另外,因为方括号本身可以表示字符组『[0-9]』,所以在字符组内部的闭方括号在任何情况下都要转义,否则类似『[]]』的正则表达式会出现二义性,造成识别错误。

如果需要匹配方括号内(包括方括号),至少包含一个字符的字符串(比如 [text]),所用的正则表达式就应该是:『\[[^\]]+]』。

看明白了吗?『\[』匹配开方括号,然后用一个排除型字符组匹配“除闭方括号 ] 之外的任意字符(注意,在字符组内部,闭方括号 ] 一定需要转义),用『+』表示它至少要出现一次以上,最后用一个『]』匹配闭方括号。

下面用代码来验证,以 python 为例:

import re #为使用正则表达式,必须首先导入 re

>>> re.search(’^\[[^\]]+]$’, ‘[abcdefg]’) #进行数据验证时,在表达式首尾加上 ^ 和 $ 是好习惯

<_sre.SRE_Match object at 0x7ff3bc5e75e0>

>>> re.search(’^\[[^\]]+]$’, ‘[]’)

>>>

看来确实没有问题,下面用 Java 试试。直接调用 Java 中的 _string_.matches(regex) 方法,观察返回的 boolean 值:

“[]”.matches("^\[[^\]]+]$")

但是却出现了编译错误:invalid escape sequence。这是为什么呢?在 Python 中我们并没有使用 raw string(如果使用 raw string,就应该用 r"^\[[^\]]+]$"),一切正常,可是在 Java 中为什么会出错呢?

要回答这个问题,就得分清转义的层次和规则。如果你留心观察就会发现,上面我们讲的都是“正则表达式的转义”,比如『\[[^\]]+]』是正确转义的正则表达式。仅仅用做正则表达式,它是绝对没有问题的,但它“不仅仅”是正则表达式,而是“字符串形式给出的正则表达式”——注意到了吗?在表达式两端,各有一个双引号。

回忆一下 Java 中字符串(String)的规则,其中转义序列(escape sequence)用来表示特殊字符,比如\n 表示换行符,\t 表示制表符,而\[并不是 Java 能识别的转义序列,当然要出错了。为了表示“正则表达式中的\[”,我们传递给 Pattern.compile() 的字符串必须正确表示\[——在字符串中,[ 是不需要转义的,而 \ 是需要转义的,所以在字符串中,应该写做 \\[。

总结一下:

字符串的表现层

\\[

字符串的概念层

\[

正则表达式的表现层

\[

正则表达式的概念层

[(非元字符)

理解了这一点,就不难理解为什么正则表达式的转义序列在正则表达式中要写两个反斜线了,比如 \+ 要写成 \\+ 。但是 \n 之类的有点特殊,无论你写成 \n 或是 \\n ,结果都是一样,\t 之类的情况与此类似。

字符串的表现层

\\n

\n

字符串的概念层

\n

换行符

正则表达式的表现层

\[

换行符

正则表达式的概念层

换行符

换行符

如果字符串中表示反斜线字符本身(不是用来转义的符号),则需要在正则表达式中写四个反斜线字符。

“\”.matches("\\\\"); //true

字符串的表现层

\\\\

字符串的概念层

\\

正则表达式的表现层

\\

正则表达式的概念层

\(非元字符)

看起来,转义问题似乎就是这样,想明白了也很简单。不过,如果你记忆力比较好,估计会问:为什么在Python 中写\[不会报错,而Java 中会报错?这确实是个好问题,所以我们把它当成本文的结束。

照道理说,各种语言的转义规则都一样:\n 表示换行符,\t 表示制表符…… 事实也确实如此,只是Python 对字符串的处理更复杂一些:如果一个转义序列不能识别,会直接原样保存到字符串中。也就是说,Python 遇到无法识别字符串中的\[,不会报错,而是将它原样“转交”给字符串:

字符串的表现层

\[

\\[

字符串的概念层

\[

\[

正则表达式的表现层

\[

\[

正则表达式的概念层

[(非元字符)

[(非元字符)

“无法识别的转义序列直接转交字符串”的做法不只 Python 有,PHP 也会这样处理,但是我并不推荐这样使用,因为它往往会令不理解这特性的人困惑,正则表达式对应的字符串中出现\[如何不会报错?\[和\\[为什么竟然是一样的效果?

最好的办法或许还是统一表示法,都写成\\[,既方便与其它语言兼容,也方便大家阅读和理解。

关于作者

余晟,程序员,曾任抓虾网高级顾问,现就职于盛大创新院,感兴趣的方向包括搜索和分布式算法等。翻译爱好者,译有《精通正则表达式》(第三版)和《技术领导之路》,目前正在写作《正则表达式傻瓜书》(暂定名),希望为国内开发同行贡献一本实用的正则表达式教程。


感谢张凯峰对本文的策划及审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

2011-01-21 00:0025601

评论

发布
暂无评论
发现更多内容

区块链技术支撑元宇宙经济系统的有序运转

CECBC

Web3.0时代的保险业,真的可以实现去中心化吗?

CECBC

加速数字经济发展,2022更需要云上创新的稳定器与推动力

脑极体

Go编译原理系列5(抽象语法树构建)

书旅

Go 后端 编译原理 编译语言

ReactNative进阶(十七):RefreshControl 组件实现刷新效果

No Silver Bullet

1月月更 ReactNative RefreshControl

王者荣耀商城异地多活架构设计

ren

记一次安全漏洞分析

网络安全学海

黑客 网络安全 信息安全 渗透测试 安全漏洞

大画 Spark :: 网络(2)-下篇-通过网络收取消息的过程

dclar

大数据 spark 源代码 Spark 源码

云原生训练营毕业总结

9527

低代码实现探索(十八)流程中参数的初始化设计

零道云-混合式低代码平台

低代码实现探索(十九)低代码的意义

零道云-混合式低代码平台

Salesforce架构师的10大原则

俞凡

架构 大厂实践 Salesforce

模块九作业:设计电商秒杀系统

危险游戏

架构实战营

架构实战营 - 毕业设计

随风King

「架构实战营」

面向未来,我们来聊一聊什么是现代化数据架构

亚马逊云科技 (Amazon Web Services)

存储

虚拟数字人发展提速 多家银行宣布“造人”

CECBC

Weblogic-SSRF漏洞复现

喀拉峻

网络安全 安全 信息安全

Go 语言快速入门指南:Go 转 JSON

宇宙之一粟

Go JSON序列化 1月月更

2021年行摄回忆录

穿过生命散发芬芳

盘点2021 1月月更

三行代码下载抖音视频

你?

程序员的快乐,只需要这7款工具!

Jackpop

小白都能吃透 Java IOl 流,最骚最全笔记,没有之一!2️⃣

XiaoLin_Java

简讯:明道云近期荣获的三项荣誉

明道云

火狐浏览器一败涂地?

Jackpop

毕业设计项目

panxiaochun

架构实战营

Apache Atlas 数据血缘

亚马逊云科技 (Amazon Web Services)

存储

没有区块链,就没有元宇宙

CECBC

如何保证消息消费时的幂等性?

JavaEdge

1月月更

基于Amazon ECS Fargate构建Apache Superset

亚马逊云科技 (Amazon Web Services)

存储

偷偷看了同事的代码找到了优雅代码的秘密

慕枫技术笔记

后端 1月月更

【SpringCloud技术专题】「Gateway网关系列」微服务网关服务的Gateway全流程开发实践指南(2.2.X)

码界西柚

SpringCloud SpringCloud Gateway API Gateway 1月月更

正则表达式(一):纠结的转义_Java_余晟_InfoQ精选文章