高级正则表达式技术Python版



正则表达式是从信息中搜索特定的模式的一把瑞士军刀。它们是一个巨大的工具库,其中的一些功能经常被忽视或未被充分利用。今天我将向你们展示一些正则表达式的高级用法。

举个例子,这是一个我们可能用来检测电话美国电话号码的正则表达式:

1
r '^(1[-\s.])?(\()?\d{3}(?(2)\))[-\s.]?\d{3}[-\s.]?\d{4}$'

我们可以加上一些注释和空格使得它更具有可读性。

1
2
3
4
5
6
7
8
9
'^'
'(1[-\s.])?' # optional '1-','1.' or '1'
'(\()?'      # optional opening parenthesis
'\d{3}'      # the area code
'(?(2)\))'   # if there was opening parenthesis,close it
'[-\s.]?'    # followed by '-' or '.' or space
# first 3 digits
# followed by '-' or '.' or space
'\d{4}$'    # last 4 digits

让我们把它放到一个代码片段里:

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import re
 
numbers = [ "123 555 6789" ,
             "1-(123)-555-6789" "(123-555-6789" "(123).555.6789" "123 55 6789" ]
 
for number in numbers:
     pattern re.match(r '^'
                    '(1[-\s.])?'           '(\()?'                # optional opening parenthesis
'\d{3}'                # the area code
'(?(2)\))'             '[-\s.]?'              # followed by '-' or '.' or space
# first 3 digits
# followed by '-' or '.' or space
'\d{4}$\s*' # last 4 digits
 
     if pattern:
         print '{0} is valid' . format (number)
else :
         print '{0} is not valid' (number)

输出,不带空格:

5
123 555 6789 is valid
1 - ( 123 ) - 555 6789 valid
123 is not valid
). 555.6789 valid
55 valid

正则表达式是 python 的一个很好的功能,但是调试它们很艰难,而且正则表达式很容易就出错。

幸运的是,python 可以通过对 re.compile 或 re.match 设置 re.DEBUG (实际上就是整数 128) 标志就可以输出正则表达式的解析树。

23
                     '(1[-\s.])?'        '(\()?'             '\d{3}'             '(?(2)\))'          '[-\s.]?'           '\d{4}$' (number)

解析树

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
at_beginning
max_repeat 0 1
   subpattern 1
literal 49
in
       45
category category_space
46
2147483648
   in
category category_space
1
2
40
2147483648
in
category category_space
3 3
in
category category_digit
2147483648
in
category category_space
subpattern None
groupref_exists 2
41
None
2147483648
in
category category_space
1
in
45
category category_space
46
2147483648
in
category category_space
3
in
category category_digit
2147483648
in
category category_space
1
in
45
category category_space
46
2147483648
in
category category_space
4 4
in
category category_digit
at at_end
2147483648
in
category category_space
valid
valid
valid
valid
valid

贪婪和非贪婪

在我解释这个概念之前,我想先展示一个例子。我们要从一段 html 文本寻找锚标签:

7
html = 'Hello <a href="http://pypix.com" title="pypix">Pypix</a>'
 
m re.findall( '<a.*>.*<\/a>' m:
     print m

结果将在意料之中:

['<a href="http://pypix.com" title="pypix">Pypix</a>']

我们改下输入,添加第二个锚标签:

8
'Hello <a href="http://pypix.com" title="pypix">Pypix</a>' \
        'Hello <a href="http://example.com" title"example">Example</a>'
 
m:
m

结果看起来再次对了。但是不要上当了!如果我们在同一行遇到两个锚标签后,它将不再正确工作:

1
'<a href="http://pypix.com" title="pypix">Pypix</a>Hello <a href="http://example.com" title"example">Example</a>' ]

这次模式匹配了第一个开标签和最后一个闭标签以及在它们之间的所有的内容,成了一个匹配而不是两个 单独的匹配。这是因为默认的匹配模式是“贪婪的”。

当处于贪婪模式时,量词(比如 * 和 +)匹配尽可能多的字符。

当你加一个问号在后面时(.*?)它将变为“非贪婪的”。

'<a.*?>.*?<\/a>' m

现在结果是正确的。

1
'<a href="http://example.com" title"example">Example</a>' ]

前向界定符和后向界定符

一个前向界定符搜索当前的匹配之后搜索匹配。通过一个例子比较好解释一点。

下面的模式首先匹配 foo,然后检测是否接着匹配 bar

11
strings "hello foo" # returns False
              "hello foobar"  ]    # returns True
