判断数字字符, /[0123456789]/
import re
re.search('[0123456789]', '0') != None
/[0123456789]/.test('0');
默认情况下re.search
只判断string的某个子串是否能有pattern匹配,为了测试整个string是否能由pattern匹配,在pattern两端加上^
和$
,他们并不匹配任何字符,只是表示定位到字符串开始和字符串结束。
字符组中的字符排列顺序不影响字符组的功能,出现重复字符也没有影响。
[0-9]
等价与[0123456789]
-
的表示范围和码值有关,就是ASCII中字符的码值[0-9]
,但不能使用[9-0]
[0-z]
中有数字,标点符号,大写字母和小写字母[0-9a-zA-Z]
可以匹配数字,大写字母或者小写字母,[0-9a-fA-F]
可以匹配十六进制字符。[\x00-\x7F]
可以匹配所有的ASCII字符元字符如-
,[
,]
等在正则表达式里有特殊的意义,如果想让他们表示普通字符,就需要用到转义。
[-09]
-> - 0 9[0-9]
-> 0123456789[-0-9]
-> - 0123456789re.search("^[0\\-9]$", "3") != None # => False
re.search(r"^[0-9]$", "-") != None # => True
^[012]345]$ "2345" -> True
^[012]345]$ "5"-> False
^[012]345]$ "]" -> False
^[012\]345]$ "2345" -> False
^[012\]345]$ "5" -> True
^[012\]345]$ "]" -> True
^\[012]$ "[012]" -> True
排除型字符组非常类似普通字符组,写作[^]
,比如[^0-9]
表示非数字字符。
^[^0-9][0-9]$ "A8" -> True
^[^0-9][0-9]$ "x6" -> True
在当前位置,匹配一个没有列出的字符和在当前位置,不要匹配列出的字符是不一样的,排除型字符组必须匹配一个字符。
在排除型字符组中,-
应该紧跟在^
之后,而在普通字符组中,-
应该紧跟在开方括号以后。
^[^0-9][0-9]$ "8" -> False
^[^0-9][0-9]$ "x6" -> True
^[^-09]$ "-" -> False
^[^-09]$ "8" -> True
^[^0-9]$ "-" -> True
^[^0-9]$ "8" -> False
\d == [0-9]
digit 数字\w == [0-9a-zA-Z_]
word 单词字符 注意这里包含有下划线_
\s == [ \t\r\n\v\f]
space 空白字符[\da-zA-Z] == [0-9a-zA-Z]
[^0-9] == [^\d]
[^0-9a-zA-Z_] == [^\w]
[^\d] == [\D]
[^\w] == [\W]
[^\s] == [\S]
[\s\S]
表示匹配任意字符Java和.NET中提供,其他语言不支持。
[[a-z] && [^aeiou]]
Java写法[a-z-[aeiou]]
.NET写法不打算看。。。
^\d\d\d\d$ == ^\d{3}$
{n}
之前的元素必须出现n次{m,n}
之前的元素最少m次,最多出现n次{m,}
之前的元素最少出现m次,最多无上限{0,n}
之前的元素可以不出现,也可以出现,最多出现n次*
== {0,}
可能出现,也可能不出现,出现次数没有上限+
== {1,}
至少出现1次,出现次数没有上限?
== {0,1}
至多出现1次,也可能不出现^travell?er "traveler" -> True
^travell?er "traveller" -> True
^<[^>]+> "<blod>" -> True
^<[^>]+> "</table>" -> True
^<[^>]+> "<> -> False"
<[^/][^>]*>
open tag: <b> <div>
</[^>]+>
close tag: </b> </div>
<[^>]+/>
self close tag: <img /> <img src="/a.jpg" />
<[^>]+>
all the tagre.search(r"\d{6}", "ab123456cd").group(0) # => 123456
re.search(r"^<[^>]+>$", "<blod>").group(0) # => <blod>
re.findall(r"\d{6}", "zipcode: 201203, zipcode: 100099") # => ["201203", "100099"]
点号可以匹配任意字符,但是不能匹配换行符\n
,如果想要匹配任意字符,可以:
[\s\S]
,或者[\d\D]
,或者[\w\W]
^.$ "\n" -> False
(?s)^.$ "\n" -> True
^[\s\S]$
"\n" -> True`匹配优先量词(贪婪量词): 在不确定是否要匹配时,先尝试匹配的选择
".*"
"[^"]*"
忽略优先量词(懒惰量词):在不确定是否要匹配时,先尝试不匹配的选择,先匹配到最近的
//.*
/\*[\s\S]*?\*/
<a\s[\s\S]+?</a>
HTML中的超链接<script[\s>][\s\S]+?</script>
匹配JavaScript的表达式<table[\s>][\s\S]+?</table>
匹配table<tr[\s>][\s\S]+?</tr>
匹配tr<td[\s>][\s\S]+?</td>
匹配td^.*/
匹配linux路径 /usr/local/bin/python -> /usr/local/bin/
[^/]*$
匹配linux程序名 /usr/local/bin/python -> python
^.*\\
匹配windows路径[^\\]*$
匹配windows程序名
## 匹配路径
>>> re.search(r"^.*/", "/usr/local/bin/python").group(0)
'/usr/local/bin/'
>>> re.search(r".*/", "/usr/local/bin/python").group(0)
'/usr/local/bin/'
>>> re.search(r".*?/", "/usr/local/bin/python").group(0)
'/'
>>> re.search(r".+?/", "/usr/local/bin/python").group(0)
'/usr/'
>>> re.search(r".??/", "/usr/local/bin/python").group(0)
'/'
## 匹配程序名
>>> re.search(r"[^/]*$", "/usr/local/bin/python").group(0)
'python'
>>> re.search(r"[^/]*?$", "/usr/local/bin/python").group(0)
'python'
>>> re.search(r"[^/]+?$", "/usr/local/bin/python").group(0)
'python'
>>> re.search(r"[^/]??$", "/usr/local/bin/python").group(0) ??
'n'
在正则表达式中,+
,*
,?
等作为量词具有特殊意义,如果只希望表示这些字符本身,需要只用转义,在它们之前加上\
即可。{m,n}
的转义只需要写成\{m,n}
即可。忽略优先量词则需要将2个量词全部转义,*?
必须要写成\*\?
。
[1-9]\d{13,16}[0-9x]
错误[1-9]\d{14}(\d{2}[0-9x])?
正确\d{13,16}
错误\d{13}(\d{3})?
正确<[^/][^>]*[^/]>
错误,会错过 <u>
这种形式<[^/]([^>]*[^/])?>
正确/[a-z]+/?[a-z]*_?[a-z]*(\.php)?
错误/[a-z]+(/[a-z]+(_[a-z]+)?\.php)?
正确^[-\w.]{0,64}@([-a-zA-Z0-9]{1,63}\.)*[-a-zA-Z0-9]{1,63}$
[1-9]\d{14}(\d{2}[0-9x])?
正确([1-9]\d{14}|[1-9]\d{14}\d{2}[0-9x])
正确,采用多选结构^([0-9]|[0-9]{2}|1[0-9][0-9][0-9]|2[0-4][0-9]|25[0-5])$
^((00)?[0-9]|0?[0-9]{2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$
可以匹配005,020这样的数值0?[0-9]{2}
这个匹配到的(0?[1-9]|1[012])
月 01-12(0?[1-9]|[12]\d|3[01])
日 01-31(0?[1-9]|[01]\d|2[0-4])
小时 01-24(0?[1-9]|[1-5]\d|60)
分钟01-60 闰秒?(0|\+86)?(13[0-9]|15[0-356]|18[025-9])\d{8}
<('[^']*'|"[^"]*"|[^'"])+>
^ab|cd$
字符串开头的ab或字符串结尾的cd,而不是只包含ab或cd的字符串捕获型分组,分组编号取决于开括号出现的顺序
(\d{4})-(\d{2})-(\d{2})
"2010-01-22" group(1) -> 2010 group(2) -> 01 group(3) -> 22<a\s+href\s*=\s*["']?([^"'\s]+)["']?>([^<]+)</s>
提取超链接<head>([^<]+)</head>
提取网页标题<img\s+[^>]*?src=["']?([^"'\s]+)["']?[^>]*>
提取图片链接(\d{4})-(\d{2})-(\d{2})
2010-12-22 group(1) -> 2010(\d){4}-(\d{2})-(\d{2})
2010-12-22 group(1) -> 0[a-z]
("1a2b3c"," ") -> 1 2 3(\d{4})-(\d{2})-(\d{2})
("2010-12-22", r"\2/\3/\1") -> 12/22/2010(\d{4})-(\d{2})-(\d{2})
("2010-12-22", r"\1年/\2月/\3日") -> 2010年12月22日注意不能使用\0,如果要引用整个正则表达式匹配的文本,可以包一个大括号然后使用\1
使用\1
等来使用反向引用。
^([a-z])\1$
"aa" True^([a-z])\1$
"dd" True^([a-z])\1$
"ac" False<([^>]+)>[\s\S]*?</\1>
表达式中的反向引用出javaScript是$num
外,.NET, Java, PHP, Python, Ruby都是\num
,替换中的.NET, Java, Javascript是$num
, PHP, Python, Ruby都是\num
。
$0
可以表示第0个分组(也就是整个表达式匹配的文本),但是\0
不行\10
是表示第10个捕获分组,还是第1个捕获分组后跟着字符0?\g<num>0
\${num}
和\${num}0
解决这个问题\10
引用此捕获分组的文本,否则认为是后一种\10
表示编号为1的分组和字符0(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})
"2012-01-22" .group('year') -> 2012
语言 分组记法 反向引用记法 替换时的引用记法
.NET (?<name>...) \k<name> ${name}
PHP (?P<name>...) (?P=name) 不支持,只能用${num}
Python (?P<name>...) (?P=name) \g<name>
Ruby (?P<name>...) \k<name> \k<name>
括号的作用:
非捕获分组:(?:)
,只限定量词的作用范围,不捕获任何文本
(\d{4})-(\d{2})-(\d{2})
"2012-12-11" .group(2) -> 12(?:\d{4})-(\d{2})-(\d{2})
"2012-12-11" .group(1) -> 12+
, *
, ?
-> \+
, \*
, \?
{6}
, [a-z]
-> \{6}
, \[a-z]
(|)
-> \(\|\)
IIS:
Apache:
nginx:
(\w+\.?)+
"aaa.bbb.ccc" ["ccc"](\w+\.?)+?
"aaa.bbb.ccc" ['aaa.', 'bbb.', 'ccc']结构匹配的文本不真正匹配文本,只负责判断在某个位置左/右侧的文本是否符合要求,这种结构被称为断言。常见的断言有3类,单词边界,行起始/结束位置,环视。
\b
\w
能匹配的字符,就是[a-zA-z0-9_]
\b\w+\b
即可匹配英文单词\B
print re.sub(r"\brow\b", r"<span class=\"h1\">\g<0></span>", "a string row, haha")
(?m)
(?m)^\w+
(?m)\A\w+
^
$
\w+$
(?m)^\s+
(?m)\s+$
(^\s+|\s+$)
,它会合并多个行什么的。。
plainText = "line1\nline2\nline3"
print re.sub(r"(?m)$", "</p>", re.sub(r"(?m)^","<p", plainText))
(?!/)
当前位置之后(右侧),不能出现/能匹配的文本(?<!/)
当前位置之前(左侧),不能出现/能匹配的文本<(?!/)('[^']*'|"[^"]*"|[^'">])+(?<!/)>
名字 判断方向
肯定顺序环视 (?=...)
否定顺序环视 (?!…)
肯定逆序环视 (?<=…)
否定逆序环视 (?<!…)
(?<=\d)(?=(\d{3})+(?!\d))
(?<![a-zA-z])\s+(?![a-zA-Z])
<(?!/)[^>]+(?<!/)>
(?<!\d>)\d{6}(?!\d)
(?![aeiou])[a-z]
(^(?=\d+)(?!999))
之后是数字,但是不能是999^((?!\d)|(?=\d\D))
要么不是数字字符,要么是一个数字字符和一个非数字字符匹配模式就是匹配时使用的规则,常用的匹配模式有4中,不区分大小写模式,单行模式,多行模式和注释模式。
(?i)the
模式修饰符 JavaScript不支持re.I
预定义常量
.NET RegexOPtions.IgnoreCase
Java Pattern.CASE_INSENSTIVE
JavaScript /regex/i
PHP /regex/i
Python re.I, re.IGNORECASE
Ruby Regexp::IGNORECASE, /regex/i
单行模式影响的是点号的匹配规则,在默认模式下,点号可以匹配除换行符之外的任何字符,在单行模式下,点号也可以匹配换行符。
多行模式影响的是^
和$
的匹配,早默认模式下,^
和$
匹配的是整个字符串的起始位置和结束为止,但在多行模式下,它们也能匹配字符串内部某一行文本的起始位置和结束为止。
(?m)^\d.*
(?#comment)
|
运算法,如Pattern.COMMENTS | Pattern.MULTILINE
Pattern.UNIX_LINES
和Pattern.CANON_EQ
re.U
和re.A
(?-modifer)
某个模式生效到此处为止 Python和JavaScript不支持((?i)foo|zero)BAR
(?i)(foo|zee)(?-i)BAR
Python中无法限定模式的作用范围,这里以Java为例。
((?i)abc)\\1
abcABC false(?i)(abc)\\1
abcABC true((?s)a.)\\1
a\na\n true(?s)(a.)\\1
a\na\n true((?m)^a).\\1
abs true(?m)(^a).\\1
abba trueJava中,模式修饰符具有更高的优先级
字符串文字 字符串/正则文字 正则表达式
\\n \n 回车
\\t \t tab
\\\\ \\ \
记法 转义 说明
[] \[] 只对开括号转义
. \.
- \- [a\-b]等价于[-ab]
* \*
+ \+
? \?
*? \*\?
+? \+\?
?? \?\?
(…) (...) 开,闭括号都需要转义 | | (…|…) (…|…) ^ ^ $ \$ $num \$或$$
### 6.1.3 彻底消除元字符的特殊含义
* `\Qca*t\E` 只有Java和PHP支持
* 其他语言支持`escape`方法或`quote`方法,除JavaScript外
### 6.1.4 字符组的转义
* 字符组里,`]`, `-`, `^`需要转义
## 6.2 正则表达式的处理形式
### 6.2.1 函数式处理
调用对应函数,将正则表达式和字符串作为参数传入即可。比如Python和PHP。
### 6.2.2 面向对象式处理
生成正则表达式对象,调用对象的成员函数。比如Java和.NET。
### 6.2.3 比较
* 面向对象式:步骤分明,效率更高
* 函数式:方便,代码简洁
### 6.2.4 线程安全性
* 正则表达式对象和匹配结果对象
* 多个线程可以共享一个正则表达式对象,但操作不同的文本时,对应每个线程生成专属的匹配结果对象
## 6.3 表达式中的优先级
### 4种组合关系
* 普通拼接 abc
* 括号 (abc)
* 量词 a*b
* 多选结构 ab|cd
### 优先级
1. (regex) 整个括号里的子表达式成为单个元素
2. * ? + 限定之前紧邻的元素
3. abc 普通拼接,元素相继出现
4. a|bc 多选结构
# 第7章 Unicode
## 7.1 关于编码
* ASCII字符
* 非ASCII字符
## 7.2 推荐使用Unicode编码
略。。
## 7.3 Unicode匹配规则
* ASCII匹配规则
* Unicode匹配规则
## 7.4 单词边界
* Unicode中的单词边界
## 7.5 码值
### 发(53d1)的表示方法
语言 表示法 .NET \u53d1 Java \u53d1 JavaScript \u53d1 Python \u53d1 PHP \x{53d1} Ruby \u{53d1}
```
略
略
必须满足的4个条件:
<
和>
@
(+?(\d+|\.\d+|\d+\.\d+)|-?(\d+|\d+\.\d+))
"[^"]*"
[^/][^>]*
\A\w([-\w.](?!\.\.)){0,63}\Z
完成,精确,效率
返回true和false即可
使用字符串或者replacement函数
将字符串切分为数组
保存为变量,避免重复编译
使用最合适(专用)的结构
a+a+
,[0-9]+\w+
等表达式regex1*regex2
的表达式,避免regex1
和regex2
能匹配相同的内容(reg1|reg2)
的表达式,避免reg1
和reg2
能匹配相同的内容使用startwith
,endwith
等方法
不一定非要用正则表达式
使用正则表达式和其他手段配合解决问题
/\d/.test("1")
true
略
[]
\d
, \D
, \w
, \W
, \s
, \S
均采用ASCII匹配规则
\b
匹配成功只有1中情况,一边必须保证\w
匹配成功,一边必须保证\w
匹配不成功
^
指定行起始位置,多行模式(m)下,^
可以匹配字符串内部的文本行的开始位置 /^2/m.test("1\n2")
true$
同理,/1$/m.test("1\n2")
true[\s\S]
或[\d\D]
\num
记法$num
记法/regexp/
或者new RegExp()
RegExp.souce
只读,保存文本形式的表达式RegExp.global
只读,是否指定了全局模式RegExp.ignoreCase
只读,是否指定了多行模式RegExp.multiline
只读,是否指定了多行模式RegExp.lastIndex
可写变量,如果使用了全局模式,这个变量保存的是在字符串中尝试下次匹配的偏移值,在RegExp.tesst()
和RegExp.exec()
中都会用到这个变量RegExp.exec()
都会从字符串起始位置开始尝试匹配,结果会造成无穷循环。RegExp.test(string)
匹配成功之后,各捕获分组的文本已经保存了下来,用RegExp.$1
,RegExp..$2
等就可以获得RegExp.exec(string)
$num
表示对应捕获分组匹配的文本, $$
表示$字符, $`` 正则表达式所匹配文本之前(左侧)的文本,
$'` 正则表达式所匹配文本之后(右侧)的文本略。。
略。。