读书笔记 - 正则指引

2013-06-12 09:59

第1章 字符组

1.1 普通字符组

判断数字字符, /[0123456789]/

python:


import re
re.search('[0123456789]', '0') != None

javascript:


/[0123456789]/.test('0');

1.2 关于Python的基础知识

默认情况下re.search只判断string的某个子串是否能有pattern匹配,为了测试整个string是否能由pattern匹配,在pattern两端加上^$,他们并不匹配任何字符,只是表示定位到字符串开始和字符串结束。

1.3 普通字符组(续)

字符组中的字符排列顺序不影响字符组的功能,出现重复字符也没有影响。

范围表示法:

  • [0-9]等价与[0123456789]
  • -的表示范围和码值有关,就是ASCII中字符的码值
  • 0 -> 48
  • 9 -> 57
  • 所以可以直接使用[0-9],但不能使用[9-0]
  • a -> 97
  • z -> 122
  • A -> 65
  • Z -> 90
  • [0-z]中有数字,标点符号,大写字母和小写字母

多个范围表示法

  • 字符组中可以使用多个范围表示法,[0-9a-zA-Z]可以匹配数字,大写字母或者小写字母,
  • 字符组[0-9a-fA-F]可以匹配十六进制字符。

十六进制表示法

  • [\x00-\x7F]可以匹配所有的ASCII字符

 1.4 元字符与转义

元字符如-[]等在正则表达式里有特殊的意义,如果想让他们表示普通字符,就需要用到转义。

  • [-09] -> - 0 9
  • [0-9] -> 0123456789
  • [-0-9] -> - 0123456789

原生字符串

  • 不使用原生字符串: re.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

1.5 排除型字符组

排除型字符组非常类似普通字符组,写作[^],比如[^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

 1.6 字符组简记法

  • \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] 表示匹配任意字符

1.7 字符组运算

Java和.NET中提供,其他语言不支持。

  • [[a-z] && [^aeiou]] Java写法
  • [a-z-[aeiou]] .NET写法

1.8 POSIZ字符组

不打算看。。。

第2章 量词

2.1 一般形式

  • ^\d\d\d\d$ == ^\d{3}$
  • {n} 之前的元素必须出现n次
  • {m,n} 之前的元素最少m次,最多出现n次
  • {m,} 之前的元素最少出现m次,最多无上限
  • {0,n} 之前的元素可以不出现,也可以出现,最多出现n次

 2.2 常用量词

  • * == {0,} 可能出现,也可能不出现,出现次数没有上限
  • + == {1,} 至少出现1次,出现次数没有上限
  • ? == {0,1} 至多出现1次,也可能不出现

例子

  • ^travell?er "traveler" -> True
  • ^travell?er "traveller" -> True
  • ^<[^>]+> "<blod>" -> True
  • ^<[^>]+> "</table>" -> True
  • ^<[^>]+> "<> -> False"

HTML tag

  • <[^/][^>]*> open tag: <b> <div>
  • </[^>]+> close tag: </b> </div>
  • <[^>]+/> self close tag: <img /> <img src="/a.jpg" />
  • <[^>]+> all the tag

2.3 数据提取

  • re.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"]

2.4 点号

点号可以匹配任意字符,但是不能匹配换行符\n,如果想要匹配任意字符,可以:

  1. 指定单行匹配模式
  2. 使用自制的通配字符组[\s\S],或者[\d\D],或者[\w\W]

