春争日,夏争时,扫码抽取夏日礼包!!! 了解详情
写点什么

Linux/Unix 工具与正则表达式的 POSIX 规范

  • 2011 年 7 月 11 日
  • 本文字数:3728 字

    阅读完需:约 12 分钟

对正则表达式有基本了解的读者,一定不会陌生『\d』、『[a-z]+』之类的表达式,前者匹配一个数字字符,后者匹配一个以上的小写英文字母。但是如果你用过 vi、grep、awk、sed 之类 Linux/Unix 下的工具或许会发现,这些工具虽然支持正则表达式,语法却很不一样,照通常习惯的办法写的『\d』、『[a-z]+』之类的正则表达式,往往不是无法识别就是匹配错误。而且,这些工具自身之间也存在差异,同样的结构,有时需要转义有时不需要转义。这,究竟是为什么呢?

原因在于,Unix/Linux 下的工具大多采用 POSIX 规范,同时,POSIX 规范又可分为两种流派(flavor)。所以,首先有必要了解一下 POSIX 规范。

POSIX 规范

常见的正则表达式记法,其实都源于 Perl,实际上,正则表达式从 Perl 衍生出一个显赫的流派,叫做 PCRE(Perl Compatible Regular Expression),『\d』、『\w』、『\s』之类的记法,就是这个流派的特征。但是在 PCRE 之外,正则表达式还有其它流派,比如下面要介绍的 POSIX 规范的正则表达式。

POSIX 的全称是 Portable Operating System Interface for uniX,它由一系列规范构成,定义了 UNIX 操作系统应当支持的功能,所以“POSIX 规范的正则表达式”其实只是“关于正则表达式的 POSIX 规范”,它定义了 BRE(Basic Regular Expression,基本型正则表达式)和 ERE(Extended Regular Express,扩展型正则表达式)两大流派。在兼容 POSIX 的 UNIX 系统上,grep 和 egrep 之类的工具都遵循 POSIX 规范,一些数据库系统中的正则表达式也符合 POSIX 规范。

BRE

在 Linux/Unix 常用工具中,grep、vi、sed 都属于 BRE 这一派,它的语法看起来比较奇怪,元字符『(』、『)』、『{』、『}』必须转义之后才具有特殊含义,所以正则表达式『(a)b』只能匹配字符串 (a)b 而不是字符串 ab;正则表达式『a{1,2}』只能匹配字符串 a{1,2},正则表达式『a\{1,2\}』才能匹配字符串 a 或者 aa。

之所以这么麻烦,是因为这些工具的诞生时间很早,正则表达式的许多功能却是逐步发展演化出来的,之前这些元字符可能并没有特殊的含义;为保证向后兼容,就只能使用转义。而且有些功能甚至根本就不支持,比如 BRE 就不支持『+』和『?』量词,也不支持多选结构『(…|…)』和反向引用『\1』、『\2』…。

不过今天,纯粹的 BRE 已经很少见了,毕竟大家已经认为正则表达式“理所应当”支持多选结构和反向引用等功能,没有确实太不方便。所以虽然 vi 属于 BRE 流派,但提供了这些功能。GNU 也对 BRE 做了扩展,支持『+』、『?』、『|』,只是使用时必须写成『\+』、『\?』、『\|』,而且也支持『\1』、『\2』之类反向引用。这样,GNU 的 grep 等工具虽然名义上属于 BRE 流,但更确切的名称是 GNU BRE。

ERE

在 Linux/Unix 常用工具中,egrep、awk 则属于 ERE 这一派,。虽然 BRE 名为“基本”而 ERE 名为“扩展”,但 ERE 并不要求兼容 BRE 的语法,而是自成一体。因此其中的元字符不用转义(在元字符之前添加反斜线会取消其特殊含义),所以『(ab|cd)』就可以匹配字符串 ab 或者 cd,量词『+』、『?』、『{n,m}』可以直接使用。ERE 并没有明确规定支持反向引用,但是不少工具都支持『\1』、『\2』之类的反向引用。

GNU 出品的 egrep 等工具就属于 ERE 流(更准确的名字是 GNU ERE),但因为 GNU 已经对 BRE 做了不少扩展,所谓的 GNU ERE 其实只是个说法而已,它有的功能 GNU BRE 都有了,只是元字符不需要转义而已。

下面的表格简要说明了几种 POSIX 流派的区别 [1] (其实,现在的 BRE 和 ERE 在功能上并没有什么区别,主要的差异是在元字符的转义上)。

几种 POSIX 流派的说明

流派

说明

工具

BRE

(、)、{、}都必须转义使用,不支持 +、?、|

grep、sed、vi(但 vi 支持这些多选结构和反向引用)

GNU BRE

(、)、{、}、+、?、|都必须转义使用

GNU grep、GNU sed

ERE

