LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

正则表达式详解

admin
2025年11月29日 11:33 本文热度 393

1 正则是个啥

维基百科说:

正则表达式(英语:Regular Expression,常简写为 regex、regexp 或 RE),又称正则表示式、正则表示法、规则表达式、常规表示法,是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。在很多文本编辑器里,正则表达式通常被用来检索、替换那些匹配某个模式的文本。

正则表达式 译于 Regular ExpressionRegular 此处为规则规律的意思,亦即符合某种模式、规律的表达式。

使用 Linux 比较多的朋友应该有使用过 grepgrepg/re/p)是正则表达式应用的一个典型工具,含义是:Global search for Regular Expression and Print matching lines

说这么多,正则表达式可能是一个可意会难言传的名词,通常用来做字符串查找或者替换。如果还觉得难理解,那把它当隔壁老王也是可以的——虽然不知道为啥这个邻居叫老王,但大家说老王的时候知道是这个邻居就行了,反正正则表达式就这么个东西。

2 正则的理论

这一节用来提升一下高度,期望通过这一节可以更透彻地理解正则表达式(不理解也没关系的,不懂发动机原理并不妨碍咱们开车)。

正则表达式可以用形式化语言理论的方式来表达。正则表达式由常量和算子组成,它们分别表示字符串的集合和在这些集合上的运算。给定有限字母表 

定义了下列常量:

  • 空集: 表示一个空集合。
  • 空串: 表示仅包含一个“不含任何字符、长度为 0 的字符串”的集合。
  • 文字字符: 表示仅包含一个元素  的集合 

定义了下列运算:

  • 串接: 表示集合 ,这里的  表示将  和  两个字符串按顺序连接。例如:。(还记得乘法分配律吗?)
  • 选择: 表示  和  的并集。例如:。(参考 Python 中 List 的方法 extend。)
  • 克莱尼 (Kleene) 星号:  表示包含  且在字符串串接运算下闭合的  的最小超集。这是可以通过  中零或有限个字符串的串接得到所有字符串的集合。例如:

上述常量和算子形成了克莱尼代数。

也有文档使用对选择使用符号  或  替代竖线。

为了避免括号,假定 Kleene 星号有最高优先级,接着是串接,接着是并集。如果没有歧义则可以省略括号。例如:(ab)c 可以写为 abc 而 a|(b(c*)) 可以写为 a|bc*。

例子:

  •  表示 
  •  表示包括空串和任意数目个  或  字符组成的所有字符串的集合:
  •  表示开始于一个  接着零或多个  和最后一个可选的  组成的字符串的集合:

为了使表达式更简洁,正则表达式也定义了 ? 和 +aa* 等于 a+,表示 a 出现至少一次;而 (a|ε) 等于 a?,表示 a 出现 1 次或不出现。(这里隐藏了这几个量词的记忆方法,如果后面记不住,可以回到这里。)

3 正则的语法

好了,长篇大论那么多,终于进入“正题”。

我有个习惯,难以说好坏,虽然有好处,但也带来了好多困扰:我做事情的时候喜欢想着森林,对,就是那个只见树木不见森林的森林。好处是,这样去搞一个东西,会把东西梳理清楚,比较透彻;但坏处是,如果这个东西非常复杂,就会导致东西一直搞不下去。

正则语法应该是通用的,至少我用 grep 和 python 的 re 还没发现有啥不同的。不过本文标题为 Python,那就以 re 包为准了。

3.1 基本概念

在介绍语法之前,先介绍几个基本概念。

3.1.1 模式(pattern)

朋友们在查资料的时候可能会经常碰到这个词语,模式(pattern)其实指的就是某个正则表达式。

