写点什么

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

  • 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:0025043

评论

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

anyRTC实时音视频-社交娱乐解决方案

anyRTC开发者

ios android 音视频 WebRTC RTC

阿里三面惨遭被虐,spring,jvm,mybatis,并发编程等一窍不通

Java架构之路

Java 程序员 架构 面试 编程语言

期权代持的“坑”里,加拿大人也在 | 法庭上的CTO(11)

赵新龙

CTO 法庭上的CTO

Java并发编程:多线程如何实现阻塞与唤醒

码农架构

Java并发

数据类型第2篇「字典和集合的原理和应用」

清菡软件测试

测试开发

从零开始学习Java8 Stream,看这篇就够了

Silently9527

Java stream java8

量化交易APP系统软件开发(现成)

系统开发

架构师训练营W09作业

Geek_f06ede

在线医疗的发展和优势

anyRTC开发者

android 音视频 WebRTC RTC 医疗方案

BAT等大厂面试复习资料文档整理:ActiveMQ+redis+Spring+高并发多线程+JVM

Java架构之路

Java 程序员 架构 面试 编程语言

C语言服务器编程必备常识

MySQL从删库到跑路

c

甲方日常 68

句子

工作 随笔杂谈 日常

架构作业--大数据

Nick~毓

旷工三天被开除,公司赔偿十万五 | 法庭上的CTO(9)

赵新龙

CTO 法庭上的CTO

DolphinDB与MongoDB在时序数据上的对比测试

DolphinDB

mongodb 分布式系统 时序数据库 DolphinDB 数据库开发

探营苏州数字人民币试点

CECBC

数字人民币

SSO的通用标准OpenID Connect

程序那些事

OAuth 2.0 程序那些事 授权框架 安全框架 openid

JVM从概述到调优图文详解,含思维脑图深度剖析!

Java架构师迁哥

Spring Boot 集成 Redis

噜噜猫

Spring Boot

手把手教你免费获取正版 Jetbrains 全家桶 License

郭旭东

ide JetBrains

互联网新规鼓励保险与大数据、区块链等新技术融合!业内呼吁配套产品管理制度尽快出炉

CECBC

互联网金融

生产环境全链路压测建设历程之十 淘宝网2013年的建设过程

数列科技杨德华

【小菜学网络】数据链路层概述

fasionchan

网络编程 计算机网络 网络协议 TCP/IP

LeetCode题解:429. N叉树的层序遍历,BFS,JavaScript,详细注释

Lee Chen

算法 大前端 LeetCode

第三代人工智能基础设施背后,是一次技术应用的常识普及运动

脑极体

没弄懂这些Java基础,简历上千万别写熟悉:异常+反射+注解+泛型

小Q

Java 学习 编程 面试 基础

赶紧看!阿里架构师必备“绝杀版”Tomact架构笔记堪称绝技

比伯

Java tomcat 编程 架构 程序人生

1428万的Adobe采购纠纷 | 法庭上的CTO(10)

赵新龙

CTO 法庭上的CTO

架构之书:雄伟与《Domain Driven Design》

lidaobing

架构 领域驱动设计

架构师训练营 Week8 - 课后作业

极客大学架构师训练营

BATJ面试常被问到的100+题:Spring+微服务+SpringMVC+MyBatis

Java架构之路

Java 程序员 架构 面试 编程语言

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