ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

数据清洗--2. regular expression 正则表达

2021-02-16 12:57:04  阅读:245  来源: 互联网

标签:匹配 -- re 正则 regular 2019 贪婪 date expression


数据清洗–2. regular expression 正则表达


目录


前言

正则表达是数据清洗中不可或缺的一项技能。


一、正则表达是什么

正则表达是用来描述文本规律/模式的一系列的符号。


二、正则表达能干什么

正则表达式在从文本(如日志文件、HTML/XML文件和其他文档)中查找、替换和提取信息时非常有用。
比如提取邮箱或验证输入文本是否符合邮箱格式
邮箱验证正则表达
figure generate by https://regexper.com


三、正则表达的语法

字母候选集(Character sets)

grey gray

可以用 gr[ea]y 来匹配
charactor set

注意 :

  1. 只匹配候选集中1个字母;
  2. 候选集中的顺序是无影响的;
  3. 匹配是大小写敏感的。
    即 grAy, graay, 和 graey 是不会被匹配到的

字母候选域(Character ranges)

[0 − 9], [a − z] or [A − Z]

这里先给一个错误的示范:
想匹配50-99之间的数字,并不能用[50-99]表示。
[50-99]表示的是
在这里插入图片描述
也就是说跟[0-9]是一样的。

现在在给大家展示一个正确的例子:

XRA 000, 1AA 1AA
请问如何设计一个正则表达式,来同时可以满足上面两个字符串?

答案: [A-Z0-9][A-Z][A-Z]\s[0-9][A-Z0-9][A-Z0-9]

在这里插入图片描述



反字母候选集(Negative character sets)

除了用正则表达式可以表达想要哪些字母,还可以用正则表达式表达不要哪些字母,如:

hog dog bog
这三个词中,希望保留hog 和dog,该如何操作呢?

你可以用 :

[hd]og
觉得麻烦? 那你可以试试:
[^b]og

思考:

see[^mn] 会匹配“see”,“see ”中的哪个,还是都会匹配,亦或者都不匹配??
Try the regular expression in Pythex~

特殊字符在候选集内(Metacharacters inside character sets)

var(9), var[0]
如何使用一个正则表达式来满足上述两个字符串? 我们需要匹配"(" ")"和"[" "]"

metacharacters

注意:第一个表达式是正确的,需要用"" 来取消第一个"]“的代码意义变成一个单纯的字符,否则系统会认为表达式2中倒数第二个 “]” 和倒数第四个”["是一对。

下表是一些Re的缩写
short hand
思考:
[^\d\s] 和[\D\S] 是一样的吗?

答案: 必然不一样
第一个是:既不是数字,也不是空格类
第二个是:不是数字或者不是空格类
not

在这里插入图片描述

在这里插入图片描述

重复元字符(Repetition Expressions)

repetition experssion
*: 匹配0个或多个该字符
+:匹配1个或多个该字符
?:匹配0个或1个该字符
假设我们想匹配

oops ooops ooooops oooooops
但是不匹配
ops
以下表达式哪个是正确的?
  • oo*ps
  • ooo*ps
  • oo+ps
  • oo?ps

自己去试试吧~ tips: 上面有效的 re 不止一个哦~


另外,还可以定量的重复
  • \d{2} 只匹配2个数字
  • \d{2,4} 匹配2~4个数字
  • \d{2,} 至少匹配2个数字

当然这个方法也可以用在上面那个例子, 试试 o{2:}ps

再举个例子
匹配

report_2016_09 | assignment_2016_9
budget_16_08 | assignment_08_7
但是不匹配
report_201609_39 | assignment_6_9000
budget_2345678_08 | assignment_000999_7

我们可以用什么表达式呢?
通过观察,有效的字符串由几个字母,一个下划线,2-4位数字,下划线,1-2位数字组成。
re可以写作:
\w+_\d{2,4}_\d{1,2}

字符组(Grouping)

可以把想捕捉的固定字符串组合放到()中,来实现对固定字符串的捕捉。
比如:

(abc)+
可以匹配 abc, abcabc 或 abcabcabc

在这里插入图片描述
例二:
想匹配 I *** you:

I love you, I hate you, I like you, I miss you

可以用

I (.*) you

ps: “.” 表示除换行符(\n)之外的任意单字符,".*"表示在 I 和 you 之间有0个或多个任意字符。


贪婪或非贪婪(greedy or lazy)

贪婪匹配策略:会匹配满足条件的最常的字符串,* 和 + 都是贪婪匹配。
非贪婪匹配策略: 会匹配满足条件的最短的字符串,? 是非贪婪匹配


举个例子
我们有这样一串string

"dog", "cat", "fish"

尝试以下两种re,我们会获得不一样的pattern(匹配片段)

  1. (".+"),*
    在这里插入图片描述
    re1 是贪婪匹配策略,会找到符合条件的最长的片段,即整个string
  2. (".+?"),*

在这里插入图片描述
re2 加入了 “?” 匹配策略变成了非贪婪,就会找满足re的最短的片段,即每个单词。
所以当你编写的re 不能很好的找出想要的片段时,不妨试着改变一下匹配策略。


或(Or)

当想匹配(A or B) + C 这样的pattern的时候就需要使用or的逻辑了, 符号为 ”|“。

举例:

apple juice, orange juice, pear juice, berry juice

以上一堆果汁中,只要匹配苹果汁和橙汁时,就需要用到or的逻辑
Re可以写作:
(apple|orange) juice

在这里插入图片描述

四、代码部分(Python)

以下练习以Python语言为例

贪婪&非贪婪匹配初体验

#pip install re
import re # regular expression的包

# 首先我们来看一下不同的匹配策略对于相同的一段string,
# 是如何匹配的
# 我们可以用re.findall(re,string) 来匹配我们编写的正则表达式
str1 = re.findall(r'.*', 'Please find all.')
print (str1) 
# output: ['Please find all.', '']

str2 = re.findall(r'.?', 'Please find all.')
print (str2) 
# output: ['P', 'l', 'e', 'a', 's', 'e', ' ', 'f', 'i', 'n', 'd', ' ', 'a', 'l', 'l', '.', '']

str3 = re.findall(r'.+', 'Please find all.')
print (str3)
# output: ['Please find all.']

str1 = re.findall(r'l+', 'Please find all')
print (str1)
# output: ['l', 'll']

判断一个日期是不是正确的日期

# task:判断输入的日期是不是一个正确的日期。
# 日期是日月年格式,年份可能是2位,也可能是4位数字表示
# 假设所有年份都是闰年,即2月份有29天。

# 判断输入的日期是不是一个正确的日期
def date(pattern, m):
    if re.match(pattern, m):
        print (m + " is a date")
    else:
        print (m + " is NOT a date")
# 当regular expression比较复杂时,可以用
# ```(?x)```来表示,然后再告诉系统 "?x" 到底是什么
# ```xxx```在Python中表示多行raw string
# 逻辑看不懂的同学,可以把代码放到 
# https://regexper.com 里看看具体逻辑
regex = r'''(?x)
    (?:
    # February (29 days every year)
    (([12]\d|0?[1-9])[-/ ]0?2)|
    # 30-day months
      ([12]\d|0?[1-9]|30)[-/ ](0?[469]|11)|
    # 31-day months
     ([12]\d|0?[1-9]|3[01])[-/ ](0?[13578]|1[02])
    ) [-/ ]
    # Year
    (\d{2,4})
'''

date(regex, "28/02/2019")
date(regex, "31/04/2019")
date(regex, "29/05/2019")
date(regex, "31/06/2019")
# 28/02/2019 is a date
# 31/04/2019 is NOT a date
# 29/05/2019 is a date
# 31/06/2019 is NOT a date

有兴趣的小伙伴可以思考一下如何完善日期的判断,把是否是闰年考虑进去。


提取IP地址

# IP地址的规律是4段1~3位数字,中间用 . 隔开。
# 当然,这种re并不包括判断IP的有效性,
# 有兴趣的同学可以考虑考虑如何实现判断IP的有效性
pattern_ip = r'\d{1,3}[.]\d{1,3}[.]\d{1,3}[.]\d{1,3}'

all_ip = re.findall(pattern_ip, text)

提取邮箱

pattern_email = r"[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+"
str_email = re.findall(pattern_email, text)

总结

以上就是今天整理的内容,本文仅仅简单介绍了正则表达式的使用,其实还有很多进阶的正则表达式使用方法,比如定界符 “^” “$”, uncapture group (非捕获组) “?:” “?=” “?<=” “?!” “?<!” 等等。。。

在实际操作中还是有很多困难等着大家解决的[手动捂脸],代码和核心还是多想,多看,多练。与君共勉~

标签:匹配,--,re,正则,regular,2019,贪婪,date,expression
来源: https://blog.csdn.net/Alvin3411/article/details/113813704

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有