未来已来|人工智能与数据库融合发展分论坛议程初探 了解详情
写点什么

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

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

    阅读完需:约 8 分钟

6 月 17 日,极客时间《企业级 Agents 开发实战营》正式上线,10 周掌握企业级 Agents 从设计、开发到部署全流程。

【编者按】正则表达式一直是开发者工具箱中趁手的利器,但很多人对此仍是一知半解。这次 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:0024952

评论

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

☕【Java技术指南】「TestNG专题」单元测试框架之TestNG使用教程指南(下)

洛神灬殇

Java JUnit 8月日更 testNG

Golang:并发操作中常见的读写锁

Regan Yue

读写锁 Go 语言 8月日更

架构实战营模块 6 作业指导

华仔

#架构实战营

架构实战营模块六作业-电商微服务架构

王晓宇

架构实战营

教你一招疯狂拿Offer!用微服务设计一个超大型分布式电商平台

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

无意发现,某东买来的Redis核心实践PDF,竟在GitHub数据库榜第一

Java~~~

Java redis 架构 面试 架构师

难以置信!网易首席架构师竟用了500页笔记,把网络协议给趣谈了

Java~~~

Java 架构 面试 TCP 网络

PM2实用指南及容器Docker部署

devpoint

Docker Node 8月日更

集字节大神面试经验诚意出品:微服务(耀世版),助你向大厂迈进

Java~~~

Java 架构 面试 分布式 微服务

秀出新天际的SpringBoot笔记,让开发像搭积木一样简单

Java~~~

Java spring 架构 面试 微服务

架构实战营模块6作业

技术是伙伴

架构实战营

阿里P8整理的《百亿级并发系统设计》实战教程,实在是太香了

Java 架构 后端 高并发

华为18级工程师耗时三年才总结出这份Java亿级高并发核心编程手册

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

面试进阶双飞!Alibaba新产SpringBoot深度历险(嵩山版)开源

Java~~~

Java spring 架构 面试 JVM

GitHub程序调优「黑马」!阿里大牛的Java性能优化实战笔记已上线

Java~~~

Java 架构 面试 JVM 性能调优

认认真真的聊聊"软"中断

编程 架构 后端 计算机

详解Java方法句柄MethodHandle

Java 方法句柄

Vue进阶(八十二):updated 应用详解

No Silver Bullet

Vue 8月日更

阿里二面:怎么解决MySQL死锁问题的?

Java MySQL 架构 面试 后端

八位阿里大牛耗时三年总结出Java面试复盘手册!带你实现逆风翻盘

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

Scrum Patterns:持续改进脉冲(Kaizen Pulse)(译)

Bruce Talk

敏捷开发 译文 Agile Scrum Patterns

爱了!阿里巴巴内部出品“SpringBoot+微服务指南”,理论与实战

Java~~~

Java spring 架构 面试 微服务

基于Netty,20分钟手撸一个RPC框架

码农参上

netty 8月日更

架构实战营 - 模块六作业

思梦乐

阿里内部疯传的(Java知识面试指南项目),成功面试9家大厂Offer

Java~~~

Java spring 架构 面试 微服务

我说想去京东面架构师,阿里表哥手甩我Java大厂技术题,让我滚蛋

Java~~~

Java spring 架构 面试 Spring Cloud

模块六作业

俊杰

架构实战营

难以置信!阿里新产微服务架构进阶笔记我粉了!理论实战齐飞

Java 编程 架构 面试 架构师

清华毕业20年从业经验马士兵建议所有Java高工必看的高并发编程

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

业界良心啊!第五次更新的Spring Cloud Alibaba升级太多内容

公众号_愿天堂没有BUG

Java 编程 程序员 架构 面试

泪目!跳槽太不容易,蚂蚁金服三轮面试,四个小时灵魂拷问

Java~~~

Java spring 架构 面试 微服务

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