20分钟学会正则表达式

程序员的世界有个笑话,在你遇到困难并决定使用正则表达式的时候,你会发现你又多了个困难。正则表达式是个非常强大的工具,老练的程序员常把它当作最后的杀手锏,一旦使用,必会技惊四座。

正则表达式是一种专门用于定义文本的模式匹配规则的语言(英文称作Regular Expressions,现在计算机界更正规的术语是regex-es)。正则表达式有自己的语法和语法规则,初学者使用起来极易出错,但它的价值使你不能不学。以下是个精简版的教程:

1、匹配单个字符

所有的编程语言都有自己的一套定义和使用正则表达式的方法,尽管各有不同,但本文所列的部分几乎适用于所有场合。这些例子使用JavaScript编写,可在浏览器中运行。

最基本的正则表达式是匹配单个字符,规则如下:

  • 点(.)匹配任何字符,如果想要匹配(.),需要用到转义字符(\)
  • 问号(?)表示其前面的字符是可选的,如果想要匹配(?),同样需要用到转义字符(\)

示例:

var text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit lest. Donec convallis dignissim ligula, et rutrum est elat vistibulum eu.';

/*
 *将会匹配"elit""elat",点可匹配任何字符
 *修饰符"g"表示全局模式,即模式将会被应用于所有的字符串,而
 *不是在发现第一个匹配项时立即停止。
 *如果去掉g,改为(var regex = /el.t/;),则只会匹配"elit"。
 */
var regex = /el.t/g;
console.log( text.match(regex) );


/*
 *将会匹配"est""lest"
 *问号使得"l"是可选的
 */
var regex2 = /l?est/g;
console.log( text.match(regex2) );

程序的运行结果为:

[“elit”,”elat”]
[“lest”,”est”]

2、匹配一组字符

基于前面的示例,我们还可以使用集合来匹配一组特定字符:

  • 集合是置于中括号内的一个或多个字符,比如[abc],它将仅匹配其中的一个字符,此例中只会匹配a或b或c。也可以使用^来否定一组字符,例如[^abc]将会匹配除abc外的任何字符。也可以指定集合的范围,例如[0-9]、[a-z],将会匹配该范围内的所有字符。
  • 正则表达式拥有一些内置集合,来帮助我们快速书写正则表达式。例如[0-9]可以写作 \d ,[^0-9]可以写作 \D。还有一些用于单词字符的集合,例如:匹配a到z及数字和下划线—— \w 和 \W(与\d、\D类似,大写表示“非”);匹配空格包括制表符和换行符——\s 和 \S。

看如下示例:

var text = 'cat car can';

//匹配"cat" 和 "can"
console.log( text.match(/ca[tn]/g) );

//匹配"car",注意^
console.log( text.match(/ca[^tn]/g) );


//这个示例演示匹配数字
text = 'I would like 8 cups of coffee, please.';

//匹配text中的数字,此处为8
console.log('How many cups: ' + text.match( /[0-9]/g ));

//更精简的写法是使用  \d
console.log('How many cups: ' + text.match( /\d/g ));

//匹配除数字外的所有字符,将返回一个非数字数组
console.log( text.match(/\D/g) );

程序运行结果如下:

[“cat”,”can”]
[“car”]
How many cups: 8
How many cups: 8
[“I”,” “,”w”,”o”,”u”,”l”,”d”,” “,”l”,”i”,”k”,”e”,” “,” “,”c”,”u”,”p”,”s”,” “,”o”,”f”,” “,”c”,”o”,”f”,”f”,”e”,”e”,”,”,” “,”p”,”l”,”e”,”a”,”s”,”e”,”.”]

3、匹配单词

大多数时候,我们需要匹配整个单词,而不是单个字符,可通过如下的修饰符来完成的这项匹配,这些修饰符用来重复匹配一个字符或字符集:

  • +,匹配前面的字符或字符集一次或多次;
  • *,匹配前面的字符或字符集0次或多次;
  • {x},表示匹配前面的字符或字符集x次,{x,}表示至少匹配x次,{x, y}表示匹配x-y次;

\b模式比较特殊,用于匹配单词边界,也就是指单词和空格间的位置。例如,“\bh”可以匹配“one hour”中的“h”,但不能匹配“match”中的“h”。

看如下示例:

var text = 'Hello people of 1974. I come from the future. In 2020 we have laser guns, hover boards and live on the moon!';

//匹配句中的年份,\d+可匹配一个或多个数字
var yearRegex = /\d+/g;
console.log('Years: ', text.match( yearRegex ) );