3.1.2 选择(|

竖线 | 代表选择(即或集),具有最低优先级。例如 gray|grey 可以匹配 grey 或 gray

3.1.3 重复限定符(* 、  、 +

某个字符后的数量限定符(* 、  、 +)用来限定前面这个字符允许出现的个数。最常见的数量限定符包括 * 、  和 +(不加数量限定则代表出现一次且仅出现一次):

  • *:星号表示前方字符出现任意次,例如: ab*c 匹配 acabcabbcabbbc,等等。
  • ?:问号表示前方字符出现 0 次或 1 次,例如:colou?r 匹配 color 和 colour
  • +: 加号表示前方字符至少出现 1 次,例如: ab+c 匹配 abcabbcabbbc 等等,但不匹配 ac
  • {n}: 前方字符出现 n 次。
  • {min,}: 前方字符至少出现 min 次。
  • {,max}: 前方字符最多出现 max 次。
  • {min,max}: 前方字符至少出现 min 次,但不多于 max 次。

这里面, 和 + 可能是比较容易记混的。

首先需要知道一点,* 是原生的,表示任意; 和 + 只是添加进来简化表达式而已。

在这个基础上,一个助记法是:?表示疑问嘛,有没有?有(1)还是没有(0),所以 ? 就表示  / 没有,即 0 次或者 1 次。剩下的 + 就是 至少 1 次了。

实在记不住?没关系,不用就是了!下面举例说明怎么个不用法:

  • 0 或 1 次(?):ab? 可以匹配 a 和 abab{0,1} 可以实现一样的功能!
  • 至少 1 次(+):ab+ 可以匹配 ab 和 abbab{1,} 可以实现一样的功能!

3.1.4 优先级

既然是表达式,当然会涉及到优先级了。正则的优先级是比较简单的:

优先级
符号
最高
\
()
 、 (?:) 、 (?=) 、 []
*
 、 + 、 ? 、 {n} 、 {n,} 、 {n,m}
^
 、 $ 、3.2.2 里的其他特殊字符
更低
串接,即相邻字符连接在一起
最低
|

3.2 Python 中的正则

Python 内置一个正则库 re,使用的时候直接引入即可:

import re

3.2.1 re 中的标志(flags)

标志
含义
re.A
re.ASCII
\w
\W\b\B\d\D\s 和 \S 只匹配 ASCII 字符
re.DEBUG
显示调试信息
re.I
re.IGNORECASE
不区分大小写
re.L
re.LOCALE
\w
\W\b\B 和大小写遵循当前 locale
re.M
re.MULTILINE
^
 匹配每行的行首;$ 匹配每行的行末
re.S
re.DOTALL
.
 匹配所有字符,包括换行
re.X
re.VERBOSE
用于增强正则表达式的可读性,可在表达式中分段和添加注释,也称冗长模式

3.2.2 re 中的特殊字符(组合)

这里给出一张表,可以扫一眼,后面用到记不准的时候再查就可以了。不好理解的看后面示例。

字符
含义
^
匹配字符串的开头
$
匹配字符串尾或者在字符串尾的换行符的前一个字符
.
默认模式下匹配除换行外的所有字符;如果指定 DOTALL 标志则匹配包含换行的所有字符。
*
对它前面的正则式匹配任意次重复, 尽量多的匹配字符串。 ab* 会匹配 aab,或者 a 后面跟随任意个 b
+
匹配 1 到任意次重复。 ab+ 会匹配 a 后面跟随 1 个以上到任意个 b,它不会匹配 a
?
匹配 0 或 1 次重复。 ab? 会匹配 a 或者 ab
*?
+???
非贪婪模式
{m}
匹配 m 次重复,a{6} 将匹配 6 个 a
{m,}
匹配至少 m 次重复,a{4,}b 将匹配 aaaab 或者 1000 个 a 尾随一个 b
{,n}
匹配最多 n 次重复,a{,2}b 将匹配 babaab
{m,n}
匹配 m~n 次重复,a{3,5} 将匹配 3 到 5 个 a
{m,n}?
非贪婪模式,只匹配尽量少的字符次数。比如,对于 aaaaaa, a{3,5} 匹配 5 个 a ,而 a{3,5}? 只匹配 3 个 a
|A|B
, A 和 B 可以是任意正则表达式,创建一个正则表达式,匹配 A 或者 B。注意,这有“短路”特性,即匹配 A 之后就不再匹配 B
\
转义特殊字符,参见后面例子
\数字
匹配数字代表的组合。每个括号是一个组合,组合从 1 开始编号。比如 (.+) \1 匹配 the the 或者 55 55, 但不会匹配 thethe (注意组合后面的空格)。这个特殊序列只能用于匹配前面 99 个组合。
\A
只匹配以字符串开始,单行模式(未设置 MULTILINE 标志时)下与 ^ 一样。
\b
匹配空字符串,但只能在单词开始或结尾的位置。一个单词被定义为一个单词字符的序列。注意,通常 \b 定义为 \w 和 \W 字符之间,或者 \w 和字符串开始 / 结尾的边界, 意思就是 r'\bfoo\b' 匹配 foofoo.(foo)bar foo baz 但不匹配 foobar 或者 foo3
\B
匹配空字符串,但不能在词的开头或者结尾。意思就是 r'py\B' 匹配 pythonpy3py2, 但不匹配 pypy., 或者 py!。 \B 是 \b 的取非
\d
匹配任何十进制数,就是 [0-9]
\D
匹配任何非十进制数字的字符,就是 \d 取非。 如果设置了 ASCII 标志,就相当于 [^0-9]
\s
匹配空白字符,如果 ASCII 被设置,就只匹配 [ \t\n\r\f\v](注意第一个为空格)
\S
匹配非空白字符,如果 ASCII 被设置,就只匹配 [^ \t\n\r\f\v]
\w
匹配字符中的数字和字母和下划线,如果设置了 ASCII 标志,就只匹配 [a-zA-Z0-9_]
\W
匹配非单词字符的字符。这与 \w 正相反。如果使用了 ASCII 旗标,这就等价于 [^a-zA-Z0-9_]
\Z
只匹配字符串尾
[]
用于表示一个字符集合,
1. 字符可以单独列出,比如 [amk] 匹配 a, m, 或者 k
2. 可以表示字符范围,通过用 - 将两个字符连起来。比如 [a-z] 将匹配任何小写 ASCII 字符, [0-5][0-9] 将匹配从 00 到 59 的两位数字
3. 特殊字符在集合中,失去它的特殊含义。比如 [(+*)] 只会匹配这几个文法字符 (, +, *, 或者 )
4. 字符类如 \w 或者 \S 可以匹配的字符由 ASCII 或者 LOCALE 标志决定
5. 不在集合范围内的字符可以通过取反来进行匹配。如果集合首字符是 ^ ,所有在集合内的字符将会被匹配,比如 [^9] 将匹配所有字符,除了 9, [^^] 将匹配所有字符,除了 ^, ^ 如果不在集合首位,则没有特殊含义。
(...)
匹配括号内的任意正则表达式,并标识出组合的开始和结尾。匹配完成后,组合的内容可以被获取,并可以在之后用 \数字 转义序列进行再次匹配。要匹配字符 ( 或者 ), 用 \( 或 \), 或者把它们包含在字符集合里:[(][)]
(?…)
这是个扩展标记法 (一个 ? 跟随 ( 并无含义),? 后面的第一个字符决定了这个构建采用什么样的语法,下面列出所支持的扩展。
(?:…)
匹配在括号内的任何正则表达式,但该分组所匹配的子字符串不能在执行匹配后被获取或是之后在模式中被引用
(?aiLmsux)
a, i, L, m, s, u, x 中的一个或多个) 这个组合匹配一个空字符串;这些字符对正则表达式设置以下标记 re.A (只匹配 ASCII 字符), re.I (忽略大小写), re.L (语言依赖), re.M (多行模式), re.S ( . 匹配全部字符), re.U (Unicode 匹配), 和 re.X (冗长模式)。 这个方法免去了在 re.compile() 中传递 flag 参数。标记应该放在正则表达式开始。
(?aiLmsux-imsx:…)
跟上面的一样复杂,还是直接配置标志算了吧。
(?P<name>…)
(命名组合)类似正则组合,但是匹配到的子串组在外部是通过定义的 name 来获取的。组合名必须是有效的 Python 标识符,并且每个组合名只能用一个正则表达式定义,只能定义一次。一个符号组合同样是一个数字组合,也可以通过数字引用。
(?P=name)
反向引用一个命名组合;它匹配前面那个叫 name 的命名组中匹配到的串一样的字串。
(?#…)
注释,没想到吧,正则表达式也有注释。
(?=…)
匹配  的内容,但是并不消费模式的内容。这个叫做 Positive Lookahead Assertion。
(?!…)
匹配  不符合的情况。这个叫 Negative Lookahead Assertion。
(?<=…)
匹配字符串的当前位置,它的前面匹配  的内容到当前位置。这叫 Positive Lookbehind Assertion。
(?<!…)
匹配当前位置之前不是  的模式。这个叫 Negative Lookbehind Assertion。
(?(id/name)yes-pattern|no-pattern)
如果给定的 id 或 name 存在,将会尝试匹配 yes-pattern ,否则就尝试匹配 no-pattern,no-pattern 可选。

这里介绍了 re 正则里的特殊字符(组合),大部分都是比较好理解的,有些不好理解的也可以不使用。但几个 Assertion 可能是重要但又不好理解的。之所以没写中文,是因为流行的翻译更难理解,索性直接用英文好了。

Assertion 通常译为 断言,用于测试一个假设是否成立。上面表格看着有四种,但可以分为两类:Lookahead 和 Lookbehind

这两个词好理解,就是 向前看 和 向后看 嘛,但这里的前后需要结合阅读顺序。现代阅读方向基本都是从左往右的(如果还有从右往左的请告诉我!), 是按照阅读方向来说的,即:从左向右为前;从右向左为后。

剩下两个词是 Positive 和 Negative,分别是 正的/阳性的/肯定 和 负的/阴性的/否定,代表了 包含 和 不包含

于是这四类断言便可分为:向前看包含向前看 不 包含向后看包含向后看 不 包含。但刚才也说了,这里的前后容易误会,所以不如引入树的两个概念,前驱 和 后继,更好理解,比如:前驱包含前驱不包含后继包含后继不包含,感觉要清晰得多。或者干脆用  代替:左右包含断言 、 左右不包含断言 、 右左包含断言 、 右左包含断言 😂

Anyway,理解了本质,叫什么其实不重要了。现在朋友们应该清楚这四个断言了。如果还有疑惑,就看后面的例子吧。

3.2.3 re 中的方法

TL;DR:大部分时候,认准 findall 就可以了;有其他需求,再看其他的。

  • re.compile(pattern, flags=0)

将正则表达式的模式编译为一个正则表达式对象(正则对象),然后可以通过这个对象的方法,如 match()search() 等,进行匹配。其主要目的,是让多次使用某个模式(正则表达式)的时候更加高效。

  • re.search(pattern, string, flags=0)

扫描整个 字符串 找到匹配模式的第一个位置,并返回一个相应的 匹配对象。如果没有匹配,就返回一个 None。

  • re.match(pattern, string, flags=0)

如果 string 开始的 0 或者多个字符匹配到了正则表达式模式,就返回一个相应的匹配对象。如果没有匹配,就返回 None。

  • re.fullmatch(pattern, string, flags=0)

如果整个 string 匹配到正则表达式模式,就返回一个相应的匹配对象。否则就返回一个 None。

  • re.split(pattern, string, maxsplit=0, flags=0)

用 pattern 分开 string。如果在 pattern 中捕获到括号,那么所有的组里的文字也会包含在列表里。如果 maxsplit 非零,最多进行 maxsplit 次分隔,剩下的字符全部返回到列表的最后一个元素。

  • re.findall(pattern, string, flags=0)
  • re.finditer(pattern, string, flags=0)

这两个方法类似,都是查找所有符合模式的字符串,区别在于,findall 返回一个列表;finditer 返回一个迭代器。

  • re.sub(pattern, repl, string, count=0, flags=0)

返回通过使用 repl 替换在 string 最左边非重叠出现的 pattern 而获得的字符串。 如果模式没有找到,则返回原 string。 repl 可以是字符串或函数;如为字符串,则其中任何反斜杠转义序列都会被处理。 也就是说,\n 会被转换为一个换行符,\r 会被转换为一个回车符,依此类推。 未知的 ASCII 字符转义序列保留在未来使用,会被当作错误来处理。 其他未知转义序列例如 \& 会保持原样。 向后引用,如 \6, 会用模式中第 6 组所匹配到的子字符串来替换。

  • re.subn(pattern, repl, string, count=0, flags=0)

作用与 sub() 相同,但是返回一个元组 (字符串,替换次数)

  • re.escape(pattern)

转义 pattern 中的特殊字符。

  • re.purge()

清除正则表达式的缓存。

4 示例

这部分可能是多数朋友想看的部分了。这里就演示 一招鲜,只使用 findall,如果不能满足需求,再去研究其他方法吧。

:跑本节示例需要引入 re 包:

import re

4.1 3.2.2 示例

先把 3.2.2 里介绍的特殊字符(组合)示例一下。

# ^
re.findall(r"^From""From Here to Eternity")
# ['From']
re.findall(r"^From""Reciting From Memory")
# []

# $
re.findall(r"}$""{block}")
# ['}']
re.findall(r"}$""{block}\n")
# ['}']
re.findall(r"}$""{block} ")
# []

# .
re.findall(r"a.b""axb")
# ['axb']
re.findall(r"a.b""a\nb")
# []
re.findall(r"a.b""a\nb', re.DOTAL")
# ['a\nb']

# *
re.findall(r"ab*""a")
# ['a']
re.findall(r"ab*""ab")
# ['ab']
re.findall(r"ab*""abb")
# ['abb']

# +
re.findall(r"ab+""a")
# []
re.findall(r"ab+""ab")
# ['ab']
re.findall(r"ab+""abb")
# ['abb']

# ?
re.findall(r"ab?""a")
# ['a']
re.findall(r"ab?""ab")
# ['ab']
re.findall(r"ab?""abb")
# ['ab']

# *? +? ??
re.findall(r"ab*?""abb")
# ['a']
re.findall(r"ab+?""abb")
# ['ab']
re.findall(r"ab??""abb")
# ['a']

# {m} {m,} {m,n} {,n} {m,n}?
re.findall(r"ab{1}""abbbbbb")
# ['ab']
re.findall(r"ab{1,}""abbbbbb")
# ['abbbbbb']
re.findall(r"ab{1,3}""abbbbbb")
# ['abbb']
re.findall(r"ab{,3}""abbbbbb")
# ['abbb']
re.findall(r"ab{3,5}?""abbbbbb")
# ['abbb']

# |
re.findall(r"a|ab""abbbbbb")
# ['a']  # 注意前面提到的“短路”特性,匹配 a 之后就不再匹配 ab 了

# \
re.findall(r"a\(b\)""a(b)bbbbb")
# ['a(b)']
s = """a
b"""

re.findall(r"a\nb', ")
# ['a\nb']

# \数字
re.findall(r"(.+) \1""the the")
# ['the']  # 对比下面,更多可查询:python raw string
re.findall("(.+) \1""the the")
# []

# \A
re.findall(r"\Aabc""abcdefg")
# ['abc']
re.findall(r"\Aabc""1abcdefg")
# []

# \b
re.findall(r"\bclass\b""no class at all")
# ['class']
re.findall(r"\bclass\b""the declassified algorithm")
# []

# \B
re.findall(r"\Bclass\B""the declassified algorithm")
# ['class']
re.findall(r"\Bclass\B""no class at all")
# []

# \d 等效为 [0-9]
re.findall(r"\d""123abc")
# ['1', '2', '3']
re.findall(r"\d""abc")
# []

# \D 等效为 [^0-9]
re.findall(r"\D""abc")
# ['a', 'b', 'c']
re.findall(r"\D""123")
# []

# \s 等效为 [ \t\n\r\f\v]
re.findall(r"a\sb""a b")
# ['a b']
re.findall(r"a\sb""ab")
# []

# \S 等效为 [^ \t\n\r\f\v]
re.findall(r"a\Sb""a&b")
# ['a&b']
re.findall(r"a\Sb""a b")
# []

# \w 等效为 [a-zA-Z0-9_]
re.findall(r"a\wb""a0b")
# ['a0b']
re.findall(r"a\wb""a&b")
# []

# \W 等效为 [^a-zA-Z0-9_]
re.findall(r"a\Wb""a&b")
# ['a&b']
re.findall(r"a\Wb""a0b")
# []

# \Z
re.findall(r"abc\Z""123abc")
# ['abc']
re.findall(r"abc\Z""abc123")
# []

# (...)
re.findall(r"(ab)""ababcdefg")
# ['ab', 'ab']
re.findall(r"(ab)*""ababcdefg")
# ['ab', '', '', '', '', '', '']

# (?:…)
re.findall(r"(?:ab)ab""ababcdefg")
# ['abab']

# (?P<name>…) 与 (?P=name)
re.findall(r"(?P<word>.+) (?P=word)""the the")
# ['the']  # 对比前面 \数字

# (?#…)
re.findall(r"(?#这个表达式用于演示注释和组合命令)(?P<word>.+) (?P=word)""the the")
# ['the']

# 下面演示四种断言
# 左右包含断言 (?=…)
re.findall(r"Isaac(?=\sAsimov)""Isaac Asimov")
# ['Isaac']
re.findall(r"Isaac(?=\sAsimov)""Isaac simov")
# []

# 左右不包含断言 (?!…)
re.findall(r"Isaac(?!\sAsimov)""Isaac asimov")
# ['Isaac']
re.findall(r"Isaac(?!\sAsimov)""Isaac Asimov")
# []

# 右左包含断言 (?<=…)
re.findall(r"(?<=-)\w+""spam-egg")
# ['egg']
re.findall(r"(?<=-)\w+""spamegg")
# []

# 右左不包含断言 (?<!…)
re.findall(r"(?<!-)\w+""spamegg")
# ['spamegg']
re.findall(r"(?<!-)^\w+$""spam-egg")
# []

4.2 常用例子

下面举一些常用的例子。需要注意的是,大部分事情都是八仙过海各显神通,方法并不唯一。

为了方便,使用同一串字符串:

string = "我的身份证号是110010200012200036,我的私人邮箱是chuck@outlook.com,公司邮箱是12345678@qq.org,邮编是520040,出生日期是2000-12-20,入职时间2021/12/20,手机号是18811882288,办公室电话010-88888888,服务器地址有:192.168.1.1 192.168.1.85 192.168.1.86 127.0.0.1 256.1.1.1 192.256.256.256 192.255.255.255 aa.bb.cc.dd"

4.2.1 中文字符

中文的编码从 \u4e00 到 \u9fa5

pattern = r"[\u4e00-\u9fa5]+"
re.findall(pattern, string)
['我的身份证号是''我的私人邮箱是''公司邮箱是''邮编是''出生日期是''入职时间''手机号是''办公室电话''服务器地址有']

4.2.2 邮箱

用户名为 4~20 个字符,包含大小写字母,下划线,阿拉伯数字,点号,中划线;二域名为 1~10 个字符;顶级域名为 1~10 个字符。

pattern = r"[a-zA-Z0-9_-]{4,20}@[a-zA-Z0-9_-]{1,10}[.][a-zA-Z0-9_-]{1,10}"
re.findall(pattern, string)
# ['chuck@outlook.com', '123456@qq.org']

4.2.3 身份证

身份证十八位,前六位是地区,然后是六位生日,然后是三位顺序,然后是一位校验。

pattern = r"[1-9]\d{5}(?:19|(?:2\d))\d{2}(?:(?:0[1-9])|(?:10|11|12))(?:(?:[0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]"
re.findall(pattern, string)
# ['110010200012200036']

4.2.4 国内邮政编码

邮编前两位数字表示省(直辖市、自治区),第三位数字表示邮区;第四位数字表示县(市);最后两位数字表示投递局(所)。

pattern = r"(?<!\d)[1-9]\d{5}(?!\d)"
re.findall(pattern, string)
# ['520040']

4.2.5 日期

常见日期格式:yyyyMMdd、yyyy-MM-dd、yyyy/MM/dd、yyyy.MM.dd。

pattern = r"(?<!\d)\d{4}(?:-|\/|.)\d{2}(?:-|\/|.)\d{2}(?!\d)"
re.findall(pattern, string)
# ['2000-12-20', '2021/12/20']

4.2.6 IP 地址

IPv4 的定义 IPv4 地址是 32 位的二进制数字,可转换为 4 个 0~255 之间的数字。

pattern = r"(?<![\.\d])(?:25[0-5]\.|2[0-4]\d\.|[01]?\d\d?\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)(?![\.\d])"
re.findall(pattern, string)
# ['192.168.1.1', '192.168.1.85', '192.168.1.86', '127.0.0.1', '192.255.255.255']

4.2.7 固定电话

区号 3~4 位,号码 7~8 位。

pattern = r"\d{3}-\d{8}|\d{4}-\d{7}"
re.findall(pattern, string)
# ['010-88888888']

4.2.8 手机号码

手机号以 1 开头,第二位一般是 356789:

pattern = r"1[356789]\d{9}"
re.findall(pattern, string)
# ['18811882288']

4.2.9 微信号

根据微信的提示:微信帐号长度限 6-20 位,建议避免包含姓名、生日等涉及个人隐私的信息。实测发现,只能以字母开头,不能使用汉字:

微信号须以字母开头,仅支持 6~20 个字母、数字、下划线、减号自由组合。

pattern = r"([a-zA-Z][\w|-]{5,19})"
re.findall(pattern, string)
# 正常来说这样就可以了

但一般都是不正常的。比如前面的 寻找发言人,是从一个字节数组里找到微信号。

下面的几种情况都可以用同一个模式解决:

pattern = r"\\x.{2}(?:\\t|\\r|\\n)?([a-zA-Z][\w|-]{5,19})"
string = b'\n\x04\x08\x10\x10\x00\x1a\x0f\x08\x01\x12\x0bxiang090705\x1aw\x08\x07\x12s<msgsource>\n\t<silence>1</silence>\n\t<membercount>229</membercount>\n\t<signature>v1_/2pgK9XJ</signature>\n</msgsource>\n\x1a$\x08\x02\x12 c1c0a01f588dff1477906712b410dcb6'

re.findall(pattern, str(string))
# ['xiang090705']
string = b'\n\x04\x08\x10\x10\x00\x1a\x16\x08\x01\x12\rwxid_4311119412\x1aw\x08\x07\x12s<msgsource>\n\t<silence>1</silence>\n\t<membercount>229</membercount>\n\t<signature>v1_ur8vW3ou</signature>\n</msgsource>\n\x1a$\x08\x02\x12 1d0821bd376a7b800eaae34421641d75'

re.findall(pattern, str(string))
# ['wxid_4311119412']
string = b'\n\x04\x08\x10\x10\x00\x1a\r\x08\x01\x12\taaa850414\x1aw\x08\x07\x12s<msgsource>\n\t<silence>1</silence>\n\t<membercount>229</membercount>\n\t<signature>v1_esI/AIx0</signature>\n</msgsource>\n\x1a$\x08\x02\x12 e5ad5ed70899ad68ecc494e0020a85a4'

re.findall(pattern, str(string))
# ['aaa850414']

该文章在 2025/11/29 15:12:33 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved