最近在学习linux操作系统相关的内容。随着对linux系统的了解,逐渐的发现了系统这个东西的魅力。于此同时也发现linux系统上一些极为好用的文本批处理工具,比如find、grep、sed、awk。在使用这些批处理工具的时候发现有一个概念始终绕不过去,为了更加快速的学习使用这些工具决定先来研究一下这个在文本批处理中都会占据一席之地的概念-正则表达式。
1.信息的文本序列
我学习的时候比较喜欢追本溯源,所以在介绍正则表达式之前,先阐述下自己对正则表达式源头的理解。
一般来说文本批处理,涉及的内容无非是增、删、改、查。这四类基础操作再组合一下就完成了复杂文本的批处理操作。这四类操作有一个基础那就是“匹配”(在文本中找到自己想要的文本序列-语言序列)。
所有的在计算机上存储的文本文件,都是按照一定的编码格式存储的,有ASCII码(英文编码)、UTF8码(通用编码)、GB2312编码(中文编码)。这些编码的根本还是二进制数据。
目前我所接触到的语言,都可以通过语言中的基础符号,通过有限的排列组合而成。比如英语是以26个字母为语言单元,有字母进行排列构成单词,单词再经过组合构成语句,最终构成了英语。汉语更加博大精深,但是其根本也是基础偏旁构成字,字成词,词成句,最终形成了汉语。再辅以可穷举的各种文本符号(.,?!-...)就可以通过文本形式保存并阐述语义。
在此基础上延伸一下,其他的文本。比如C、shell、C++、数学公式等等基本都是通过可穷举的基础单元,通过排列组合而形成了对应的体系。
英语中使用到的字母符号最终以ASCII编码的方式进行了穷举,汉语则可以通过UTF8或者其他标准的编码格式穷举。
无论是语言还是公式都可以通过有限个单元,通过有限的排列组合形成文本传递特定的信息。反过来的话应该也是成立的。
信息可以通过有有限个基础单元进行有限次排列组合进行传递。每一种排列组合可以称为一个序列,我叫它信息的文本序列。
再延伸思考一下:图片、音频、视频等很多能传递信息的事务,都是由对应的基础单元构成的序列。图片的基础是像素,音频的基础是各种频率波。
“ hello world !”
由“ ” 空格 h e l l o w o r l d !构成的序列,其中每个独立的单元我们可以叫他正则单元。
2.正则表达式基础概念
正则表达式是由字符面量和元字符构成的表达式,这个表达式运算结果是带有一定特征的文本序列的集合。每个字符面量或者元字符都是一个正则单元。
如 : .* 就是一个正则表达式 其中.表示匹配任意的一个字符(我理解的是任意一个编码的基础单元)*表示匹配0个或者多个前面一个字符,放在一起就表达了一个序列集合-除空序列以外所有的文本序列。
正则表达式所表示的文本序列的基本单元是字符(实际上我更愿意理解为8位数据,因为有其他的非ascii编码也可以使用正则表达式,可能有点本末倒置,这个看个人喜好吧)。
3.字符面量
字符我的片面理解是ASCII表中的所有的数据。
包括了可在计算机终端显示的字符。[a,z];[A,Z];[0,9];基础符号;共计96个字符
不可显示的字符,一般是制表符共计33个
这些可见的字符就构成了字符面量的基础单元。
字符面量是由可见字符构成的所见及所得的常量(这个是为了和元字符区分)
4.元字符
元字符我的片面理解,就是从字符常量中找出一些特别的常量或者通过转义字符表达特殊的含义。
(因为不可见字符时无法直接通过可见的方式展示比如常用的回车换行,没办法通过可以显示的字符表示,因此出现一种表示方法叫做转义。通过使用\+可见字符的方式表达这些不可见的字符或者表达一些其他含义。例如\n表示回车,\r表示换行等等,这个时候n和r就是被转义的字符,就是把n和r的意义发生转换。\这个可见字符就有了一个新名字-转义字符。除了表示不可见字符外,还能通过转义字符扩展、改变有些字符的含义)
(其实我不明白的是为什么不统一规范都使用转义字符进行转义表达元字符,这样就能统一表达式,降低学习成本,可能是为了表达式的简洁??)
(1).基本元字符:
. 表示除换行符以外的所有符号
^ 表示字符串开始的位置
示例:^abc 表示以 "abc" 开头的字符串序列
$ 字符串的结束位置
示例:xyz$ 表示以 "xyz" 结尾的字符串序列
\ 转义字符
后面需要讲述下由于有些可见字符被使用为基本元字符,那该如何表示这个被用作元字符的可见字符。
(2).量词元字符 -在表示某种序列的同时还表达了序列重复的次数
* (星号) 表示前面一个正则单元出现零次或多次
示例:ab*c 可以表示 "ac", "abc", "abbc" 等序列
+ (加号) 表示前面的正则单元出现一次或多次
示例:ab+c 表示 "abc", "abbc" 等序列但不表示 "ac"
? (问号) 表示前面的正则单元出现零次或一次
示例:colou?r 表示"color" 和 "colour"序列
{n} 表示前面一个正则单元出现n次,其中前面一个正则单元只是这个元字符的输入参数,不参与整体表达式。
示例 a{2}bc,表示aabc这个序列
{n,}表示前面一个正则单元至少出现n次,其中前面一个正则单元只是这个元字符的输入参数,不参与整体表达式。
示例 a{2,}bc,表示aabc,aaabc等序列,不表示abc
{n,m}表示前面一个正则单元至少出现n次,其中前面一个正则单元只是这个元字符的输入参数,不参与整体表达式。
示例 a{2,4}bc,表示aabc,aaabc,aaaabc序列,不表示aaaaaabc
特殊表达: 正常状态下*,+,?,{n,},{n,m}这些源字符都有表示多次的概念,但是当*,+,?,{n,},{n,m}后面加了一个?后将被限定表示意义。
限制原则就是 ,
假设 从 *,+,?,{n,},{n,m}选一个元字符为A,A前有一个正则单元M,A?后有个正则单元N
MA?N
这个表示在表示满足MAN的序列中同时满足长度最小的原则
现有字符串 a******cddda***cacddda***acaacdddd
比如 'a[a-z]+?' 可以拆解成
满足MAN的序列 ,又长度最小,就是ac、aa序列
比如 'a[a-z]+d' 可以拆解成
满足MAN的序列 ,又长度最小,就是a******cd、a***cacd、a***acaacd序列
比如 'a[a-z]+da' 可以拆解成
满足MAN的序列 ,又长度最小,就是a******cddda、a***cacddda序列
(3)集合类元字符
[] 表示序列中包含[]中的任意一个字符面量
例如: a[aeiou] 表示aa、ae、ai、ao、au序列
[^]表示序列中不包含[]中的任意一个字符面量
例如: a[^aeiou] 表示除aa、ae、ai、ao、au以外的以a开始的序列例如a#,ab等
[-]表示范围
例如: a[a-z] 表示除aa、ab、ac ... az的序列,常用的有a-z表示26个小写字母;A-Z表示26个大写字母;0-9表示10个数值
(4)分组和选择
()用来定义一个正则单元,将()内的正则表达式输出的序列看作为一个正则单元
(abc)* 表示将abc视作一个正则单元然后结合*的意义。那这个可以表示abc、abcabc 、..构成的序列
| 用来表示选择
cat|dog 表示"cat" 或 "dog"序列中的一个
(5)转义字符类元字符
\b 表示单词边界,本身不作为序列中的单元,更多的类似一种序列中格式说明
例如: a\b 表示 以a结尾的单词 如果有一个序列是 a apple ,则a\b可以指代a,\ba可以是a,也可以是apple序列中的a。
\B 表示非字符边界
\d 与[0-9]等价表示数值0-9
\D 与[^0-9]表示非数值
\w 与[a-zA-Z0-9_]等价表示字符数值下划线
\W 与[^a-zA-Z0-9_]等价表示非字符数值下划线
\s 表示任意空白字符(空格、制表符、换行符等)
\S 表示非任意空白字符(空格、制表符、换行符等)
\n 换行符
\t 制表符
\r 回车符
\f 换页符
\v 垂直制表符
等等
(6)正反向预查
(?=...)表示正向预查
Windows(?=95|98) 匹配后面跟着95或98的"Windows"
(?!...) (正向否定预查)
Windows(?!95|98) 表示后面不跟着95或98的"Windows"
(?<=...) (反向肯定预查)
(?<=95|98)Windows 匹配前面是95或98的"Windows"
(?<=...) (反向肯定预查)
(?<!95|98)Windows 匹配前面不是95或98的"Windows"
5.配合使用的工具
很多工具都会用到正则表达式:例如linux中的find,grep,sed,awg等文本处理工具都会用到
6.使用中的难点
7.使用中遇到的问题点记录
(1)由于有部分可视化的字符被用作了元字符比如 \ * . [ ] - { } + ? ( ) |等,这个时候如果需要表示这些元字符的字符面量,那就需要用转义字符进行转义
比如: \* 表示 *的字符面量
ab\*+ 可以表示 ab*,ab**,ab***等序列
(2)()除了可以将正则表达式装换为一个正则单元以外,还能够在后续的正则表达式中通过\数值的方式应用这个正则单元
(abc)(de)X\1\2 其中\1就表示表达式中第一个用()生成的正则单元\2表示第二个
则(abc)(de)X\1\2就表示 abcdeXabcde的序列,这种\数值的方式焦分组的引用
通过(?:)可以取消使用()生成的正则单元分组的引用
(?:abc)(de)X\1\2 表示的是abcdeXde的序列,第一个()被取消了引用,所以第二个()就变成了第一个引用,表达式没有第二个引用为空
(3)特殊表带 ^$
^表示行首$表示末尾,如果正则表达式为 '^$'表示空行