//匹配所有以大写字母开头,句号或感叹号结尾的句子
/*
 *问号表示匹配模式是非贪婪的
 *非贪婪模式尽可能少的匹配所搜索的字符串
 *默认的贪婪模式则尽可能多的匹配所搜索的字符串
 *若此处去掉?,则匹配结果是整个text
 */
var sentenceRegex = /[A-Z].+?(\.|!)/g;
console.log('Sentences: ', text.match(sentenceRegex) );

//找到所有以h开头的单词,i修饰符表示不区分大小写
//g修饰符前面已经提过
/*
 * \b 匹配单词边界
 * 若去掉则单词the中的he也会被匹配到
 */
var hWords = /\bh\w+/ig;
console.log('H Words: ', text.match(hWords) );

//找出所有长度为4-6的单词
var findWords = /\b\w{4,6}\b/g;
console.log( 'Words between 4 and 6 chars: ', text.match(findWords) );

//找出所有长度大于5的单词
console.log( 'Words 5 chars or longer: ', text.match(/\b\w{5,}\b/g) );

//找出所有长度为6的单词
console.log( 'Words exactly 6 chars long: ', text.match(/\b\w{6}\b/g) );

程序运行结果如下:

Years: [“1974″,”2020”]
Sentences: [“Hello people of 1974.”,”I come from the future.”,”In 2020 we have laser guns, hover boards and live on the moon!”]
H Words: [“Hello”,”have”,”hover”]
Words between 4 and 6 chars: [“Hello”,”people”,”1974″,”come”,”from”,”future”,”2020″,”have”,”laser”,”guns”,”hover”,”boards”,”live”,”moon”]
Words 5 chars or longer: [“Hello”,”people”,”future”,”laser”,”hover”,”boards”]
Words exactly 6 chars long: [“people”,”future”,”boards”]

4、匹配/校验整行

在JavaScript中,这种模式会被呗用来验证用户输入的文本字段。除了使用^开头(行开头),结尾将保证模式跨越整个文本长度,而不只是匹配其中的一部分。

此外,这种情况下我们可以使用正则对象的test()方法来测试字符串是否匹配,匹配则返回true,否则返回false。

看如下示例:

//下面是一组字符串,我们来提取出其中的URL
var strings = [
    'http://tutorialzine.com/posts/',
    'this is not a URL',
    'https://google.com/',
    '123461',
    'http://tutorialzine.com/?search=jquery',
    'http://not a valid url',
    'abc http://invalid.url/'
];

//下面这个简单的正则表达式即可完成这项工作
//^开头$结尾表示整个字符串必须都匹配,而不是只有一部分能匹配
var regex = /^https?:\/\/[\w\/?.&-=]+$/;

var urls = [];
for( var i = 0; i < strings.length; i++ ){
    if( regex.test(strings[i]) ){       
        urls.push(strings[i]);
    }
}

console.log('Valid URLs: ', urls);

程序运行结果如下:

Valid URLs: [“http://tutorialzine.com/posts/“,”https://google.com/“,”http://tutorialzine.com/?search=jquery“]

5、查找和替换

正则表达式另一个常见的应用场景是文本的查找和替换,注意两个基本概念:

  • 组(group):一组置于小括号()中的模式,每个组将捕获并保存与其中模式匹配的文本,这些文本可以通过索引进行访问,从11表示第一组。
  • 反向引用:在表达式中也可以反向引用组——反斜杠后跟组索引,从\1开始,只有捕获了文本后,\x中的内容才能确定。见下面的示例,这个功能很少使用,你可以忽略它。

看如下示例:

// 使用反向引用,
// 找出仅由相同字母组成的单词
var text = 'Abc ddefg, hijk lllll mnopqr ssss. Tuv wxyyy z.';
var sameLetterRegex = /\b(\w)\1*\b/g;
console.log( text.match(sameLetterRegex) );

//将"John Smith" 替换为"Smith, John"
//每个组(\w+)匹配一个单词
//可通过索引$访问
var name = 'John Smith';
var nameRegex = /(\w+) (\w+)/;
console.log( name.replace(nameRegex, '$2, $1') );

//对于更高级的操作,我们需要提供JS回调
//例如下面将姓变为大写
var upcasename = name.replace(nameRegex, function(string, group1, group2){
    return group2.toUpperCase() + ', ' + group1;
});
console.log( upcasename );

程序运行结果如下:

[“lllll”,”ssss”,”z”]
Smith, John
SMITH, John

更多资源推荐和深入阅读

如果你读完并掌握了本文中的内容,差不多就可以解决80%正则表达式相关的问题。以下是一些有用的学习资源和工具,可以帮助你更深入地学习:

转载请注明以下信息: