第4章:正则表达式¶
一、元字符和集合¶
1. 正则表达式的概念¶
正则表达式(Regular Expressions)是一种用于描述字符串模式的形式化语言,其核心功能在于匹配、查找、替换文本中的特定结构。
-
定义:正则表达式是一套由普通字符与特殊符号(元字符)组成的规则系统,用于精确或模糊地描述一类字符串的共同特征。
-
应用范围:
- 主要用于字符串的匹配操作(如判断某行是否包含特定格式)和替换操作(如批量修改日志中的IP地址)。
- 在 Linux 系统中,广泛应用于以下工具:
- 文本编辑器:
vi - 内容查看工具:
more - 文本过滤命令:
grep - 编译器生成工具:
yacc、lex - 流式文本处理器:
sed、awk
- 文本编辑器:
-
在非 Linux 环境中,如 Visual Studio、Microsoft Word 等现代文本编辑器也支持正则表达式进行高级查找与替换。
-
注意事项:
- 正则表达式 ≠ 文件名通配符(如
*.txt):- 正则表达式用于文本内容处理(如
grep 'a.c' file匹配 "abc"、"a2c" 等); - 通配符用于文件路径匹配(如
ls *.c列出 C 源文件),两者语法规则完全不同。
- 正则表达式用于文本内容处理(如
- 不同软件对正则表达式的实现存在差异:
- 例如,BRE(基本正则表达式)、ERE(扩展正则表达式)、PCRE(Perl 兼容正则表达式)在语法细节(如分组、量词)上有所不同;
- 使用时需注意当前工具支持的正则类型(如
grep默认使用 BRE,egrep使用 ERE)。
2. 正则表达式中的特殊字符(元字符)¶
在正则表达式中,某些字符具有特殊含义,称为元字符(Metacharacters)。PPT 明确指出有 6 个基本元字符:
- 元字符列表:
. * [ \ ^ $
这些字符在正则表达式中不表示其字面意义,而是代表某种匹配逻辑。其余所有字符(包括字母、数字、标点等)默认与其自身匹配。
- 转义机制:
- 若希望匹配元字符的字面值,需在其前加反斜线
\进行转义。 - 示例:
- 正则表达式
end\.仅匹配字符串"end.",其中\.表示字面的句点; - 若写作
end.,则会匹配"enda"、"end3"等任意以"end"开头后接任意字符的字符串。
- 正则表达式
注意:反斜线本身也是元字符,因此匹配字面反斜线需写为
\\。
3. 单字符正则表达式¶
正则表达式的基本构建单元是“单字符正则表达式”,即一次只匹配一个字符的模式。
- 普通字符:如
a、b、/等非元字符,直接匹配其自身。例如: - 正则
a匹配字符串中的"a"; -
正则
/匹配路径中的斜杠。 -
转义字符支持:
- 对 6 个元字符进行转义后可匹配其字面形式:
\.→ 匹配.\*→ 匹配*\$→ 匹配$\^→ 匹配^\[→ 匹配[\\→ 匹配\
- 示例:正则表达式
\\*表示“一个反斜线后跟一个星号”,因此匹配字符串\*;- 但不匹配
\\*(因为该字符串包含两个反斜线和一个星号,而\\*中的*是量词,表示前面的\出现 0 次或多次——此处易混淆,PPT 强调此写法实际意图为匹配字面\*,故应理解为两个转义字符\和\*的组合,即\\\*才更准确;但根据 PPT 原文,其意图是说明\\*被解释为“字面\后跟字面*”,可能隐含上下文为 BRE 中*需转义才为字面)。
- 但不匹配
-
非法转义:如
\u、\x(在基础正则中)等组合未被定义,属于 undefined(未定义)行为;后续软件(如 Perl、Python)可能赋予其特殊含义(如 Unicode 转义),但在传统 Unix 工具中应避免使用。 -
圆点
.: - 是最重要的单字符通配符,匹配任意单个字符(通常不包括换行符
\n); - 示例:正则
a.c可匹配"abc"、"a2c"、"a c"等。
4. 单字符正则表达式:定义集合(字符类)¶
通过方括号 [...] 可定义一个字符集合(Character Class),表示匹配其中任意一个字符。
- 基本语法:
[abcd]表示匹配a、b、c或d中的任意一个字符;-
集合内字符顺序无关,重复无影响。
-
元字符在集合内的行为:
- 在
[...]内部,大多数元字符失去特殊含义,被视为普通字符; - 特别地,
.、*、\在集合中表示其字面值; -
示例:
[\\*.]是一个包含三个字符的集合,可匹配:- 反斜线
\ - 星号
* - 句点
.
- 反斜线
-
区间表示法(使用连字符
-): - 用
[起始-结束]表示连续字符范围; - 常见用法:
[a-d]等价于[abcd][A-Z]匹配任意大写字母[a-zA-Z0-9]匹配任意英文字母或数字(即“字母数字”)
-
注意:若
-出现在集合的末尾(或开头),则不表示区间,而是普通字符;- 例如:
[ad-]仅匹配a、d或-三个字符; - 同理,
[-ad]也匹配-、a、d。
- 例如:
-
补集(否定集合):
- 在集合开头使用
^表示“匹配不在该集合中的任意字符”;- 示例:
[^a-z]匹配任意非小写字母的字符(如大写字母、数字、标点等);
- 示例:
- 特殊情况:若需在补集中包含
]或[,需注意转义或位置;- 示例:
[^][]表示“匹配既不是[也不是]的字符”; - 解析:第一个
]紧跟在^后,作为普通字符加入集合;第二个]是集合的结束符;
- 示例:
- 关键规则:
^只有在集合的第一个位置才表示补集;- 若出现在其他位置(如
[a-z^]),则^被视为普通字符; - 因此
[a-z^]可匹配 26 个小写字母 +^,共 27 个字符。
- 若出现在其他位置(如
总结:字符集合是构建灵活匹配模式的基础,结合区间与补集可高效描述复杂字符类别。
二、组合与锚点¶
1. 单字符正则表达式的组合¶
正则表达式通过将单字符正则表达式按特定规则组合,形成更复杂的匹配模式。PPT 指出:“长的正则表达式由单字符正则表达式构成”。
- 连接(串接)
多个单字符正则表达式依次排列,表示连续匹配。 -
示例:
abc:严格匹配连续三个字符 "a"、"b"、"c";[A-Z].[0-9]:匹配一个大写字母 + 任意字符 + 一个数字,如 "X3y5" 中的 "X3y" 不匹配(因后接 y 非数字),但 "M@7" 可匹配。
-
星号
*(Kleene 闭包)
在基本正则表达式(BRE)中,*是一个量词,作用于其前一个单字符正则表达式,表示该单元出现 0 次或多次。 - 关键特性:
*不是独立字符,必须紧跟在可重复单元之后;- 匹配是贪婪的(尽可能多匹配)。
-
PPT 提供的典型示例:
12*4:- 匹配
14(2出现 0 次), - 匹配
124(2出现 1 次), - 匹配
1224(2出现 2 次), - 不匹配
1234(因2*只能重复2,不能匹配3)。 [A-Z][0-9]*:- 匹配
A(数字部分出现 0 次), - 匹配
A1、C45、D768等(一个大写字母后跟任意长度数字串), - 不匹配
b64512(首字符非大写)、T56t(末尾t非数字且不在模式中)。
-
空格与转义组合示例
PPT 强调正则表达式可处理带空格和转义符号的复杂文本结构: [Cc]hapter *[1-4]:[Cc]匹配 "C" 或 "c";hapter字面匹配;*表示零个或多个空格(注意:空格本身是普通字符,*作用于空格);[1-4]匹配数字 1–4;- 因此可匹配
"Chapter2"(无空格)、"chapter 3"(一个空格)、"Chapter 4"(多个空格)等。
a\[i\]*=*b\[j\]*\\*\*c\[k\]:- 此模式用于匹配类似
a[i]=b[j]\*c[k]的字符串; \[和\]转义为字面方括号;*=表示“零或多个空格 + 等号”(实际应为*= *更准确,但 PPT 原文如此,意在说明*可作用于空格以容忍两侧空白);\\*\*匹配字面\*(即反斜线 + 星号),其中\\为字面\,\*为字面*;- 整体容许多余空格出现在
=和\*两侧。
- 此模式用于匹配类似
注意:此处
*作用对象需明确——在 BRE 中,*仅作用于紧邻的前一个字符或字符类。
2. 锚点:$ 和 ^¶
锚点(Anchors)用于限定匹配发生的位置,而非匹配具体字符。PPT 明确指出 $ 和 ^ 的含义依赖于其在正则表达式中的位置。
- 行尾锚点
$ - 当
$出现在正则表达式的最末尾时,表示匹配行的结束位置;- 示例:
123$仅匹配行尾为 "123" 的行(如 "abc123" 匹配,"123abc" 不匹配); .$匹配行尾的任意单个字符。
- 示例:
-
若
$不在末尾,则被视为普通字符;- 示例:
$123匹配字面字符串 "$123"。
- 示例:
-
行首锚点
^ - 当
^出现在正则表达式的最开头时,表示匹配行的起始位置;- 示例:
^printf仅匹配以 "printf" 开头的行; - 不在行首的 "printf"(如 " printf")不匹配。
- 示例:
-
若
^不在开头,则被视为普通字符;- 示例:
Hel^lo匹配字面字符串 "Hel^lo"。
- 示例:
-
应用示例(vi 命令)
PPT 结合 vi 编辑器展示锚点的实际用途: :1,$s/[0-9]*/xx/g:- 对全文(第 1 行到最后一行
$)执行替换; - 将所有由数字组成的子串(包括空匹配,因
*允许 0 次)替换为 "xx"; - 注意:由于
*允许 0 次,可能在非数字位置也插入 "xx",实际使用中常改用[0-9][0-9]*或[0-9]\+。
- 对全文(第 1 行到最后一行
:10,50s/^ //g:- 对第 10 至 50 行操作;
^表示行首的四个空格;- 替换为空,即删除每行开头的四个空格;
- 此处
^作为行首锚点,确保只删除行首空格,而非行中空格。
关键理解:
^和$是位置断言,不消耗字符,仅限定匹配上下文。
3. 正则表达式扩展(ERE / PCRE)¶
PPT 指出,基本正则表达式(BRE)存在局限,因此发展出扩展正则表达式(ERE) 和 Perl 兼容正则表达式(PCRE),提供更强大的语法。
- 分组与逻辑
- 分组:使用圆括号
()将子表达式组合为一个单元;- 在 BRE 中需转义:
\( ... \); - 在 ERE/PCRE 中直接使用
( ... ); - 示例:
(xy)*可匹配 ""(空)、"xy"、"xyxy" 等。
- 在 BRE 中需转义:
-
逻辑“或”:使用
\|(BRE)或|(ERE/PCRE);- 示例:
(pink\|green)(BRE 写法)匹配 "pink" 或 "green"; - 在
egrep或grep -E中可写作(pink|green)。
- 示例:
-
重复限定符(扩展量词)
相比 BRE 仅有*,ERE/PCRE 引入更多量词: +:匹配前一项 1 次或多次(等价于\{1,\});- 示例:
[0-9]+匹配至少一位的数字串(如 "1"、"123"),不匹配空字符串。
- 示例:
?:匹配前一项 0 次或 1 次(等价于\{0,1\});- 示例:
a?匹配 "" 或 "a"。
- 示例:
-
\{m,n\}:BRE 中的区间量词(ERE 中为{m,n});- 示例:
[1-9][0-9]\{6,8\}: [1-9]:首位非零;[0-9]\{6,8\}:后续 6 到 8 位数字;- 整体匹配 7 到 9 位的数字(如 "1234567"、"987654321")。
- 示例:
-
预定义字符类
为简化常用字符集合,引入命名类别: - POSIX 标准(适用于
grep、sed等):[[:xdigit:]]:匹配十六进制数字(0-9、a-f、A-F);- 其他如
[[:digit:]]、[[:alpha:]]等。
-
PCRE 扩展(如
grep -P):\d:等价于[0-9](数字);\D:等价于[^0-9](非数字);- 还有
\w(单词字符)、\s(空白符)等。
-
高级锚点
PPT 提及更灵活的锚定需求,超越^/$: - 例如:“寻找一个数字串,但要求这个数字串不许出现在‘合计’两个字之后”;
- 这类需求通常需要负向先行断言(negative lookbehind),如
(?<!合计)\d+(PCRE 支持); - 传统 BRE/ERE 无法直接实现,需结合上下文处理或多步过滤。
总结:ERE 和 PCRE 极大增强了正则表达式的表达能力,使复杂文本处理成为可能,但需注意工具兼容性(如
grep默认 BRE,需-E或-P启用扩展)。
三、三个与正则表达式相关的处理命令¶
1. grep:在文件中查找字符串(筛选)¶
grep(Global Regular Expression Print)是 Linux 中最常用的文本搜索工具,其核心功能是根据正则表达式模式筛选包含匹配内容的行。
- 基本语法:
Bash - 若未指定文件,则从标准输入读取;
-
默认输出匹配的整行内容。
-
三种变体及其正则支持:
grep:默认使用基本正则表达式(BRE),元字符如(、)、{、}、|需转义才具特殊含义。egrep(或grep -E):使用扩展正则表达式(ERE),支持()、|、+、?等无需转义,模式描述更灵活。-
fgrep(或grep -F):固定字符串搜索(Fixed strings),不解释任何正则元字符,按字面字符串匹配,速度更快,适用于纯文本关键词查找;底层可使用 KMP、AC 等高效字符串匹配算法加速。 -
常用选项(PPT 明确列出):
-n:在输出前加上行号;- 示例:
grep -n main *.c→ 查找所有.c文件中含 "main" 的行,并显示行号;多文件时同时显示文件名。
- 示例:
-v:反向匹配,输出不包含模式的行;- 示例:
grep -v '[Dd]isable' dev.stat > dev.active→ 过滤掉含 "Disable" 或 "disable" 的行,生成新文件。
- 示例:
-i:忽略大小写;- 示例:
grep -i richard telnos→ 匹配 "Richard"、"RICHARD"、"richard" 等。
- 示例:
-E/-G/-P:-E:启用 ERE(等价于egrep);-G:显式使用 BRE(默认行为);-P:启用 Perl 兼容正则表达式(PCRE),支持\d、\w、前瞻断言等高级特性(需系统支持,可通过man pcresyntax查阅语法)。
注意:PPT 强调
grep是“筛选”工具,侧重于选择性输出,而非修改内容。
2. sed:流编辑(文本加工)¶
sed(Stream Editor)是一种非交互式流编辑器,逐行读取输入,对每行应用编辑命令,输出结果。它擅长批量文本替换、删除、插入等加工操作。
- 基本用法:
- 单命令:
sed '命令' 文件名列表 - 多命令:
sed -e '命令1' -e '命令2' 文件sed -f 命令文件 文件(从文件读取多条命令)
-
可与管道结合:
tail -f pppd.log | sed 's/.../.../g' -
核心命令:替换(s 命令)
语法:s/pattern/replacement/flags - 模式(pattern):支持 BRE(部分实现支持 ERE);
- 示例:
s/145\.37\.23\.26/桥西/g→ 将 IP 地址替换为地名(注意.需转义); - PPT 示例:
cat pm.txt | sed 's/\[[^][]*\]//g'→ 删除所有[...]形式的标记(如[注释])。
- 示例:
-
子表达式捕获与回溯引用:
- 在 BRE 中,用
\(...\)定义子组; - 在替换串中用
\1,\2, ... 引用匹配内容(\0或&表示整个匹配); - 示例:
Bash - 将
04-26-1997转换为1997.04.26; \1=月,\2=日,\3=年。- 批量重命名示例:
Bash - 输入:
[快视频...]-69.rmvb - 输出:
mv "[快视频...]-69.rmvb" "第69集.rmvb" &(或\0)代表整个匹配的字符串。
- 在 BRE 中,用
-
实际应用场景(PPT 提供):
- 实时日志脱敏/转换:
tail -f pppd.log | sed -f sed.cmd,其中sed.cmd包含多条 IP 替换规则。 - 结构化数据提取与格式化:如日期转换、文件名重构等。
关键点:
sed是“加工”工具,直接修改文本内容,常用于自动化脚本。
3. awk:逐行扫描的文本处理语言(筛选+加工)¶
awk 不仅是一个命令,更是一门专为文本处理设计的小型编程语言,兼具筛选与加工能力。
- 基本结构:
条件 { 动作 } awk自动对输入的每一行执行程序;- 若满足“条件”,则执行“动作”;
-
可定义多个语句块,用空格或分号分隔。
-
自动行为与内置变量:
- 记录(Record):默认每行是一个记录;
- 内置变量
NR:当前记录号(即行号)。
- 内置变量
-
域(Field):每行按空白(空格/制表符)分割为多个字段;
$1:第一个字段,$2:第二个字段,……;$0:整行内容;- 字段数量存于
NF。
-
条件描述方式:
- 关系与逻辑运算符(类似 C 语言):
<,<=,==,!=,>,>=&&(与)、||(或)、!(非)
- 正则表达式匹配:
/regexpr/:若整行包含该模式,则执行动作;$2 ~ /[1-9][0-9]*/:判断第二字段是否匹配“非零开头的数字串”;!~表示不匹配。
-
特殊条件块:
BEGIN { ... }:在读取任何输入前执行(常用于初始化);END { ... }:处理完所有行后执行(常用于汇总输出);- 无条件块:对所有行执行。
-
动作描述方式:
- 变量赋值与算术运算:支持
+,-,*,/,%等; - 流程控制:
if (...) { ... }、for (i=1; i<=NF; i++) { ... }; -
输出:
print var1, var2, ...:输出变量,以空格分隔;printf("格式", var1, var2, ...):格式化输出(类似 C 语言)。
-
典型应用场景(PPT 暗示):
- 从结构化日志中提取特定列;
- 对数值字段求和、计数、平均;
- 结合正则过滤并重组输出格式(如生成 CSV)。
总结:
awk是“筛选+加工”的全能工具,适合处理列式数据和复杂逻辑。
四、正则表达式应用实例(选讲)¶
PPT 通过三个综合性案例展示正则表达式在真实场景中的威力。
例1:累加作品发行量¶
- 任务:从一段非结构化中文文本(如作家简介)中提取各作品的发行量并求和。
- 文本特征:包含“畅销200多万册”、“110多万册”等描述。
- 解决思路:
- 使用正则表达式匹配数字模式,如
[0-9]+或[0-9]+万?; - 提取数字部分(可能需处理“万”单位);
- 用
awk或脚本累加数值。 - 技术要点:正则匹配 + 数值提取 + 累加计算。
例2:统计英文小说高频词¶
- 任务:给定英文小说文本文件,找出出现频率最高的 200 个单词。
- 解决步骤:
- 预处理:转为小写(
tr 'A-Z' 'a-z'),去除标点(用正则替换非字母字符为空格); - 切分单词:利用
awk按空白分割字段; - 计数:用
awk哈希表统计每个单词出现次数; - 排序取前200:
sort -nr | head -200 - 技术要点:正则清洗 + 字段分割 + 哈希计数 + 排序。
例3:绘制游客流量随时间变化的曲线¶
- 任务:从景区流量网页中提取数据,生成 CSV 供绘图。
- 流程:
- 获取数据:
wget http://.../flow.php -O 180801.html - 解析 HTML:用正则表达式提取景点名、时间、人数;
- 示例目标格式:
故宫,2018-08-01 9:00,1.26
- 示例目标格式:
- 结构化输出:通过
sed或awk提取并重组为 CSV 行;- 需匹配如
<div>故宫</div><time>09:00</time><num>1.26</num>等结构(PPT 未给 HTML 片段,但强调正则提取能力)。
- 需匹配如
- 输出要求:严格格式化的
.csv文件,便于 Excel 或 Python 绘图。 - 技术要点:网络抓取 + 正则提取 + 格式标准化。
总结:这三个例子分别体现了正则表达式在信息抽取、文本分析和数据工程中的核心作用,展示了
grep/sed/awk组合的强大生产力。