正则表达式 入门

原理

正则引擎

为什么正则能有效,因为有引擎,这和为什么JS能执行一样,有JS引擎

正则的引擎大致可分为两类:DFA和NFA

  • DFA (Deterministic finite automaton) 确定型有穷自动机
  • NFA (Non-deterministic finite automaton) 非确定型有穷自动机,大部分都是NFA

这里的“确定型”指,对于某个确定字符的输入,这台机器的状态会确定地从a跳到b,“非确定型”指,对于某个确定字符的输入,这台机器可能有好几种状态的跳法;这里的“有穷”指,状态是有限的,可以在有限的步数内确定某个字符串是被接受还是发好人卡的;这里的“自动机”,可以理解为,一旦这台机器的规则设定完成,就可以自行判断了,不要人看。

基础知识

正则眼中的字符串——n个字符,n+1个位置

为什么要有字符还要有位置呢?因为位置是可以被匹配的。

“占有字符”和“零宽度”:

  • 如果一个子正则表达式匹配到的是字符,而不是位置,而且会被保存到最终的结果中,那个这个子表达式就是占有字符的,比如 /ha/ (匹配 ha )就是占有字符的;
  • 如果一个子正则匹配的是位置,而不是字符,或者匹配到的内容不保存在结果中(其实也可以看做一个位置),那么这个子表达式是零宽度的,比如 /read(?=ing)/ (匹配 reading ,但是只将read放入结果中,下文会详述语法,此处仅仅举例用),其中的(?=ing)就是零宽度的,它本质代表一个位置。

占有字符是互斥的,零宽度是非互斥的。也就是一个字符,同一时间只能由一个子表达式匹配,而一个位置,却可以同时由多个零宽度的子表达式匹配。举个栗子,比如/aa/是匹配不了a的,这个字符串中的a只能由正则的第一个a字符匹配,而不能同时由第二个a匹配(废话);但是位置是可以多个匹配的,比如/\b\ba/是可以匹配a的,虽然正则表达式里有2个表示单词开头位置的\b元字符,这两个\b是可以同时匹配位置0(在这个例子中)的

控制权和传动

控制权是指哪一个正则子表达式(可能为一个普通字符、元字符或元字符序列组成)在匹配字符串,那么控制权就在哪。

传动是指正则引擎的一种机制,传动装置将定位正则从字符串的哪里开始匹配。

正则表达式当开始匹配的时候,一般是由一个子表达式获取控制权,从字符串中的某一个位置开始尝试匹配,一个子表达式开始尝试匹配的位置,是从前一子表达匹配成功的结束位置开始的

语法

要用某类常见字符——简单元字符

  • '.' 匹配除了换行符以外的任意字符,也即是[^\n],如果要包含任意字符,可使用(.|\n)
  • \w (whatever) 匹配任意字母、数字或者下划线,等价于[a-zA-Z0-9_],在deerchao的文中还指出可匹配汉字,但是\w在JS中是不能匹配汉字的
  • \s (space) 匹配任意空白符,包含换页符\f、换行符\n、回车符\r、水平制表符\t、垂直制表符\v
  • \d 匹配数字
  • \un (Unicode) 匹配n,这里的n是一个有4个十六进制数字表示的Unicode字符,比如\u597d表示中文字符“好”,那么超过\uffff编号的字符怎么表示呢?ES6的u修饰符会帮你。

要表示出现次数(重复)——限定符

  • a*表示字符a连续出现次数 >= 0 次
  • a+表示字符a连续出现次数 >= 1 次
  • a?表示字符a出现次数 0 或 1 次
  • a{5}表示字符a连续出现次数 5 次
  • a{5,}表示字符a连续出现次数 >= 5次
  • a{5,10}表示字符a连续出现次数为 5到10次 ,包括5和10

匹配位置——定位符和零宽断言

  • \b 匹配单词边界位置,准确的描述是它匹配一个位置,这个位置前后不全是\w能描述的字符,所以像\u597d\babc是可以匹配“好abc”的。
  • ^ 匹配字符串开始位置,也就是位置0,如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 '\n' 或 '\r' 之后的位置
  • $ 匹配字符串结束位置,如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 '\n' 或 '\r' 之前的位置

想表达“或”的意思——字符簇和分歧

字符簇可用来表达字符级别的“或”语义,表示的是方括号中的字符任选一:

  • [abc]表示a、b、c这3个字符中的任意一个,如果字母或者数字是连续的,那么可以用-连起来表示,[b-f]代表从b到f这么多字符中任选一个
  • [(ab)(cd)]并不会用来匹配字符串“ab”或“cd”,而是匹配a、b、c、d、(、)这6个字符中的任一个,也就是想表达“匹配字符串ab或者cd”这样的需求不能这么做,要这么写ab|cd。但这里要匹配圆括号本身,讲道理是要反斜杠转义的,但是在方括号中,圆括号被当成普通字符看待,即便如此,仍然建议显式地转义
  • 分歧用来表达表达式级别的“或”语义,表示的是匹配|左右任一表达就可:
    ab|cd会匹配字符串“ab”或者“cd”
  • 会短路,回想下编程语言中逻辑或的短路,所以用(ab|abc)去匹配字符串“abc”,结果会是“ab”,因为竖线左边的已经满足了,就用左边的匹配结果代表整个正则的结果

想表达“非”的意思——反义

  • \W、\D、\S、\B 用大写字母的这几个元字符表示就是对应小写字母匹配内容的反义,这几个依次匹配“除了字母、数字、下划线外的字符”、“非数字字符”、“非空白符”、“非单词边界位置”
  • [aeiou]表示除了a、e、i、o、u外的任一字符,在方括号中且出现在开头位置的表示排除,如果在方括号中不出现在开头位置,那么它仅仅代表字符本身

贪婪和非贪婪

在限定符中,除了{n}确切表示重复几次,其余的都是一个有下限的范围。

在默认的模式(贪婪)下,会尽可能多的匹配内容。比如用ab*去匹配字符串“abbb”,结果是“abbb”。

而通过在限定符后面加问号?可以进行非贪婪匹配,会尽可能少地匹配。用ab*?去匹配“abbb”,结果会是“a”。

不带问号的限定符也称匹配优先量词,带问号的限定符也称忽略匹配优先量词。

JS 中的正则

字面量, 构造函数和工厂符号都是可以的:

         /pattern/flags
         new RegExp(pattern [, flags])
         RegExp(pattern [, flags])

参数
pattern
正则表达式的文本。
flags
如果指定,标志可以具有以下值的任意组合:

  • g 全局匹配;找到所有匹配,而不是在第一个匹配后停止

  • i 忽略大小写

  • m 多行; 将开始和结束字符(^和$)视为在多行上工作(例如,分别匹配每一行的开始和结束(由 \n 或 \r 分割),而不只是只匹配整个输入字符串的最开始和最末尾处。

  • u Unicode; 将模式视为Unicode序列点的序列

  • y 粘性匹配; 仅匹配目标字符串中此正则表达式的lastIndex属性指示的索引(并且不尝试从任何后续的索引匹配)。

有两种方法来创建一个RegExp对象:一是字面量、二是构造函数。要指示字符串,字面量的参数不使用引号,而构造函数的参数使用引号。因此,以下表达式创建相同的正则表达式

        /ab+c/i;
        new RegExp('ab+c', 'i');
        new RegExp(/ab+c/, 'i');

方法

RegExp.prototype.exec()

exec() 方法在一个指定字符串中执行一个搜索匹配。返回一个结果数组或 null。

        var matches = /h./.exec('This is a hello world!');
        console.log(matches);        // [ 'hi', index: 1, input: 'This is a hello world!' ]

RegExp.prototype.test()

test() 方法执行一个检索,用来查看正则表达式与指定的字符串是否匹配。返回 true 或 false。

当你想要知道一个模式是否存在于一个字符串中时,就可以使用 test()(类似于 String.prototype.search() 方法),差别在于test返回一个布尔值,而 search 返回索引(如果找到)或者-1(如果没找到)

        let str = 'hello world!';
        let result = /hello/.test(str);
        console.log(result);
        // true

例子

使用正则改变数据结构

下例使用 replace 方法 (继承自 String)去匹配姓名 first last 输出新的格式 last, first。脚本中使用 $1 和 $2 指明括号里先前的匹配.

        var re = /(\w+)\s(\w+)/;
        var str = "John Smith";
        var newstr = str.replace(re, "$2, $1");
        print(newstr);          //"Smith, John"

在多行中使用正则表达式

        var s = "Please yes\nmake my day!";
        s.match(/yes.*day/);
        // Returns null
        s.match(/yes[^]*day/);
        // Returns 'yes\nmake my day'

使用正则表达式和 Unicode 字符

\w 或 \W 只会匹配基本的 ASCII 字符;如 'a' 到 'z'、 'A' 到 'Z'、 0 到 9 及 '_'。为了匹配其他语言中的字符,如西里尔(Cyrillic)或 希伯来语(Hebrew),要使用 \uhhhh,"hhhh" 表示以十六进制表示的字符的 Unicode 值

        var text = "Образец text на русском языке";
        var regex = /[\u0400-\u04FF]+/g;

        var match = regex.exec(text);
        print(match[1]);  // prints "Образец"
        print(regex.lastIndex);  // prints "7"

        var match2 = regex.exec(text);
        print(match2[1]);  // prints "на" [did not print "text"]
        print(regex.lastIndex);  // prints "15"

        // and so on

从 URL 中提取子域名

        var url = "http://xxx.domain.com";
        print(/[^.]+/.exec(url)[0].substr(7)); // prints "xxx"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容