前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >学会正则表达式,玩弄文本于股掌之中

学会正则表达式,玩弄文本于股掌之中

作者头像
somenzz
发布2020-12-10 10:57:35
7030
发布2020-12-10 10:57:35
举报
文章被收录于专栏:Python七号Python七号

阅读本文大约需要 5 分钟

由于微信不允许外部链接,你需要点击页尾左下角的“阅读原文”,才能访问文中的链接。

1950 年, 一位叫斯蒂芬·科尔·克莱尼的数学家发表了一篇标题为《神经网事件的表示法》的论文,引入了正则表达式的概念。正则表达式就是用来描述他称为”正则集的代数”的表达式,因此采用”正则表达式”这个术语。

随后,肯·汤普逊将这一符号系统引入 Unix 中的 qed 编辑器 ,肯·汤普逊也是 Unix 的主要发明人。正则表达式的第一个实用应用程序诞生。

目前,正则表达式已经在很多软件中得到广泛的应用,包括 *nix(Linux, Unix等)、HP 等操作系统,PHP、C#、Java、 Python、javascript 等编程语言,以及很多的文本处理软件中,都可以看到正则表达式的影子。

今天,无论你是否从事 IT 工作,你都应该学习正则表达式,因为它不仅能让你处理文本信息时事半功倍,更能为你提供一种思维方式,更重要的是,它是通用的知识,不因具体的文本编辑软件而不同,也不因具体的编程语言而不同。

大多数的 IT 青年都知道正则表达式,也能通过 grep 来查找含有相应字符串的文本信息,但是能使用正则表达式的高级功能的,却是少数,一个重要的原因就是正则表达式的符号有点难以记忆,也很不直观。看到别人写的正则表达式,就像看天书一般。虽然正则表达式是有点丑陋,但却是最优秀的文本处理工具。学会使用正则表达式,就算你不会编程,你也轻松高效地处理文本。

假如这样的需求:有一个近上万行内容的文本文件,内容是中英文混合,毫无规律,现在要求把所有的中文全部删除,你会怎么做呢?

如果不会正则表达式,你只能一行一行地删除,会不会觉得很累?但是如果会用正则表达式,只要几秒的时间即可完成。下次如果有人有类似这样的问题请你帮忙,你可以使用正则表达式,弹指间,不需要的字符串已灰飞烟灭,从此,你在别人眼里深藏功与名。(正则表达式是装逼利器 ^_^)。

下面是关于正则表达式的基础内容,希望帮助你更进一步地学会使用正则表达式的较高级功能,如有疑问可加微信 somenzz 交流。

1、要匹配什么

相信你肯定用过 windows 里的文件搜索功能吧,在搜索栏输入”*.doc”,然后所有后缀为 doc 的文件都查找了出来,这里的 * 就是通配符。在正则表达式也是一样,* 表示通配符,表示任意数目的字符,这点相信大家都不陌生。正则表达式也有一些通配符,我们叫它元字符,这是需要记忆的,不过很容易记忆 ,如下所示:

常用的元字符

代码

说明

.

匹配除换行符以外的任意字符

\w

匹配字母或数字或下划线或汉字

\s

匹配任意的空格

\d或[0-9]

匹配一个数字

^

匹配字符串的开始位置

$

匹配字符串的结束位置

比如

  • .* 代表匹配任意一行
  • \d\d 匹配连续的两个数字
  • ^[0-9] 匹配字符串开始位置是数字的字符串
  • \s$ 匹配字符串结尾是空格的字符串
  • ^$ 匹配不含空格的空行
  • ^\s*$ 匹配含空格空行

2、要匹配多少次

有时要匹配很多次数,比如 11 位的手机号码,可以简单地这样写

代码语言:javascript
复制
\d\d\d\d\d\d\d\d\d\d\d
或
[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]

这样写显然是非常麻烦的,正则表达式提供了匹配次数的简洁语法,很容易记忆,如下所示:

重复

代码

说明

*

重复零次或更多次

+

重复一次或更多次

?

重复零次或一次

{n}

重复n次

{n,}

重复n次或更多次

{n,m}

重复n到m次

11 位的手机号码的正则表达式可以简单地这样写

代码语言:javascript
复制
\d{11}
或
[0-9]{11}

假如你要匹配 5 位 至 8 位的 QQ 号则可以使用:

代码语言:javascript
复制
\d{5,12}
或
[0-9]{5,12}