例子

  • ^.$ "\n" -> False
  • (?s)^.$ "\n" -> True
  • ^[\s\S]$ "\n" -> True`

2.5 滥用点号的问题

匹配优先量词(贪婪量词): 在不确定是否要匹配时,先尝试匹配的选择

  • ".*"
  • "[^"]*"

2.6 忽略优先量词

忽略优先量词(懒惰量词):在不确定是否要匹配时,先尝试不匹配的选择,先匹配到最近的

    • *? 可能不出现,也可能出现,出现次数没有上限
    • +? 至少出现1次,出现次数没有上限
  • ? ?? 至多出现1次,也可能不出现
  • {m,n} {m,n}? 出现次数最少为m次,最多为n次
  • {m,} {m,}? 出现次数最少为m次,最多没有上限
  • {,n} {,n}? 可能不出现,也可能出现,最多出现n次

例子

C语言中的注释

  • 单行注释: //.*
  • 多行注释: /\*[\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' 

2.7 转义

在正则表达式中,+*?等作为量词具有特殊意义,如果只希望表示这些字符本身,需要只用转义,在它们之前加上\即可。{m,n}的转义只需要写成\{m,n}即可。忽略优先量词则需要将2个量词全部转义,*?必须要写成\*\?

第3章 括号

3.1 分组

身份证号码的匹配

  • [1-9]\d{13,16}[0-9x] 错误
  • [1-9]\d{14}(\d{2}[0-9x])? 正确

匹配13或16位的数字

  • \d{13,16} 错误
  • \d{13}(\d{3})? 正确

匹配HTML open tag

  • <[^/][^>]*[^/]> 错误,会错过 <u> 这种形式
  • <[^/]([^>]*[^/])?> 正确

匹配URL "/foo"或"/foo/bar.php"或"/foo/bar_qux.php"

  • /[a-z]+/?[a-z]*_?[a-z]*(\.php)? 错误
  • /[a-z]+(/[a-z]+(_[a-z]+)?\.php)? 正确

Email地址

  • ^[-\w.]{0,64}@([-a-zA-Z0-9]{1,63}\.)*[-a-zA-Z0-9]{1,63}$

3.2 多选结构

身份证号码的匹配

  • [1-9]\d{14}(\d{2}[0-9x])? 正确
  • ([1-9]\d{14}|[1-9]\d{14}\d{2}[0-9x]) 正确,采用多选结构

匹配IPV4的地址

  • ^([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这样的数值
  • re.search(r"^((00)?[0-9]|0?[0-9]{2}|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$", "02") 这个也能匹配 ??恩,是0?[0-9]{2}这个匹配到的
  • re.search(r"^((00)?[0-9])$", "02") 这个是不匹配的
  • re.search(r"^0?[0-9]{2}$", "02") 这个是匹配的

匹配月,日,小时,分钟

  • (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}

准确的HTML tag匹配

  • <('[^']*'|"[^"]*"|[^'"])+>

注意

  1. 没有括号的多选结构,^ab|cd$ 字符串开头的ab或字符串结尾的cd,而不是只包含ab或cd的字符串
  2. 多选分支并不等于字符组,分支更麻烦,效率不高,不能表示无法匹配的字符,但是分支可以写的很复杂,字符组可能不能满足需求
  3. 多选分支的排列是由讲究的,当几个分支都可以匹配时,结果可能会不同

3.3 引用分组

捕获型分组,分组编号取决于开括号出现的顺序

提取日期

  • (\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

3.3.1 反向引用

使用\1等来使用反向引用。

  • ^([a-z])\1$ "aa" True
  • ^([a-z])\1$ "dd" True
  • ^([a-z])\1$ "ac" False

匹配成对的tag

  • <([^>]+)>[\s\S]*?</\1>

3.3.2 各种引用的记法

表达式中的反向引用出javaScript是$num外,.NET, Java, PHP, Python, Ruby都是\num,替换中的.NET, Java, Javascript是$num, PHP, Python, Ruby都是\num

注意

  • $0可以表示第0个分组(也就是整个表达式匹配的文本),但是\0不行
  • \10是表示第10个捕获分组,还是第1个捕获分组后跟着字符0?
  • Python中表示第10个分组,如果想要表示后一种,则可以使用\g<num>0
  • PHP使用\${num}\${num}0解决这个问题
  • Java, Ruby, JavaScript中,如果确实存在第10个捕获分组,则\10引用此捕获分组的文本,否则认为是后一种
  • 上面的方式无法解决这类问题:存在编号为10的分组,无法用\10表示编号为1的分组和字符0

3.3.3 命名分组

  • (?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>

3.4 非捕获分组

括号的作用:

  1. 分组,将相关的元素归拢到一起,构成单个元素
  2. 多选结构,规定可能出现的多个子表达式
  3. 引用分组,将子表达式匹配的文本存储起来,供之后使用

非捕获分组:(?:),只限定量词的作用范围,不捕获任何文本

  • (\d{4})-(\d{2})-(\d{2}) "2012-12-11" .group(2) -> 12
  • (?:\d{4})-(\d{2})-(\d{2}) "2012-12-11" .group(1) -> 12

3.5 补充

转义:

  1. +, *, ? -> \+, \*, \?
  2. {6}, [a-z] -> \{6}, \[a-z]
  3. (|) -> \(\|\)

URL Rewrite

IIS:

  • "^blog/([0-9]{4})/([0-9]{2})/?$"
  • "blog/post.php?year={R:1}&month={R:2}"

Apache:

  • RewriteEngine on
  • RewriteRule ^blog/([0-9]{4})/([0-9]{2})/?$ blog/post.php?year=$1&month=$2 [L]

nginx:

  • rewrite ^blog/([0-9]{4})/([0-9]{2})/?$ blog/post.php?year=$1&month=$2 last;

一个例子

  • (\w+\.?)+ "aaa.bbb.ccc" ["ccc"]
  • (\w+\.?)+? "aaa.bbb.ccc" ['aaa.', 'bbb.', 'ccc']

第4章 断言

结构匹配的文本不真正匹配文本,只负责判断在某个位置左/右侧的文本是否符合要求,这种结构被称为断言。常见的断言有3类,单词边界,行起始/结束位置,环视。

4.1 单词边界

  • 单词边界记为\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")

4.2 行起始/结束位置

  • 多行模式 (?m)
  • 提取每行的第1个单词 (?m)^\w+
  • 只匹配整个字符串的起始位置 (?m)\A\w+
  • 行起始符 ^
  • 行结束符 $
  • 匹配最后一个单词 \w+$
  • \Z和\z不受多行模式的影响,在任何情况下都匹配整个字符串的结束位置
  • \Z等价于默认模式(非多行模式)下的$,如果字符串末尾有行终止符,则它匹配换行符之前的位置
  • \z则不管行终止符,只匹配整个字符串的结束位置
  • ^和$, \A和\z
  • Python不支持\z,但是Python的\Z等价于其他语言的\z
  • JavaScript没有\A,\z,\Z,只有^和$,其中$只能匹配字符串/行的结束位置
  • 去除行首空白 (?m)^\s+
  • 去除行尾空白 (?m)\s+$
  • 不能使用多选结构,(^\s+|\s+$),它会合并多个行什么的。。
  • JavaScript的$匹配的是行尾,不能匹配末尾行终止符之前的那个位置

多行文本替换


plainText = "line1\nline2\nline3"
print re.sub(r"(?m)$", "</p>", re.sub(r"(?m)^","<p", plainText))

4.3 环视

  • (?!/) 当前位置之后(右侧),不能出现/能匹配的文本
  • (?<!/) 当前位置之前(左侧),不能出现/能匹配的文本
  • html open tag <(?!/)('[^']*'|"[^"]*"|[^'">])+(?<!/)>

名字                    判断方向
肯定顺序环视       (?=...)
否定顺序环视       (?!…)
肯定逆序环视       (?<=…)
否定逆序环视       (?<!…)
  • 123456 -> 123,456 (?<=\d)(?=(\d{3})+(?!\d))
  • 去掉中英文混排中的空白字符 (?<![a-zA-z])\s+(?![a-zA-Z])

4.4 补充

4.4.1 环视的价值

  • open tag <(?!/)[^>]+(?<!/)>
  • 邮政编码 (?<!\d>)\d{6}(?!\d)
  • 辅音字母 (?![aeiou])[a-z]

4.4.2 环视与分组编号

  • 环视中出现了捕获型括号,会影响分组

4.4.3 环视的支持程度

  • JavaScript和ruby 1.8 不支持逆序环视
  • 其他语言支持顺序环视,但是对逆序环视支持都有限制,除了.NET

4.4.4 环视的组合

  • 环视中包含环视
  • 并列多个环视 (^(?=\d+)(?!999)) 之后是数字,但是不能是999
  • 环视作为多选分支排列在多选结构中 ^((?!\d)|(?=\d\D)) 要么不是数字字符,要么是一个数字字符和一个非数字字符

4.4.5 断言和反向引用之间的关系

  • 反向引用时不会保留断言的判断

第5章 匹配模式

匹配模式就是匹配时使用的规则,常用的匹配模式有4中,不区分大小写模式,单行模式,多行模式和注释模式。

5.1 不区分大小写模式

5.1.1 模式的指定方式

  • (?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

5.2 单行模式

单行模式影响的是点号的匹配规则,在默认模式下,点号可以匹配除换行符之外的任何字符,在单行模式下,点号也可以匹配换行符。

  • (?s)
  • .NET RegexOptions.Singleline
  • Java Pattern.DOTALL
  • JavaScript 不支持此模式
  • PHP /regex/s
  • Python re.S re.DOTALL
  • Ruby Regexp::MULTILINE /regex/m

5.3 多行模式

多行模式影响的是^$的匹配,早默认模式下,^$匹配的是整个字符串的起始位置和结束为止,但在多行模式下,它们也能匹配字符串内部某一行文本的起始位置和结束为止。

  • 匹配数字开头的行 (?m)^\d.*

5.4 注释模式

  • 语法 (?#comment)
  • Java和JavaScript不支持
  • x模式,(eXtended mode)
  • 使用多个模式,使用|运算法,如Pattern.COMMENTS | Pattern.MULTILINE

5.5 补充

5.5.1 更多的模式

  • Java Pattern.UNIX_LINESPattern.CANON_EQ
  • Python re.Ure.A

5.5.2 修饰符的作用范围

  • 出现在最开头,则表示整个正则表达式都指定此模式
  • 出现在之中,则表示此模式从这里开始生效

5.5.3 失效修饰符

  • (?-modifer) 某个模式生效到此处为止 Python和JavaScript不支持
  • ((?i)foo|zero)BAR
  • (?i)(foo|zee)(?-i)BAR

5.5.4 模式与反向引用

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 true

5.5.5 冲突策略

Java中,模式修饰符具有更高的优先级

5.5.6 哪种方式更好

第6章 其他

6.1 转义

6.1.1 字符串转义与正则转义


字符串文字  字符串/正则文字 正则表达式
\\n                    \n                    回车
\\t                    \t                    tab
\\\\                    \\                    \
  • \b 退格符
  • \b \b 单词边界
  • 尽量使用原生字符串或者正则文字
  • 反斜线/ 在字符串文字中必须写成\

6.1.2 元字符的转义


记法          转义          说明
[]               \[]               只对开括号转义
.               \.               
-               \-               [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}

```

汉字的表示

  • [\u4e00-\u9fff] Unicode环境
  • [\xb0-\xfe][\x00-\xff] gbk环境

7.6 Unicode属性

  • Unicode Property 字符的功能
  • Unicode Block 所属代码区段
  • Unicode Script 书写系统

7.7 Unicode属性列表

7.8 POSIX字符串

第8章 匹配原理

8.1 有穷自动机

必须满足的4个条件:

  1. 具有有限多个状态
  2. 有一套状态转移函数(规则)
  3. 有一个开始状态
  4. 有一个或多个最终状态

8.2 正则表达式的匹配过程

  • 确定型有穷自动机 DFA
  • 非确定型有穷自动机 NFA

8.3 回溯

8.4 NFA和DFA

第9章 常见问题的解决思路

9.1 关于元素的3中逻辑

9.1.1 必须出现

  • tag中,必须出现的字符是<>
  • Email地址中,必须出现的是@

9.1.2 可能出现

  1. 使用量词
  2. 使用字符组或者多选结构
  3. 匹配数字字符: (+?(\d+|\.\d+|\d+\.\d+)|-?(\d+|\d+\.\d+))

9.1.3 不能出现

  • 双引号:"[^"]*"
  • html tag:[^/][^>]*
  • 邮件地址:\A\w([-\w.](?!\.\.)){0,63}\Z

9.2 正则表达式的常见操作

9.2.1 提取

完成,精确,效率

9.2.2 验证

返回true和false即可

9.2.3 替换

使用字符串或者replacement函数

9.2.4 切分

将字符串切分为数组

9.3 正则表达式优化建议

9.3.1 使用缓存

保存为变量,避免重复编译

9.3.2 尽量准确表达意图

使用最合适(专用)的结构

9.3.3 避免重复匹配

  • 避免a+a+[0-9]+\w+等表达式
  • 凡类似regex1*regex2的表达式,避免regex1regex2能匹配相同的内容
  • 凡类似(reg1|reg2)的表达式,避免reg1reg2能匹配相同的内容

9.3.4 独立出文本和描点

  • 提取出普通的字符串,可以提高效率
  • 将量词,文本,多选分支中的重复部分提取出来

9.4 别过分依赖正则表达式

9.4.1 彻底的放弃字符串操作

使用startwithendwith等方法

9.4.2 思维定势

不一定非要用正则表达式

9.4.3 正则表达式不能完成所有的任务

使用正则表达式和其他手段配合解决问题

第12章 JavaScript

12.1 预备知识

/\d/.test("1") true

12.2 正则功能详解

12.2.1 列表

12.2.2 字符组

[]

12.2.3 字符组简记法

\d, \D, \w, \W, \s, \S 均采用ASCII匹配规则

12.2.4 单词边界

\b匹配成功只有1中情况,一边必须保证\w匹配成功,一边必须保证\w匹配不成功

12.2.5 行起始/结束位置

  • ^指定行起始位置,多行模式(m)下,^可以匹配字符串内部的文本行的开始位置 /^2/m.test("1\n2") true
  • $同理,/1$/m.test("1\n2") true

12.2.6 环视

  • JavaScript1.5之后均支持肯定顺序环视(?=...)和否定顺序环视(?!...)
  • 不支持肯定逆序环视和否定逆序环视

12.2.7 匹配模式

  • 支持不区分大小写模式(i)和多行模式(m)
  • 因为不支持单行模式,所以点号在任何情况下都不能匹配换行符,可以使用[\s\S][\d\D]

12.2.8 捕获分组的引用

  • 内部使用\num记法
  • 替换时使用$num记法
  • 若要在内部表示$字符,则需转义为$$

12.3 正则API简介

12.3.1 RegExp

  • /regexp/或者new RegExp()
  • RegExp.souce 只读,保存文本形式的表达式
  • RegExp.global 只读,是否指定了全局模式
  • RegExp.ignoreCase 只读,是否指定了多行模式
  • RegExp.multiline 只读,是否指定了多行模式
  • RegExp.lastIndex 可写变量,如果使用了全局模式,这个变量保存的是在字符串中尝试下次匹配的偏移值,在RegExp.tesst()RegExp.exec()中都会用到这个变量

12.3.1.1 RegExp.exec(string)

  • 在字符串中寻找匹配,如果成功,则返回表示匹配信息的数组,否则返回null。
  • 如果不指定全局模式,将if语句改为while语句,那么每次调用RegExp.exec()都会从字符串起始位置开始尝试匹配,结果会造成无穷循环。
  • 如果不把正则表达式保存在一个变量中,则每次都会重新生成,使用while语句同样会造成死循环

12.3.1.2 RegExp.test(string)

  • 返回布尔值表示是否在字符串中找到了匹配
  • RegExp.test(string)匹配成功之后,各捕获分组的文本已经保存了下来,用RegExp.$1RegExp..$2等就可以获得

12.3.2 String

12.3.2.1 string.match(RegExp)

  • 该方法类似RegExp.exec(string)
  • 在指定了全局模式下,该方法返回一个字符串数组,包含各次成功匹配的文本,不包含其他信息
  • 即使正则表达式中包含捕获分组,该方法返回的数组中,也不包含分组捕获的信息

12.3.2.2 string.search(RegExp)

  • 这个方法用来寻找某个正则表达式在字符串中第一次出现匹配成功的位置,不成功返回-1
  • 只能找到第一次匹配的位置,即使指定了全局模式,结果也不会有任何变化

12.3.2.3 string.replace(RegExp, replacement)

  • 将RegExp能替换的文本替换为replace
  • 默认只进行一次替换,使用全局模式(g),将全部替换
  • $num 表示对应捕获分组匹配的文本, $$ 表示$字符, $`` 正则表达式所匹配文本之前(左侧)的文本,$'` 正则表达式所匹配文本之后(右侧)的文本

12.3.2.4 string.replace(RegExp, function)

  • function是一个函数,参数是匹配的文本

12.3.2.5 string.split(RegExp)

  • 使用正则表达式切分字符串,是否使用全局模式对结果没影响
  • 可以设置第2个参数limit,指定返回数组的数目

12.4 常用操作示例

略。。

12.5 ActionScript

略。。