经验
学习方法
学习正则表达式需要投入时间和理解: 正则表达式的学习需要花一些时间,但是通过深入理解相关概念和方法,可以使学习过程更加高效。
学习方法: 推荐通过分解问题、分析子问题、使用字符组、多选结构、量词、锚点等工具来解决问题,从而建立深刻的概念模型。
调试技巧: 正则表达式难以调试,但可以通过逐步分解问题和理解每个部分的匹配规则来解决复杂的正则表达式问题。
克制使用正则表达式: 避免滥用正则表达式,尤其是编写难以理解的复杂表达式。在可以使用普通字符串处理或多个简单正则表达式的情况下,优先选择更清晰的方法。
编写方案
正则断言(Assertion): 正则断言用于对匹配到的文本位置进行要求,而不是文本内容本身。常见的断言包括单词边界、行的开始或结束以及环视。
单词边界: 通过 \b
可以匹配单词的边界,用于确保匹配的文本是单独的单词而不是部分匹配。
行的开始和结束: 使用 ^
和 $
可以匹配文本的行首和行尾,用于确保匹配发生在特定位置。
环视: 环视分为肯定逆向环视、否定逆向环视、肯定顺序环视和否定顺序环视,用于在匹配文本的前面或后面添加条件约束。
环视与子组的区别: 环视不会保存匹配的文本内容,而子组会保存,因此要根据需求选择使用环视或子组。
总的来说,正则表达式和断言是强大的文本处理工具,但要谨慎使用,同时理解其原理和用法对于解决复杂的文本匹配问题至关重要
转义字符
转义字符(Escape Character):
- 转义字符是一种特殊字符,通常放在字符序列中,用于表示后续字符的特殊含义。
- 转义字符的含义通常由上下文确定,它标志着转义序列的开始。
字符串中的转义字符:
- 在编程中,通常需要在字符串中使用转义字符来处理特殊字符,如双引号内再次出现双引号。
- 例如:
str = "How do you spell the word \"regex\"?"
正则表达式中的转义字符:
- 在正则表达式中,反斜杠
\
用于表示转义字符。 - 例如,
\d
表示单个数字,但要表示字面的\d
需要写成\\d
。
使用原生字符串:
- 在 Python 中,可以使用原生字符串,即在正则表达式前面加上小写字母 'r',以减少字符串的转义。
- 例如:
re.findall(r'\\', 'a*b+c?\\d123d\\')
元字符的转义:
- 元字符如星号(*)、加号(+)、问号(?)本身需要转义才能表示其字面含义。
- 例如:
re.findall('\+', '+')
括号的转义:
- 在正则表达式中,圆括号
()
需要两侧都进行转义,以表示字面含义。 - 例如:
re.findall('\(\)\[]\{}', '()[]{}')
使用转义函数消除特殊含义:
- 编程语言通常提供转义函数,可以将输入文本中的特殊字符转义为普通字符,以便正则匹配。
- 例如:
re.escape('\d')
字符组中的转义:
- 在字符组
[]
中,需要注意三种情况需要转义:脱字符^
、中划线-
、右括号]
。 - 通常,其他元字符在字符组中不需要转义。
总结:
- 转义在正则表达式中可以涉及字符串转义和正则转义两个步骤。
- 元字符的转义一般在前面加反斜杠,方括号和花括号的转义一般只需转义开括号,但圆括号需要两侧都转义。
- 字符组中只有三种情况需要转义。
流派和发展
正则表达式的起源可以追溯到早期神经系统研究,20世纪40年代,Warren McCulloch 和 Walter Pitts提出了用数学方式描述神经网络的方法。1956年,Stephen Kleene发表了一篇论文,介绍了“正则集合(Regular Sets)”的符号,标志着正则表达式的起源。
Unix之父Ken Thompson于1968年发表了文章《正则表达式搜索算法》,将正则表达式引入了Unix系统的编辑器qed和ed,随后移植到了grep等文本搜索工具中,使得正则表达式广泛应用于Unix系统及类Unix系统(如macOS、Linux)。
在1986年,POSIX开始标准化正则表达式,包括了BRE(Basic Regular Expression)和ERE(Extended Regular Expression)两种标准。BRE较为基本,不支持问号、加号和多选分支,需要对某些字符进行转义;ERE则更为扩展,支持了问号、加号和多选分支,同时对某些字符的转义更宽松。
GNU在实现POSIX标准时进行了扩展,GNU BRE支持了+和?,但需要转义,支持多选分支,同样需要转义;GNU ERE也支持反向引用。
正则表达式的两大流派是POSIX流派和PCRE流派。POSIX流派主要遵循POSIX标准,而PCRE流派主要指兼容Perl语言的正则表达式。
PCRE流派的主要特点是支持\d、\w、\s等字符组的简写方式,广泛用于多种编程语言。
PCRE流派在不同编程语言和工具中的兼容度有差异,分为直接兼容和间接兼容两种情况。直接兼容的语言或工具包括Perl、PHP preg、PCRE库等,间接兼容的包括Java系、Python系、JavaScript系、.Net系等。
在类Unix平台上,使用grep、sed、vi/vim等工具需要了解它们使用的正则标准,例如grep和sed属于BRE标准,而egrep和awk属于ERE标准。通过man命令可以查看工具的说明,以确定所使用的正则标准。
为了在Linux中使用正则表达式,可以使用不同标准,如BRE、ERE和PCRE,来查找包含ftp、http或https的行,具体命令可根据所使用的标准进行调整。
Unicode中的正则
Unicode 基础知识:
- Unicode 是计算机科学领域的标准,用于整理和编码世界上大部分文字,使计算机能够呈现和处理文字。
- Unicode 字符分为 17 个平面,每个平面包含 65536 个码值,大多数字符属于第 0 平面(BMP 平面),其他平面称为补充平面。
- 目前最新的 Unicode 版本是 2020 年 3 月 10 日发布的 13.0.0 版本,包含超过 14 万个字符。
- Unicode 使用多字节编码方式,其中最常见的编码是 UTF-8,它采用变长编码,一个字符占 1 到 4 个字节不等,兼容 ASCII 编码。
Unicode 中的正则:
- 使用正则处理 Unicode 编码文本时,需要特别注意编码问题。
- 在 Python 中,建议使用 Python 3,并始终使用 Unicode 编码。在 Python 2 中,要明确使用 Unicode 编码,可以在字符串前面加上 'u'。
- 不同操作系统和环境下,正则可能会以不同的编码方式工作,例如,在 macOS/Linux 下通常使用 UTF-8,而在 Windows 下可能使用 GBK。
- 未正确使用 Unicode 编码可能导致正则匹配行为异常。
点号匹配:
- 点号通常用于匹配除了换行符以外的任何字符。
- 在 Unicode 中,点号的匹配情况可能因编程语言和环境而异。建议根据具体情况进行测试和验证。
字符组匹配:
- Unicode 字符组如\d(数字)、\w(字母、下划线、数字)、\s(空白符)等仍适用,但需要根据语言和环境测试其支持程度。
- Unicode 属性(Unicode Categories、Unicode Blocks、Unicode Scripts)可以通过\p{属性}在正则中使用。
表情符号:
- 表情符号是图片字符,通常需要多字节编码,有些甚至超过 BMP 平面。
- 处理表情符号时不建议使用正则,推荐使用专用的库,以提高代码可读性和维护性。
总结:
- 了解 Unicode 编码的基础知识,包括UTF-8 编码、字符分类和编码属性。
- 注意在正则中处理 Unicode 文本时的编码问题,始终使用 Unicode 编码。
- 对于复杂的字符(如表情符号),不推荐使用正则,建议使用专用库处理。
在编辑器中使用正则
编辑器功能:
- 在常见编辑器和IDE中,使用键盘的左右箭头可移动光标,按住Shift键选择文本。
- 使用Alt(macOS上是Option)键加箭头键,可以按块移动光标,更快选择文本。
- 多焦点编辑功能允许同时编辑多个相同文本,如JSON字符串,提高效率。
- 竖向编辑功能可在多行文本中进行竖向选择和编辑,加快处理速度。
在编辑器中使用正则:
- 使用编辑器(例如Sublime Text)进行正则文本操作可提高文本处理效率。
- 正则可用于内容提取、替换、验证和切割。
- 正则提取内容是从文本中抽取子集,替换内容是改变原文本。
- 正则内容验证要求整个文本匹配,匹配次数为一次。
- 正则切割内容是根据正则表达式进行文本分割。
内容提取示例:
- 使用Sublime Text示例,提取文本中的邮箱地址。
- 使用正则(\S+@\S+.\S+)匹配邮箱,然后通过引用子组和替换操作获得结果。
内容替换示例:
- 使用Sublime Text示例,将匹配的邮箱地址去除尾部分号,并添加邮箱类型。
- 使用子组和引用进行替换操作,同时保留原文本的格式。
内容验证和切割:
- 正则可用于内容验证,要求整个文本完全匹配。
- 正则也可用于内容切割,根据正则表达式进行文本分割。
- 提供了编辑器的界面示例以进行正则操作。
总结:
- 了解了编辑器中的高效文本处理功能,如多焦点编辑、竖向编辑等。
- 学习了在编辑器中使用正则进行内容提取、替换、验证和切割等操作。
- 正则可与其他编辑器功能相结合,提高文本处理效率。
在编程语言中使用正则
1. 校验文本内容:
- 在 Python 中,你可以使用
re
包中的re.match
或re.search
方法进行文本校验。re.match
从文本开头匹配,而re.search
在文本中查找子串。 - 通常建议在正则表达式中使用
\A
和\Z
来确保完全匹配,避免部分匹配。在多行模式下,^
和$
的行为可能会发生变化。
2. 提取文本内容:
- 在 Python 中,你可以使用
re.findall
方法来提取匹配的文本内容,或者使用re.finditer
方法以迭代器方式处理匹配结果。 - 在 Go 语言中,你可以使用
regexp
包的FindAllString
和FindAllStringSubmatch
方法来提取文本内容。
3. 替换文本内容:
- 在 Python 中,使用
re.sub
和re.subn
方法进行文本替换。re.subn
还会返回替换的次数。 - 在 Go 语言中,使用
regexp
包的ReplaceAllString
方法进行替换。注意${num}
表示子组。
4. 切割文本内容:
- 在 Python 中,使用
re.split
方法进行文本切割,传入正则表达式作为分隔符。 - 在 Go 语言中,也使用
regexp
包的Split
方法进行切割,传入分隔正则。
关于 JavaScript 和 Java:
- JavaScript 中,使用
split
方法进行文本切割,需要使用g
模式来切割所有匹配。 - Java 中,使用
split
方法进行文本切割,也需要传入正则表达式作为分隔符。
在实际应用中,正则表达式可以大大提高文本处理的效率和精度,但也需要小心处理,确保正则表达式的准确性和性能。
原理和优化
有穷状态自动机: 正则表达式之所以能够处理复杂文本是因为采用了有穷状态自动机(finite automaton)的概念。有穷状态自动机包含有限个状态,根据输入条件可以在不同状态之间转移,最终达到终止状态。
DFA 和 NFA: 正则引擎主要有两种实现方式,即确定性有穷自动机(DFA)和非确定性有穷自动机(NFA)。NFA又分为传统的NFA和POSIX NFA。
正则的匹配过程: 在编程中,我们经常会编译正则表达式以提高效率。这个编译过程实际上是生成正则表达式对应的自动机,然后使用该自动机与字符串进行匹配。
DFA 和 NFA 的工作机制: DFA 引擎以文本为主导,先查找文本,再匹配正则表达式。而NFA引擎以正则表达式为主导,先查找正则,再匹配文本。这两种引擎工作方式截然不同。
NFA 回溯: NFA 引擎使用贪婪匹配回溯算法,当存在量词或多选分支结构时,可能会发生回溯。这会导致在同一部分字符串上进行多次匹配尝试,影响性能。
优化建议: 提前编译好正则、准确表示匹配范围、提取公共部分、出现可能性大的放左边、只在必要时使用子组、警惕嵌套的子组重复、避免不同分支重复匹配等方法可以优化正则表达式的性能。
测试性能的方法: 使用工具或代码测试正则表达式的性能,了解匹配次数和速度。
正则表达式优化示例: 示例演示了如何优化正则表达式,避免回溯和提高性能。
处理方法
问题处理思路
使用正则表达式处理问题的基本思路是将问题分解成多个小问题,每个小问题采用不同的正则表达式来解决。这包括在某个位置上可能有多个字符时使用字符组,某个位置上有多个字符串时使用多选结构,出现的次数不确定时使用量词,以及对出现的位置有要求时使用锚点锁定位置。
如果要查找的内容中不能包含某些字符,可以使用中括号来排除字符组,例如使用[^aeiou]
来表示非元音字母。
如果要求内容中不能包含某个子串,例如密码不能有连续两个数字出现,可以使用环视(lookahead)来解决,确保每个字符的后面都不能是两个数字。示例中使用Python的正则表达式:re.match(r'^((?!\d\d)\w){6}$', '11abcd')
。
匹配数字
- 数字可以使用
\d
或[0-9]
来表示。 - 连续的多个数字可以使用
\d+
或[0-9]+
。 - 匹配固定位数的数字,可以使用
\d{n}
。 - 匹配至少n位数字,可以使用
\d{n,}
。 - 匹配m到n位数字,可以使用
\d{m,n}
。
常见问题及解决方案
匹配正数、负数和小数
- 正则表达式可以用来匹配正数、负数和小数,例如
[-+]?\d+(?:\.\d+)?
。
十六进制数
- 十六进制数字包括0-9和a-f(或A-F),可以使用正则表达式
[0-9A-Fa-f]+
来匹配。
手机号码
- 手机号码的正则表达式可以根据号段进行匹配,示例中给出了一些可能的匹配规则,包括精确到前两位、前三位、前四位的手机号码。
身份证号码
- 中国身份证号码分为一代(15位)和二代(18位),可以使用正则表达式来匹配这两种格式,示例中给出了相应的正则表达式。
邮政编码
- 邮政编码通常为6位数字,可以使用
\d{6}
来匹配。需要添加断言来确保不匹配其他6位数字。
腾讯QQ号码
- 腾讯QQ号码可以使用
[1-9][0-9]{4,9}
来表示,要求首位是1-9,后面跟4-9位数字。
中文字符
- 中文字符可以使用Unicode范围
\u4E00-\u9FFF
来匹配,不同编程语言有不同的表示方式。
IPv4地址
- IPv4地址可以使用正则表达式来匹配,示例中给出了详细的正则表达式,包括对每个数字部分的精确匹配。
日期和时间
- 日期和时间的格式可以使用正则表达式来匹配,示例中给出了匹配日期和时间的正则表达式,包括考虑月份和日期的范围。
邮箱
- 邮箱的正则表达式可以包括用户名部分和主机名部分的匹配规则,示例中给出了一个简化的匹配规则。
网页标签
- 配对出现的网页标签可以使用正则表达式来匹配,示例中以匹配
标签为例,注意不区分大小写。
总结
正则表达式处理问题的基本思路,包括将问题分解成小问题,并逐步构建正则表达式,注意添加断言以确保匹配或提取的准确性。正则表达式不是解决所有问题的唯一方法,应根据具体情况选择使用正则表达式或其他方法。
使用机器人加强工作
AI 聊天机器人的作用:ChatGPT 类 AI 聊天机器人在编程中可以作为一个有用的工具,帮助用户完成代码编写、调试和理解。它提供了方便的自然语言接口,可以用于各种编程任务。
AI 不会取代程序员:虽然有人担心 AI 聊天机器人可能会取代程序员,但不必太担心。这类 AI 工具提供的只是知识片段,有时会出现错误,仍需要用户的判断和验证。它们更像是老师或知识渊博的朋友,可以帮助解决问题,但不会完全替代程序员。
AI 聊天机器人的应用:在正则表达式方面,ChatGPT 可以提供以下帮助:
-
提供参考示例:用户可以向 AI 聊天机器人询问关于正则表达式的示例,它会提供与用户输入相关的正则表达式示例,以及实现代码的示例。
-
自动检测错误:AI 聊天机器人可以检测正则表达式中的常见错误,并提供相应的建议,帮助用户找到并纠正问题。
-
解释正则表达式:机器人可以解释正则表达式的各个部分的功能,并提供相关文档和资源,使用户更容易理解和使用正则表达式。
-
练习和测试:用户可以利用 AI 聊天机器人进行练习和测试,以提高对正则表达式的理解和熟练度。
AI 聊天机器人的不足:尽管 AI 聊天机器人在帮助学习正则表达式方面有很大的潜力,但它们仍然存在一些不足之处。它们可能会给出不完美的答案,有时甚至是错误的答案。因此,用户需要自己验证和理解,不能完全依赖 AI。
总结:AI 智能聊天机器人为知识检索和编程提供了便利,可以用作向导,提高工作效率。它们可以在正则表达式方面提供有用的帮助,但用户仍需保持独立思考和学习的能力。