2、正则表达式入门:正则表达式的基本语法,元字符与转义,Python re模块的使用,常用正则模式。

好,咱们进入正题。正则表达式,说白了就是一套用来描述文本模式的规则。很多同学一看到正则就头大,觉得像天书。我刚开始学的时候也这样,满屏的符号看得眼花。但后来我发现,只要抓住几个核心概念,这东西其实挺顺手的。

你想想看,做NLP最常干的事是什么?清洗数据。而清洗数据最离不开的就是正则。我敢说,你项目中80%的文本处理需求,用正则都能搞定。剩下的20%,要么是需求太奇葩,要么是你正则没写对。

2.1 正则表达式的基本语法

正则表达式由普通字符和元字符组成。普通字符就是字母、数字这些,匹配自己。元字符才是真正的灵魂。

举个例子,abc 这个正则,就匹配字符串中的 "abc"。但如果你写成 a.c,这里的点号 . 就是元字符,它匹配任意一个字符。所以 a.c 能匹配 "abc"、"aac"、"a1c" 等等。

我个人习惯把正则拆成三部分来看:

  • 字面量:就是普通字符,匹配自身
  • 元字符:有特殊含义的符号
  • 量词:控制前面字符出现的次数

嗯,这里要注意,正则默认是区分大小写的。如果你要忽略大小写,得加个标志位,后面会讲。

2.2 元字符与转义

元字符是正则的基石。我把最常用的几个列出来,你记牢了,基本就能应付大部分场景。

元字符 含义 示例
. 匹配任意单个字符(除换行符) a.b 匹配 "aab", "a1b"
^ 匹配字符串开头 ^abc 匹配以 "abc" 开头的字符串
$ 匹配字符串结尾 abc$ 匹配以 "abc" 结尾的字符串
* 匹配前一个字符0次或多次 ab*c 匹配 "ac", "abc", "abbc"
+ 匹配前一个字符1次或多次 ab+c 匹配 "abc", "abbc",不匹配 "ac"
? 匹配前一个字符0次或1次 ab?c 匹配 "ac", "abc"
[] 字符集,匹配括号内任意一个字符 [abc] 匹配 "a", "b", "c"
() 分组,将多个字符当作一个整体 (ab)+ 匹配 "ab", "abab"
| 或操作 a|b 匹配 "a" 或 "b"
\ 转义字符 \. 匹配字面量点号

说到转义,我得提一嘴。我在项目中遇到过好几次,有人想匹配一个点号,直接写了个 .,结果匹配了一大堆东西。为什么?因为点号是元字符啊!正确的写法是 \.。记住,想匹配元字符本身,前面加个反斜杠就行。

避坑指南:我曾经在清洗日志文件时,想匹配IP地址中的点号,直接用了 .,结果把 "192.168.1.1" 匹配成了 "192 168 1 1" 四个独立的部分。后来才意识到,点号没转义。所以,凡是遇到 .*+ 这些符号要匹配字面量时,一定记得加反斜杠。

2.3 Python re模块的使用

Python 的 re 模块是处理正则的标准库。我常用的就四个函数:matchsearchfindallsub。说白了,一个负责开头匹配,一个负责全文搜索,一个负责找所有,一个负责替换。

先看个最简单的例子:

import re

text = "我的手机号是13812345678,请记一下。"

# 用 search 查找手机号
pattern = r'1[3-9]\d{9}'
result = re.search(pattern, text)
if result:
    print(result.group())  # 输出:13812345678

这里我用了原始字符串 r''。为什么?因为正则里反斜杠太多了,不用原始字符串的话,你得写两个反斜杠才能表示一个。比如 \d 在普通字符串里得写成 \\d,多麻烦。所以,我建议你写正则时一律用 r''

再来看 findall,这个在数据清洗中特别常用:

import re

text = "邮箱1: alice@example.com, 邮箱2: bob@test.org"
pattern = r'\w+@\w+\.\w+'
emails = re.findall(pattern, text)
print(emails)  # 输出:['alice@example.com', 'bob@test.org']

你想想看,如果让你手动从几百行日志里提取邮箱,那得累死。但用正则,一行代码就搞定了。

最后说下 sub,替换函数。我在清洗用户评论时经常用:

import re

text = "这个商品真TM垃圾,操蛋的客服!"
# 替换脏话
pattern = r'TM|操蛋|垃圾'
clean_text = re.sub(pattern, '***', text)
print(clean_text)  # 输出:这个商品真***,***的客服!
小技巧re.compile 可以预编译正则模式。如果你要多次使用同一个正则,先编译再调用,性能会好很多。我在处理百万级数据时,编译后的速度能快30%左右。

2.4 常用正则模式

下面这些模式,是我在实际项目中反复用到的。你直接拿去用就行,不用每次都自己写。

用途 正则模式 说明
匹配手机号 1[3-9]\d{9} 11位数字,以1开头,第二位3-9
匹配邮箱 \w+@\w+\.\w+ 简单的邮箱格式
匹配URL https?://[^\s]+ http或https开头的链接
匹配IP地址 \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} IPv4地址,注意没做范围校验
匹配中文 [\u4e00-\u9fa5]+ 匹配中文字符
匹配空白行 ^\s*$ 空行或只包含空白字符的行
匹配日期 \d{4}-\d{2}-\d{2} 如 2024-01-15

这里有个坑,我必须要说。IP地址的正则 \d{1,3} 虽然能匹配 "999",但这不是合法的IP。为什么?因为IP地址每段最大是255。如果你要做严格的校验,得用更复杂的模式。不过,在数据清洗阶段,我们通常只做初步提取,后续再验证。

再分享一个我常用的组合拳:

import re

def clean_text(text):
    # 1. 去除HTML标签
    text = re.sub(r'<[^>]+>', '', text)
    # 2. 去除URL
    text = re.sub(r'https?://[^\s]+', '', text)
    # 3. 去除特殊符号,保留中文、英文、数字和空格
    text = re.sub(r'[^\w\u4e00-\u9fa5\s]', '', text)
    # 4. 合并多个空格
    text = re.sub(r'\s+', ' ', text)
    return text.strip()

sample = "这是一段<b>测试</b>文本,详情请访问 https://example.com !@#¥"
print(clean_text(sample))
# 输出:这是一段测试文本,详情请访问

你看,几行正则就把文本清洗得干干净净。这就是正则的魅力。

核心要点:正则表达式是NLP数据清洗的瑞士军刀。掌握元字符、量词和分组,配合Python的re模块,你能处理90%以上的文本清洗需求。记住,先写小模式测试,再组合成大模式,这样不容易出错。

好了,正则入门就讲这么多。下一节我们会深入讨论更高级的模式,比如贪婪与非贪婪匹配、前瞻与后顾断言。这些东西在复杂文本处理中特别有用。到时候我会结合我踩过的坑,给你讲透。