元字符不必转义,+、?、(、)、{、}、|可以直接使用,\1、\2 的支持不确定

egrep、awk

GNU ERE

元字符不必转义,+、?、(、)、{、}、|可以直接使用,支持\1、\2

grep –E、GNU awk

为了方便查阅,下面再用一张表格列出基本的正则功能在常用工具中的表示法,其中的工具 GNU 的版本为准。

常用 Linux/Unix 工具中的表示法

PCRE 记法

vi/vim

grep

awk

sed

*

*

*

*

*

\+

\+

\+

?

\=

\?

?

\?

{m,n}

\{m,n}

\{m,n\}

{m,n}

\{m,n\}

\b *

\< \>

\< \>

\< \>

\y \< \>

(…|…)

\(…\|…\)

\(…\|…\)

(…|…)

(…|…)

(…)

\(…\)

\(…\)

(…)

(…)

\1 \2

\1 \2

\1 \2

不支持

\1 \2

注:PCRE 中常用\b 来表示“单词的起始或结束位置”,但 Linux/Unix 的工具中,通常用\< 来匹配“单词的起始位置”,用\> 来匹配“单词的结束位置”,sed 中的\y 可以同时匹配这两个位置。

POSIX 字符组

在某些文档中,你还会发现类似『[:digit:]』、『[:lower:]』之类的表示法,它们看起来不难理解(digit 就是“数字”,lower 就是“小写”),但又很奇怪,这就是 POSIX 字符组。不仅在 Linux/Unix 的常见工具中,甚至一些变成语言中都出现了这些字符组,为避免困惑,这里有必要简要介绍它们。

在 POSIX 规范中,『[a-z]』、『[aeiou]』之类的记法仍然是合法的,其意义与 PCRE 中的字符组也没有区别,只是这类记法的准确名称是 POSIX 方括号表达式(bracket expression),它主要用在 Unix/Linux 系统中。POSIX 方括号表示法与 PCRE 字符组的最主要差别在于:POSIX 字符组中,反斜线\不是用来转义的。所以 POSIX 方括号表示法『[\d]』只能匹配\和 d 两个字符,而不是『[0-9]』对应的数字字符。

为了解决字符组中特殊意义字符的转义问题,POSIX 方括号表示法规定,如果要在字符组中表达字符](而不是作为字符组的结束标记),应当让它紧跟在字符组的开方括号之后,所以 POSIX 中,正则表达式『[]a]』能匹配的字符就是] 和 a;如果要在 POSIX 方括号表示法中表达字符 -(而不是范围表示法),必须将它紧挨在闭方括号] 之前,所以『[a-]』能匹配的字符就是 a 和 -。

POSIX 规范也定义了 POSIX 字符组,它近似等价于于 PCRE 的字符组简记法,用一个有直观意义的名字来表示某一组字符,比如 digit 表示“数字字符”,alpha 表示“字母字符”。

不过,POSIX 中还有一个值得注意的概念:locale(通常翻译为“语言环境”)。它是一组与语言和文化相关的设定,包括日期格式、货币币值、字符编码等等。POSIX 字符组的意义会根据 locale 的变化而变化,下面的表格介绍了常见的 POSIX 字符组在 ASCII 语言环境与 Unicode 语言环境下的意义,供大家参考。

POSIX 字符组

POSIX 字符组

说明

ASCII 语言环境

Unicode 语言环境

[:alnum:]*

字母字符和数字字符

[a-zA-Z0-9]

[\p{L&}\p{Nd}]

[:alpha:]

字母

[a-zA-Z]

\p{L&}

[:ascii:]

ASCII 字符

[\x00-\x7F]

\p{InBasicLatin}

[:blank:]

空格字符和制表符

[ \t]

[\p{Zs}\t]

[:cntrl:]

控制字符

[\x00-\x1F\x7F]

\p{Cc}

[:digit:]

数字字符

[0-9]

\p{Nd}

[:graph:]

空白字符之外的字符

[\x21-\x7E]

[^\p{Z}\p{C}]

[:lower:]

小写字母字符

[a-z]

\p{Ll}

[:print:]

类似 [:graph:],但包括空白字符

[\x20-\x7E]

\P{C}

[:punct:]

标点符号

[][!"#$%&’()*+,./:;<=>?@\^_`{|}~-]

[\p{P}\p{S}]

[:space:]

空白字符

[ \t\r\n\v\f]

[\p{Z}\t\r\n\v\f]

[:upper:]

大写字母字符

[A-Z]

\p{Lu}

[:word:]*

字母字符

[A-Za-z0-9_]

[\p{L}\p{N}\p{Pc}]

[:xdigit:]

十六进制字符

[A-Fa-f0-9]

[A-Fa-f0-9]

注 1:标记 * 的字符组简记法并不是 POSIX 规范中的,但使用很多,一般语言中都提供,文档中也会出现。

注 2:对应的 Unicode 属性请参考本系列文章已经刊发过的关于 Unicode 的部分。

POSIX 字符组的使用有所不同。主要区别在于,PCRE 字符组简记法可以脱离方括号直接出现,而 POSIX 字符组必须出现在方括号内,所以同样是匹配数字字符,单独出现时,PCRE 中可以直接写『\d』,而 POSIX 字符组就必须写成『[[:digit:]]』。

Linux/Unix 下的工具中,一般都可以直接使用 POSIX 字符组,而 PCRE 的字符组简记法『\w』、『\d』等则大多不支持,所以如果你看到『[[:space:]]』而不是『\s』,一定不要感到奇怪。

不过,在常用的编程语言中,Java、PHP、Ruby 也支持使用 POSIX 字符组。其中 Java 和 PHP 中的 POSIX 字符组都是按照 ASCII 语言环境进行匹配;Ruby 的情况则要复杂一点,Ruby 1.8 按照 ASCII 语言环境进行匹配,而且不支持『[:word:]』和『[:alnum:]』,Ruby 1.9 按照 Unicode 语言环境进行匹配,同时支持『[:word:]』和『[:alnum:]』。

说明:关于正则表达式的系列文章到此即告一段落,作者最近已经完成了一本关于正则表达式的书籍,其中更详细也更全面地讲解了正则表达式使用中的各种问题。该书暂定名《正则导引》,预计近期上市,有兴趣的读者敬请关注。


[1] 关于 ERE 和 BRE 的详细规范,可以参考 http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap09.html

关于作者

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


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

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

2011 年 7 月 11 日 08:3415363

评论

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

每周学习感想

张靖

#架构实战营

CFI技术新探索,struct_san今日登场

腾讯安全云鼎实验室

云安全

Kubernetes Operator 开发入门

侯生

Kubernetes operator #k8s

事件管理CRM系统是活动策划工作的必要工具

低代码小观

CRM 管理系统 事件管理 CRM系统 活动策划

终于有人把云原生存储讲明白了

青云技术社区

云计算 云原生 存储 Cloud Native

【引航计划】优质内容合集名单公布

InfoQ写作社区官方

引航计划 热门活动

Gartner发布《2021年企业低代码平台魔力象限》低码一体化平台成趋势!

优秀

低代码

helm部署的es出现探针过不了的问题

ilinux

英特尔公司CEO帕特·基辛格致开放生态系统的一封公开信

科技新消息

微信业务架构

stars

架构训练营

京东架构师珍藏版:redis深度笔记(全彩版)全篇精华,细节满满

收到请回复

redis 编程 面试 后端 计算机

字节跳动Web Infra发起 Modern.js 开源项目,打造现代 Web 工程体系

字节跳动终端技术

字节跳动 大前端 开源技术

4个实验,彻底搞懂TCP连接的断开

Java 架构 TCP 后端 网络编程

腾讯数字生态大会【TechoDay技术回响日】

腾讯云数据库

利用守护线程隐式关闭线程池

FunTester

Java 线程池 接口测试 FunTester 守护线程

落实等级保护工作的意义简单说明-行云管家

行云管家

网络安全 等保 等级保护 安全等级保护

Aeron是什么?

BUG侦探

Aeron

2021云栖大会|东方通正式加入阿里云云原生合作伙伴计划,强强联手共创国产数字化转型新风向!

阿里巴巴云原生

阿里云 云原生 合作共赢 云栖大会

用会声会影制作手链的展示视频

懒得勤快

四万字32图,Kafka知识体系保姆级教程宝典

五分钟学大数据

大数据 kafka

携手企业创新 共创美好未来——低代码助力企业数字化

明道云

高校人才集聚飞桨启航菁英计划!百度联合中国图象图形学学会助力AI人才培养

百度大脑

人工智能 百度 图像

校招失败,在小公司熬了2年后我终于进了阿里,竭尽全力(Java岗)

Java 程序员 架构 面试 计算机

第二存储解决方案白皮书 | 专为非生产数据而设计的存储方案

QingStor分布式存储

云原生 分布式存储

【权限专栏】联盟链的“圆桌会议”

趣链科技

搭建springboot-tdengine 环境

williamcai

tdengine Spring Boot

云主机是什么?可以用来干嘛?

行云管家

云计算 云服务 云主机 云平台

1024 写给程序员的一些建议

Andy阿辉

编程 程序员 程序人生 程序 java;

MongoDB技术实践与应用案例征集活动

MongoDB中文社区

mongodb

Python代码阅读(第46篇):寻找符合条件的元素

Felix

Python 编程 Code Programing 阅读代码

二本渣渣的金九银十辛酸面试之旅:5 面阿里(Java岗) 侥幸上岸

Java 编程 程序员 架构 面试

Linux/Unix工具与正则表达式的POSIX规范_Java_余晟_InfoQ精选文章