可能你会问了,如果要匹配 *,?,+ 这些本身属于正则表达式里的字符呢? 也很简单,使用\来转义即可。要查找 * 就使用 |* ,要查找 \ 就使用 \\。例如:github\.com 匹配 github.com,C:\\Windows 匹配 C:\Windows。

3、反义

有时需要匹配不是某些字符的字符,如匹配非数字字符串,查找不含 a、e、i、o、u 这 5 个字符的字符串,这时需要用到反义。

常用的反义代码

代码

说明

\W

匹配任意不是字母,数字,下划线,汉字的字符

\S

匹配任意不是空白符的字符

\D

匹配任意非数字的字符

\B

匹配不是单词开头或结束的位置

[^x]

匹配除了x以外的任意字符

[^aeiou]

匹配除了aeiou这几个字母以外的任意字符

例子:\S+ 匹配不包含空白符的字符串,[^aeiou] 匹配不包含a,e,i,o,u 这五个字符的字符串

4 、括号表达式,多选项

(TEMP|TMP|TEST)+.*\d$ 表示匹配含有 TEMP 或 TMP 或 TEST ,并且以数字结尾的字符串,可用于运维中查询一些命名不规范的表或一些垃圾表,从而进行处理。如下所示:

代码语言:javascript
复制
SELECT TABNAME FROM syscat.tables where REGEXP_LIKE(tabname,'(TEMP|TMP|TEST)+.*\d$','i')>0

查询结果如下:

代码语言:javascript
复制
F_DEP_DGLS_TMP_2
F_NIN_TB_TAX_BANK_KIND_TEMP2
TEST20180828
TMP_CLTNBR_18
TMP_CZKH1
TMP_RPT_RMBGRDKLLSPB_01
TMP_RPT_RMBTXLLSPB_01
TMP_ZH1
TMP_ZH2
VT_TMP_JJK_ZJHM_15
TMP_1
TMP_SX500
TMP_ZFMMKXH2

这里我们用到了小括号 (),小括号可以指定子表达式,本例中 (TEMP|TMP|TEST) 就是一个表达式,里面的 | 连接多个选项,是或的关系。后面跟 + 表示这个子表达式代表的字符至少出现 1 次。下篇文章会详细介绍如何在 db2 数据库中添加自定义的正则表达式函数 REGEXP_LIKE,请关注。

5、使用零宽断言

零宽断言有点不太好理解,我以一个实用的例子来说明。

实例-获取本机 IP 地址

通过一个获取本机 IP 地址例子,对正则表达式有个更深入的认识,不需记忆,理解即可。 IP 地址是这样的一种格式,xxx.xxx.xxx.xxx 我们分成两部分 第一部分 xxx.xxx.xxx. 相当于是 3 个 xxx. ,3 个 xxx. 翻译成正则表达式就是 (xxx.){3} , xxx. 代表一至三位的数字,可以是一位、两位或三位。因此 xxx. 的翻译成正则表达式为 [0-9]{1-3}. ,合起来就是 ([0-9]{1,3}\.){3} 第二部分 xxx,非常简单,就是 [0-9]{1,3} 这两部分加起来,完整的正则表达式就是:

代码语言:javascript
复制
([0-9]{1,3}\.){3}[0-9]{1,3}

下面在一台 linux 机器上验证下:

代码语言:javascript
复制
[aaron@ubuntu]$ ifconfig -a | grep -E -o "([0-9]{1,3}\.){3}[0-9]{1,3}"
192.168.167.40
255.255.255.0
192.168.167.255
127.0.0.1
255.0.0.0
192.168.122.1
255.255.255.0
192.168.122.255

可以看出所有的 IP 地址都打印了出来。假如果要获取某一块网卡的 IP 地址,可以这样写:

代码语言:javascript
复制
[aaron@ubuntu]$ ifconfig eth0 | grep -oP "([0-9]{1,3}\.){3}.*(?=  netmask)"
192.168.167.40
[aaron@ubuntu]$ ifconfig eth0 | grep -oP "([0-9]{1,3}\.){3}.*(?=  broadcast)"
192.168.167.40  netmask 255.255.255.0

这里使用先行断言 (?=exp) 来匹配表达式前面的位置 ,即“ netmask”,前的位置,这样就打印出了 eth0 真正的 IP 地址,可以做为参数传递给程序使用。

零宽断言用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像 ^ ,$ 这样的定位作用,用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言。有以下 4 种断言方式:

先行断言 (?=exp)//表示匹配表达式 exp 前面的位置

后发断言 (?<=exp) //表示匹配表达式 exp 后面的位置

负向零宽断言 (?!exp) // 匹配一个不含 exp 前面的位置,这个有点不太好理解,举个例子吧:有以下字符串: baidu.com sina.com.cn 那么正则:^(?!baidu).*$ 匹配结果就是第 2 行,也就是第 1 行被排除了,意思就是查找不以 baidu 开头的字符串。

负向零宽后发断言为 (?<!exp) // 匹配一个不含 exp 后面的位置,举个例子,有以下字符串 www.sina.com.cn www.educ.org www.hao.cc www.baidu.com www.123.com

代码语言:javascript
复制
[aaron@ubuntu]$ cat t.txt
ww.sina.com.cn
www.educ.org
www.hao.cc
www.baidu.com
www.123.com
[aaron@ubuntu]$ grep -oP "^.*(?<!(com))$" t.txt
ww.sina.com.cn
www.educ.org
www.hao.cc
[aaron@ubuntu]$

6、匹配不含某个字符串的文本

比如,匹配不含 baidu 的字符串

代码语言:javascript
复制
[aaron@ubuntu]$ grep -oP "^(?!(.*baidu)).*$" t.txt
ww.sina.com.cn
www.educ.org
www.hao.cc
www.123.com
[aaron@ubuntu]$

匹配不含 baidu 或 hao 的字符串

代码语言:javascript
复制
[aaron@ubuntu]$ grep -oP "^(?!(.*(baidu|hao))).*$" t.txt
ww.sina.com.cn
www.educ.org
www.123.com
[aaron@ubuntu]$

这些正则表达式的知识是通用的,无论你用 grep 或是 ue,还是 vim,他们都天然支持正则表达式,很多编程语言也支持,因此正则表达式的知识是通用的,作为程序员一定要知道。

7、去除中文字符

现在回答本文开头提到的问题,如何在文本中删除中文字符。这里我使用的是文本编辑工具是 vim,你可以使用其他文本编辑工具,只要它支持正则表达式即可。 假如文本内容如下:

代码语言:javascript
复制
 1 数字:^[0-9]*$ 
 2 n位的数字:^\d{n}$ 
 3 至少n位的数字:^\d{n,}$ 
 4 m-n位的数字:^\d{m,n}$ 
 5 零和非零开头的数字:^(0|[1-9][0-9]*)$ 
 6 非零开头的最多带两位小数的数字:^([1-9][0-9]*)+(.[0-9]{1,2})?$ 
 7 带1-2位小数的正数或负数:^(\-)?\d+(\.\d{1,2})?$ 
 8 正数、负数、和小数:^(\-|\+)?\d+(\.\d+)?$ 
 9 有两位小数的正实数:^[0-9]+(.[0-9]{2})?$
10 有1~3位小数的正实数:^[0-9]+(.[0-9]{1,3})?$
11 非零的正整数:^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$
12 非零的负整数:^\-[1-9][]0-9"*$ 或 ^-[1-9]\d*$
13 非负整数:^\d+$ 或 ^[1-9]\d*|0$
14 非正整数:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$
15 非负浮点数:^\d+(\.\d+)?$ 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$
16 非正浮点数:^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 或 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$
17 正浮点数:^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ 或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
18 负浮点数:^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ 或 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
19 浮点数:^(-?\d+)(\.\d+)?$ 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$

若要去除中文信息,首先我在网上查到匹配中文的正则表达式为 [\u4e00-\u9fa5],于是在 vim 中执行命令

代码语言:javascript
复制
:%s/[\u4e00-\u9fa5]//g

其实就是查找字符串 [\u4e00-\u9fa5] 将其替换为 空即可。执行前后对比如下所示:

执行后

这里 [\u4e00-\u9fa5] 不需要记忆,一些常用的复杂的正则表达式,网上都是可以搜索到的,在做稍复杂的文本处理时,首先要想到通过正则表达式怎么解决,如果写不出相应的正则表达式,可以查询 google 或 bing 寻求帮助。

8、资源分享:

  • 我整理了33个常用的正则表达式
  • 正则表达式-维基百科
  • 正则表达式30分钟入门教程
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-11-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python七号 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 常用的元字符
  • 重复
  • 常用的反义代码
  • 实例-获取本机 IP 地址
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档