这篇文忠主要分为四部分:
第一部分:主要以JavaScript中的regular expression(中文名:正则表达式)为模板介绍其基本语法;
第二部分:介绍JavaScript中的regular expressio 中的应用
第三部分:介绍Java中的regular expression的应用
第四部分:介绍书写expression 的技巧
第一部分基本上是翻译自:JavaScript中的RegularExpression
正则表达式中的特殊字符
字符 | 含义 |
---|---|
\ |
根据下面的规则来匹配: 反斜杠后跟一个非特殊字符,这个字符就有了特殊含义而不是从文字字面上去解释,比如 ‘b‘ 前面没有‘\‘的时候,匹配词句中的小写字母‘b‘, 但是如果前面加上‘\‘将不会匹配任何字符,它用来构成特殊的单词边界,见‘\b‘; 如果反斜杠后面跟一个特殊字符,表明这个字符失去其相应的特殊含义,表示这个字符本身。比如 一个模式:/a*/ 借助‘*‘ 可以匹配0 个或多个‘a‘, 但相对而言,模式/a\*/ 就失去了‘*‘的含义,而只对类似‘a*‘的字符串有效。 在使用 RegExp("pattern")时不要忘记了反斜杠本身,因为在字符串中它也是一个转义字符。 |
^ |
匹配输入单词的开始,如果多行标志设置为true,那么它也会去匹配换行符后面的字符串。 例如/^A/ 不会匹配"an A"中的‘A‘, 而却会匹配"An E"中的‘A‘. ^有不同的含义,当你在自定义模式下使用的时候。 |
$ |
匹配输入单词的结尾,如果多行标志设置为true,那么它也会去匹配换行符前面的字符串。 比如/t$/ 不会匹配"eater"中的‘t‘,而会匹配"eat"中的‘t‘ |
* |
匹配该字符前面的表达式0 到 多次,其等价方式是{0,} 比如,/bo*/匹配"A ghost booooed"中的‘boooo‘和"A bird warbled"中的‘b‘,但是却匹配不了"A goat grunted"中的任意字符。 |
+ |
匹配该字符前面的表达式1 到 多次,其等价方式是{1,} 比如 模式/a+/匹配"candy"中的‘a‘和"caaaaaaaandy"中的全部的‘a‘,但对"cndy",却没有任何匹配结果。 |
? |
匹配该字符前面的表达式0 或1 次,其等价方式是{0,1} 比如模式/e?le?/匹配"angle"中的‘el‘和"angle"中的‘le‘以及"oslo"中的‘l‘ 如果在任何量词:*,+,?,或{}后面使用该字符,将会使该量词以非贪婪模式匹配(尽可能少量的匹配字符),否则,按照默认情况就会以贪婪模式匹配。 比如 模式/\d+/ 会匹配到"123abc"中的‘123‘, 但是如果将模式改成/\d+?/就只能匹配到‘1‘了! 在预测情况时也会用到这个字符,见下文! |
. |
这个小数点,匹配除了换行符之外的任意单个字符。 比如模式/.n/匹配"nay,an apple is on the tree"中的‘an‘和‘on‘,注意,和‘nay‘没有关系。 |
(x) |
匹配‘x‘并且记住这个匹配,这个括号叫做捕捉括号。 比如模式/(foo)(bar)\1\2/中的 ‘(foo)‘和‘(bar)‘匹配字符串"foo bar foo bar"中的头两个单词,\1和\2匹配尾两个单词。 注意\1,\2是regular expression的一部分。 |
(?:x) |
匹配‘x‘却不对该匹配记忆,这个括号叫做非捕获括号。 考虑一个简单的表达式:/(?:foo){1,2}/,如果将表达式写成/foo{1,2}/, 那么{1,2}将仅仅对"foo"中的最后一个‘0‘有效,而如果加上这个非捕获括号,{1,2}将会对整个单词"foo"有效。 |
x(?=y) |
只有当表达式x后面跟上y时,便会匹配到x,这个叫做,预测未来! 比如,/Jack(?=Spark)/ 将会匹配到"Jack",条件是它的后面跟着"Spark".模式/Jack(?=Sprat|Frost)/ 会匹配到‘Jack‘,只有当后面跟着"Sprat"或者"Frost",但在结果中是找不到"Sprat"和"Frost"的。 |
x(?!y) |
和尚一个相反,这个只要x的屁股后面没有y跟着,便会匹配x,这个称作:否定未来。 例如:/\d+(?!\.)/ 匹配一个数,条件是它的后面不能有小数点。 如果将这个表达式/\d+(?!\.)/.exec("3.141")匹配的结果会是‘141‘,而不是‘3.141‘. |
x|y |
匹配x和y中的任意一个 |
{n} |
匹配该字符前面的表达式,条件是该表达式必须严格的连续出现n次。其中n必须是一个正整数。 比如: /a{2}/在"candy"中找不到任何匹配结果,在"caandy"中会找到全部的‘a‘, 而在"caaandy"中只能找到前两个‘a‘. |
{n,m} |
这里必须满足:n<=m,并且n,m 都是正整数。当前面的表达式至少出现n次最多出现m次的时候将会匹配到。如果将m略去,表示∞. 比如/a{1,3}/在"cndy"中找不到结果,在"candy"中找到一个‘a‘,在"caandy"中找到两个‘a‘,在"caaaaandy"找到头三个‘a‘,注意当‘a‘的个数更多,也只能匹配到头三个。 |
[xyz] |
这是一个字符集,这种类型的模式匹配方括号中的任意一个字符,包括转义字符。特殊字符像(.)和(*)在字符集中就不再特殊,所以也不再需要转义,你可以用连字符来指定一个范围的字符。 比如[a-d] 与[abcd]是等价的,同样都能匹配到"brisket"中的‘b‘和"city"中的‘c‘. 模式/[a-z.]+/和/[\w.]+/都会匹配到"test.i.ng"整个字符串。 |
[^xyz] |
求余字符集。匹配任何没有在方括号中的。同样也可以用连字符定义一个范围。一切在字符集中能正常工作的在这里也可以。 比如[^abc]与[^a-c]是等价的。将会匹配到"brisket"中的‘r‘和"chop"中的‘h‘. |
[\b] |
匹配退格符(U+0008).(不要和\b搞混了)。 |
\b |
匹配单词的边界,单词的边界指该位置的前后都没有其他单词字符,注意匹配到的单词的边界并不包含在匹配中,也就是说匹配的结果的长度是0.(A word 比如:/\bm/ 匹配到"moon"中的‘m‘,(注意,m并不是匹配结果,而仅仅是匹配条件,表示一‘m‘开头,而不是要找到‘m‘) /oo\b/不会匹配到"moon"中的‘oo‘,因为它后面还跟着一个属于单词的字符‘n‘ /oon\b/将会匹配到"moon"中的‘oon‘ /\w\b\w/将不会匹配到任何东西,因为 一个单词字符后面绝不可能同时跟着单词字符和非单词字符。(\w\b匹配的是词尾,词尾后面不可能有单词字符) |
\B |
匹配 a non-word boundary,其匹配的位置前后的字符必须是相同的类型,要么全是words,要么全是non-words.字符串的开始和结尾被认为是non-words. 比如/\B../ 匹配到"noonday"中的‘oo‘,/\B./匹配到"possibly yesterday"中的‘ye‘. |
\d |
匹配一个数字字符,等价于[0-9] 例:/\d/或[0-9]会匹配到"B2 is the suite number."中的‘2‘. |
\D |
匹配一个非数字字符,等价于[^0-9] 例:/\D/或[^0-9]会匹配到"B2 is the suite number."中的‘B‘. |
\f | 匹配换页符(U+000C) |
\n | 匹配换行符(U+000A) |
\r | 匹配回车符(U+000D) |
\s |
匹配一个单个的空白字符,包括空格键, tab键, 换页符, 换行符.等价于[\f\n\r\t\v\u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff] 例:/\s\w*/匹配"foo bar"中的‘ bar‘. |
\t | 匹配tab键(U+0009) |
\v | 匹配垂直制表符(U+000B) |
\w | 匹配任意字母字符,包括下划线 , 等价于[A-Za-z0-9_] |
\W |
匹配任意 non-word 字符 等价于[^A-Za-z0-9_] 例如:/\W/或[^A-Za-z0-9_]匹配到"50%"中的‘%‘. |
\n |
这里的n要求是正整数,这是一个反向引用,引用到的是整个正则表达式中的n个圆括号中的子表达式。 例: /apple(,)\sorange\1/ 匹配结果是"apple, orange, cherry, peach."中的‘apple, orange,‘. |
\0 | 匹配到null(U+0000)字符,不要在它的后面跟任何数字,否则就会变成八进制的转义字符。 |
\xhh | 用两个编码hh去匹配一个字符(hh是两个十六进制的数字) |
\uhhhh | 用四个编码hhhh去匹配一个字符(hhhh是四个十六进制的数字) |
\u{hhhh} | (只有当u的标志是一个集合),用Unicode值hhhh去匹配一个字符(h是十六进制的数) |
第二部分:js中regular expression的工作
js中正则表达式的声明方式有两种:
var re = /pattern/flag;
或者调用构造函数
var reg = new RegExp(/pattern/,‘flag‘); // 或者 var regx = new RegExp(‘pattern‘, ‘flag‘);
其中flag的值有五个,这里只说常用的三个:
g:表示全局匹配
i :表示忽略字母的大小写
m:表示开启多行模式
在js中正则表达式用于RegExp 的方法test 和exec 以及String的方法match,replace, search, 和split
方法 | 描述 |
exec | RegExp中的方法,用于在String中查找匹配,并将查找的信息以数组的形式返回。 |
test | RegExp中的方法,用于检测String中的匹配,并返回true,或false; |
match | String中的方法,用于查找对应的匹配,根据结果返回数组,null或错误匹配。 |
search | String中的方法,用于检测string中的匹配并返回其下标,如果匹配失败,返回-1. |
replace | String中的方法,查找string中的匹配,并将匹配的子string用指定的string替换 |
split | String中的方法,根据正则表达式,或固定的字符串将原来的String分割,并返回分割后的数组。 |
exec方法使用,见代码:
function testExec(){ // 方案一// 方案一 var myRe1 = /d(b+)d/g; var myArray1 = myRe1.exec("cdbbdbsbz"); console.log(myArray1); // 方案二 var myArray2 = /d(b+)d/g.exec("cdbbdbsbz"); console.log(myArray2); //方案三 var myRe3 = new RegExp("d(b+)d", "g"); var myArray3 = myRe3.exec("cdbbdbsbz"); console.log(myArray3); /** * 结果: * ["dbbd", "bb", index: 1, input: "cdbbdbsbz"] */ }
结果说明,在输出的数组中,第一个字符串是匹配的结果,第二个字符串是记忆的匹配,而 index:1,表示从0开始索引的匹配在原字符串中的下标(也就是第一个字符串的起始位置对应在原字符串中的下标),最后,input:str, 表示输入的字符串。
在方案二中显示的,你可以利用正则表达式对象的初始化器来创建一个正则表达式,而不是将它赋给一个变量,这样的话,它的每一次出现都是一个新的正则表达式。出于这个原因,如果你真的这么做了,那你将无法得到正则表达式随后而来的属性。请看下面的例子:
var myRe = /d(b+)d/g; var myArray = myRe.exec("cdbbdbsbz"); console.log("The value of lastIndex is " + myRe.lastIndex); // " lastIndex 的值是 5" 这个属性表示下次匹配的起始位置。
但,如果:
var myArray = /d(b+)d/g.exec("cdbbdbsbz"); console.log("The value of lastIndex is " + /d(b+)d/g.lastIndex); // " lastIndex 的值是 0"
这里 模式 /d(d+)d/g 出现了两次,但不是同一个正则表达式对象,因此它们的lastIndex property 的值是不一样的。所以,如果你想得到正则表达式对象的初始化器的属性,那你应该先将它赋值给一个变量。
test()方法,返回传入的string中是否包含有对应的匹配模式。同样,这种方式初始化的pattern包含有正则表达式对象的属性。
function testTest(){ var pattern = /java/i; var a = pattern.test("JavaScript"); // 返回 true console.log(a); var b = pattern.test("ECMAScript"); // 返回 false console.log(b); }
match方法:
语法:string.match(regexp);
方法match()将检索字符串string,以找到一个或多个与regexp匹配的文本。这个方法的行为很大程度上依赖于regexp是否具有性质g。
function testMatch1(){ var str = "1 plus 2 equals 3"; var reg1 = /\d+/g; var reg2 = /\d+/; var result1 = str.match(reg1); var result2 = str.match(reg2); console.log(result1); //结果 : ["1", "2", "3"]----------这个是全局匹配的结果 console.log(result2); // 结果: ["1", index: 0, input: "1 plus 2 equals 3"] }
function testMatch2(){ var url1 = /\w+:\/\/[\w.]+\/\S*/; var url2 = /(\w+):\/\/([\w.]+)\/(\S*)/; var text = "Visit my home page at http://www.isp.com/~david"; var result1 = text.match(url1); var result2 = text.match(url2); console.log(result1); // 结果: ["http://www.isp.com/~david", index: 22, input: "Visit my home page at http://www.isp.com/~david"] console.log(result2); // 结果: ["http://www.isp.com/~david", "http", "www.isp.com", "~david", index: 22, input: "Visit my home page at http://www.isp.com/~david"] }
从testMatch2()的执行结果来看,与exec() 无二。遗憾的是match方法并不能将正则表达式对象的属性赋予参数中的正则表达式变量。
replace()方法
语法:text.replace(reg,replacement);第一个参数是正则表达式,第二个参数是用于替换的内容,返回一个用replacement替换了text中匹配内容的新的字符串,如果使用了全局匹配模式,那么text中所有匹配的子串都会被替换,否则只替换匹配到的第一个。
注意当replacement中出现了$,便有其特殊的含义:
字符 |
Replacement |
---|---|
$1, $2, ... $99 |
与regexp中的第1到第99个子表达式相匹配的文本 |
$& |
与regexp相匹配的子串 |
$` |
位于匹配子串左侧的文本 |
$‘ |
位于匹配子串右侧的文本 |
$$ |
直接量$符号 |
function testReplace(){ var text = "javascript is a programming language which is my love!"; var a = text.replace(/javascript/i, "Java"); console.log(a); /** * 结果是:"Java is a programming language which is my love!" */ var b = text.replace(/javascript/i,"$‘"); console.log(b); /** * 结果是: " is a programming language which is my love! is a programming language which is my love!" * 注意两个 ‘is‘前面都有空格。 */ }
seacher()方法
语法:string.search(regex);返回 string中第一个与regexp相匹配的子串的起始位置。如果没有找到任何匹配的子 串,则返回-1。
function testSearch(){ var s = "JavaScript is fun"; var a = s.search(/script/i); // 返回 4 console.log(a); var b = s.search(/a(.)/g); // 返回 1 console.log(b); }
注意全局模式在这里无效。另外split()方法,估计用烂了吧!
用圆括号的子匹配:
在正则表达式中引入圆括号,则对应的子匹配将会被(姑且叫做正则表达式的解析器)记忆。比如 /a(b)c/ 匹配字符串"abc",同时‘b‘将会被记忆,可以通过数组元素[1].........[n]来再次访问对应的圆括号匹配的子串。在一个正则表达式中并没有限制这样的子串的数量。其返回的数组持有所有的匹配的子串。下面的这个例子将会说明如何使用圆括号子串。
var re = /(\w+)\s(\w+)/; var str = "John Smith"; var newstr = str.replace(re, "$2, $1"); console.log(newstr); // 结果:Smith, John
第三部分:java中的正则表达式