string strings:
re.search(r 'foo(?=bar)' pattern:
'True'
:
'False'

这看起来似乎没什么用,因为我们可以直接检测 foobar 不是更简单么。然而,它也可以用来前向否定界定。 下面的例子匹配foo,当且仅当它的后面没有跟着 bar

12
# returns True
"hello foobar" # returns False
"hello foobaz" ]      # returns True
 
strings:
'foo(?!bar)' pattern:
'True'
:
'False'

后向界定符类似,但是它查看当前匹配的前面的模式。你可以使用 (?> 来表示肯定界定,(?<! 表示否定界定。

下面的模式匹配一个不是跟在 foo 后面的 bar

"hello bar""hello bazbar"'(?<!foo)bar' 'False'

条件(IF-Then-Else)模式

正则表达式提供了条件检测的功能。格式如下:

(?(?=regex)then|else)

条件可以是一个数字。表示引用前面捕捉到的分组。

比如我们可以用这个正则表达式来检测打开和闭合的尖括号:

13
"<pypix>" # returns true
"<foo" # returns false
"bar>" # returns false
"hello" ]     # returns true
 
strings:
'^(<)?[a-z]+(?(1)>)$' pattern:
'True'
:
'False'

在上面的例子中,1 表示分组 (<),当然也可以为空因为后面跟着一个问号。当且仅当条件成立时它才匹配关闭的尖括号。

条件也可以是界定符。

无捕获组

分组,由圆括号括起来,将会捕获到一个数组,然后在后面要用的时候可以被引用。但是我们也可以不捕获它们。

我们先看一个非常简单的例子:

re         
string 'Hello foobar'         
'(f.*)(b.*)' "f* => {0}" (pattern.group( 1 )) # prints f* => foo         
"b* => {0}" 2 # prints b* => bar

现在我们改动一点点,在前面加上另外一个分组 (H.*)

'(H.*)(f.*)(b.*)' # prints f* => Hello         
# prints b* => bar

模式数组改变了,取决于我们在代码中怎么使用这些变量,这可能会使我们的脚本不能正常工作。 现在我们不得不找到代码中每一处出现了模式数组的地方,然后相应地调整下标。 如果我们真的对一个新添加的分组的内容没兴趣的话,我们可以使它“不被捕获”,就像这样:

'(?:H.*)(f.*)(b.*)'# prints b* => bar

通过在分组的前面添加 ?:,我们就再也不用在模式数组中捕获它了。所以数组中其他的值也不需要移动。

命名组

像前面那个例子一样,这又是一个防止我们掉进陷阱的方法。我们实际上可以给分组命名, 然后我们就可以通过名字来引用它们,而不再需要使用数组下标。格式是:(?Ppattern) 我们可以重写前面那个例子,就像这样:

'(?P<fstar>f.*)(?P<bstar>b.*)'(pattern.group('fstar''bstar'# prints b* => bar

现在我们可以添加另外一个分组了,而不会影响模式数组里其他的已存在的组:

6
'(?P<hi>H.*)(?P<fstar>f.*)(?P<bstar>b.*)' # prints b* => bar         
"h* => {0}" 'hi' # prints b* => Hello

使用回调函数

在 Python 中 re.sub() 可以用来给正则表达式替换添加回调函数。

让我们来看看这个例子,这是一个 e-mail 模板:

22
template "Hello [first_name] [last_name],\         
  Thank you purchasing [product_name] from [store_name]. \         
The total cost of your purchase was [product_price] plus [ship_price] shipping. \         
You can expect your product to arrive [ship_days_min] to [ship_days_max] business days. \         
Sincerely,\         
[store_manager_name]"         
# assume dic has all the replacement data         
# such as dic['first_name'] dic['product_price'] etc...         
dic {         
  "first_name" : "John" "last_name" "Doe" "product_name" "iphone" "store_name" "Walkers" "product_price" : "$500" "ship_price" "$10"

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


jquery.validate使用攻略(表单校验) 目录 jquery.validate使用攻略1 第一章&#160;jquery.validate使用攻略1 第二章&#160;jQuery.validate.js API7 Custom selectors7 Utilities8 Validato
/\s+/g和/\s/g的区别 正则表达式/\s+/g和/\s/g,目的均是找出目标字符串中的所有空白字符,但两者到底有什么区别呢? 我们先来看下面一个例子: let name = &#39;ye wen jun&#39;;let ans = name.replace(/\s/g, &#39;&#3
自整理几个jquery.Validate验证正则: 1. 只能输入数字和字母 /^[0-9a-zA-Z]*$/g jQuery.validator.addMethod(&quot;letters&quot;, function (value, element) { return this.optio
this.optional(element)的用法 this.optional(element)是jquery.validator.js表单验证框架中的一个函数,用于表单控件的值不为空时才触发验证。 简单来说,就是当表单控件值为空的时候不会进行表单校验,此函数会返回true,表示校验通过,当表单控件
jQuery.validate 表单动态验证 实际上jQuery.validate提供了动态校验的方法。而动态拼JSON串的方式是不支持动态校验的。牺牲jQuery.validate的性能优化可以实现(jQuery.validate的性能优化见图1.2 jQuery.validate源码 )。 也可
自定义验证之这能输入数字(包括小数 负数 ) &lt;script type=&quot;text/javascript&quot;&gt; function onlyNumber(obj){ //得到第一个字符是否为负号 var t = obj.value.charAt(0); //先把非数字的都
// 引入了外部的验证规则 import { validateAccountNumber } from &quot;@/utils/validate&quot;; validator.js /*是否合法IP地址*/ export function validateIP(rule, value,cal
VUE开发--表单验证(六十三) 一、常用验证方式 vue 中表单字段验证的写法和方式有多种,常用的验证方式有3种: data 中验证 表单内容: &lt;!-- 表单 --&gt; &lt;el-form ref=&quot;rulesForm&quot; :rules=&quot;formRul
正则表达式 座机的: 例子: 座机有效写法: 0316-8418331 (010)-67433539 (010)67433539 010-67433539 (0316)-8418331 (0316)8418331 正则表达式写法 0\d{2,3}-\d{7,8}|\(?0\d{2,3}[)-]?\d
var reg = /^0\.[1-9]{0,2}$/;var linka = 0.1;console.log (reg.test (linka)); 0到1两位小数正则 ^(0\.(0[1-9]|[1-9]{1,2}|[1-9]0)$)|^1$ 不含0、0.0、0.00 // 验证是否是[1-10
input最大长度限制问题 &lt;input type=&quot;text&quot; maxlength=&quot;5&quot; /&gt; //可以 &lt;input type=&quot;number&quot; maxlength=&quot;5&quot; /&gt; //没有效
js输入验证是否为空、是否为null、是否都是空格 目录 1.截头去尾 trim 2.截头去尾 会去掉开始和结束的空格,类似于trim 3.会去掉所有的空格,包括开始,结束,中间 1.截头去尾 trim str=str.trim(); // 强烈推荐 最常用、最实用 or $.trim(str);
正则表达式语法大全 字符串.match(正则):返回符合的字符串,若不满足返回null 字符串.search(正则):返回搜索到的位置,若非一个字符,则返回第一个字母的下标,若不匹配则返回-1 字符串.replace(正则,新的字符串):找到符合正则的内容并替换 正则.test(字符串):在字符串中
正整数正则表达式正数的正则表达式(包括0,小数保留两位): ^((0{1}.\d{1,2})|([1-9]\d.{1}\d{1,2})|([1-9]+\d)|0)$正数的正则表达式(不包括0,小数保留两位): ^((0{1}.\d{1,2})|([1-9]\d.{1}\d{1,2})|([1-9]+
JS 正则验证 test() /*用途:检查输入手机号码是否正确输入:s:字符串返回:如果通过验证返回true,否则返回false /function checkMobile(s){var regu =/[1][3][0-9]{9}$/;var re = new RegExp(regu);if (r
请输入保留两位小数的销售价的正则: /(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/ 1.只能输入英文 &lt;input type=&quot;text&quot; onkeyup=&quot;value
判断价格的正则表达式 价格的正则表达式 /(^[1-9]\d*(\.\d{1,2})?$)|(^0(\.\d{1,2})?$)/; 1 解析:价格符合两种格式 ^ [1-9]\d*(.\d{1,2})?$ : 1-9 开头,后跟是 0-9,可以跟小数点,但小数点后要带上 1-2 位小数,类似 2,2
文章浏览阅读106次。这篇文章主要介绍了最实用的正则表达式整理,比如校验邮箱的正则,号码相关,数字相关等等,本文给大家列举的比较多,需要的朋友可以参考下。_/^(?:[1-9]d*)$/ 手机号
文章浏览阅读1.2k次。4、匹配中的==、an==、== an9、i9 == "9i"和99p==请注意下面这部分的作用,它在匹配中间内容的时候排除了说明:当html字符串如下时,可以匹配到两处,表示匹配的字符串不包含and且不包含空白字符。说明:在上面的正则表达式中,_gvim正则表达式匹配不包含某个字符串
文章浏览阅读897次。【代码】正则表达式匹配a标签的href。_auto.js 正则匹配herf