ワン❀リード
私がしていた(E)ゼロベースの学校れる一見複雑な正規表現を分割する方法を知っている、演算子の優先順位を理解した後に、この記事では標準構造と共通の演算子を説明しました。あなたが読むことができ、通常の時計に加えて、定期的に書くことが多い方が重要です。その後、定期的な顔をした多くの問題を記述して行く定期的に使用するとき?定期の精度を確保するには?定期的にどのようにパフォーマンスを向上させるには?出発の三点から、この記事では、だから、私たちはより良い、通常の書き込みの前提を書いてみましょう。
私は読むことを学習している記事の定期的なシリーズの前で言った 「JavaScriptの正規ミニブック」彼を送っ研究ノートは、テキスト、グラフィック、すべての定期的な使用されているregulex生産。そして、この資料の冒頭!
II❀は、通常の使用ではないでしょうか?
あなたはこのタイトルには、どのようにも定期的に定期的に使用してはならないさを学ぶ不思議に思われる必要があります参照してください?しかし、実際の開発では、問題は、実際には、また解決するための他の方法を使用することができ、定期的に解決することができます。我々は、それは、すべての問題を解決するため、通常の使用するために定期的に剛性の希望を学ぶために、他の慣行のおそらくより良い利用を持っていないのですか?
たとえば、私たちは今、フィールド持っている2019年12月24日私は日付出ていると思いますが、あなたが達成するために、パケット取得方法との定期的な一致を使用することができます。
VaRの結果=「2019-12-24'.match(/ ^(\ D {4}) - (\ D {2}) - (\ D {2})$ / )。 console.log(正規表現$。 1、正規表現$ 2、正規表現$ 3)。// 2019年12 24
行うには、他の事はありませんか?文字列のことを忘れてはいけないスプリットのような方法を、切断:
VaRの ARR = '2019-12-24'.split(' - 」); console.log(ARR [ 0]、ARR [1]、ARR [2])。// 2019年12 24
やる対照的に、あなたはもっとシンプルだと思いますか?
再び、我々は文字列が含まれているかどうかを確認したい「:」我々は、定期的な実装を使用することができます。
VaRの結果= /\:/.exec('12:34' ); console.log(結果)。// [ ":"、インデックス:2、入力: "12時34分"、基:未定義]
単純なアプローチは、我々は直接のindexOfチェックインデックスを使用するか、またはすることができます-1でない場合は、最初に一致した文字インデックスを返すことがある場合。
VaRの結果= '12:34'.indexOf( ":" ); console.log(結果)。// 2
メソッドの文字列、通常の使用に比べて、最後のフィールドオフの例を参照してくださいSUBSTRまたは部分文字列がはるかに簡単になります。あなたは疑問を持っている場合はもちろん、これらの2つの方法は、あなたがこの記事のブロガー読むことができます 使用状況やSUBSTRと、サブストリングとスライスとのスプライスの違いを。
var string = "hello,听风是风"; var result = /.{6}(.+)/.exec(string)[1]; console.log(result); //听风是风 var result = string.substr(6); console.log(result); //听风是风 var result = string.substring(6); console.log(result); //听风是风
通过以上三个例子可以看出,在一些更偏于字符操作的情况下,该使用字符串方法就得用,学会灵活变通。
叁 ❀ 正则的准确性
何为准确性,一段正则除了能匹配我们所需要的,还得保证不会匹配那些我们不需要的,假设我们现在要匹配如下三种座机(固定电话)号码,该如何写这个正则呢:
var num1 = '055188888888'; var num2 = '0551-88888888'; var num3 = '(0551)88888888';
科普一下,座机号码由 区号+座机号 组成,且区号长度为3-4位数字且首位数字必须为0,而座机号由7-8位数字组成,且首数字不能为0。
尝试分析上面三种座机号码格式,第一种为区号直接拼号码,第二种使用了拼接符 - ,第三种使用了圆括号包裹区号,很明显这是三种分支情况,所以我们可以先写匹配数字的正则,再加分支条件。
只是匹配数字这也太简单了,不假思索的写出 /^\d{3,4}\d{7,8}$/ ,那么这段正则就是不具备准确性的正则,别忘了我们在前面有提到区号与号码首数字的问题,所以改改应该是这样:
var regexp = /^0\d{2,3}[1-9]\d{6,7}$/;
当然这个正则只能匹配区号直接紧接号码的情况,有拼接符的情况就是这样:
var regexp = /^0\d{2,3}-[1-9]\d{6,7}$/;
带圆括号的格式就是这样:
var regexp = /^\(0\d{2,3}\)[1-9]\d{6,7}$/;
我们仔细对比这三段正则,可以发现正则后半段是完全相同的,区别也只是在前半段,所以将前部分以分支表示,改写正则后应该是这样:
var regexp = /^(?:0\d{2,3}|0\d{2,3}-|\(0\d{2,3}\))[1-9]\d{6,7}$/;
还能不能简写?仔细观察前两种分支情况,一个是无拼接符一个是有拼接符,除此之外其它部分都一样,这不又可以组合成拼接符可有可无的情况了,所以我们再次简化:
var regexp = /^(?:0\d{2,3}-?|\(0\d{2,3}\))[1-9]\d{6,7}$/;
我们简单测试下,发现完全没问题
console.log(regexp.test(num1)); //true console.log(regexp.test(num2)); //true console.log(regexp.test(num3)); //true
说到拼接符可有可无,可能有的同学就想到了,我圆括号也可以写成可有可无,这样正则不是看着更精简了,像这样:
var regexp = /^\(?0\d{2,3}\)?-?[1-9]\d{6,7}$/;
但这样就造成了一个问题,你会发现同时有括号和拼接符,或者说有一半括号的格式都能匹配:
console.log(regexp.test('(0551-88888888')); //true console.log(regexp.test('(0551)-88888888')); //true console.log(regexp.test('0551)88888888')); //true
很明显这不是我们想要的情况,这段正则就缺失了很重要的精准性。
我们来看第二个例子,写一个匹配浮点数的正则,要求能匹配如下几种数据类型:
1.23、+1.23、-1.23
10、+10、-10
.2、+.2、-.2
我们结合这三种数据来做个分析,首先关于正负符号很明显是可有可无,毋庸置疑可以写成 [+-]?;然后是整数部分,可能是多位整数也可能没有,所以是 (\d+)?;最后是小数点部分,因为可能不存在小数点,所以可以写成 (\.\d+)?,所以结合起来就是:
var regexp = /^[+-]?(\d+)?(\.\d+)?$/;
这个正则有个最大的弊端,因为三个条件后面都有?表示可有可无,极端一点,三个都为无,所以这个正则可以匹配空白:
/^[+-]?(\d+)?(\.\d+)?$/.test("");//true
可能有同学敏锐的发现了,.2,+.2这种情况都是整数部分为0的情况,那能不能为写成这样 /^[+-]?(0?|[1-9]+)(\.\d+)?$/ ,很明显也不行,比如10,+10这种整数用到了0,所以无法通过分支来控制0的显示隐藏。
那怎么做呢?还是与匹配座机号码一样,我们针对三种情况分开写正则,比如匹配 "1.23"、"+1.23"、"-1.23",正则可以这样写:
var regexp = /^[+-]?\d+\.\d+$/;
匹配 "10"、"+10"、"-10" 的正则可以写成:
var regexp = /^[+-]?\d+$/;
匹配 ".2"、"+.2"、"-.2" 正则可以写成:
var regexp = /^[+-]?\.\d+$/;
我们提取三个正则的共用部分,很明显就是 [+-]? 这一部分,其它部分采用分支表示,综合起来就是这样:
var regexp = /^[+-]?(\d+\.\d+|\d+|\.\d+)$/;
简单测试,完全没问题:
regexp.test("+.2"); //true regexp.test("-.2"); //true regexp.test("10.2"); //true regexp.test("+10.2"); //true
虽然这种分情况写,再抽出共用部分,将非共用分支表示的做法有点繁琐,但对于正则新手来说确实是最为稳妥保证精准性的做法。
肆 ❀ 正则的效率
在确保正则的精准性之后,剩下的就是如何提升正则的效率性能了(当然对于我这样的新手,能写出来就不错了...)。
如何提升正则性能,我们一般从正则的运行阶段下手,正则完整的运行分为如下几个阶段:编译 --- 设定起始位置 --- 尝试匹配 --- 匹配失败的话,从下一位开始继续第 3 步 --- 最终结果:匹配成功或失败。
我们可以通过下面这个例子模拟这个过程:
var regex = /\d+/g; console.log(regex.lastIndex, regex.exec("123abc34def")); //0 ["123", index: 0, input: "123abc34def", groups: undefined] console.log(regex.lastIndex, regex.exec("123abc34def")); //3 ["34", index: 6, input: "123abc34def", groups: undefined] console.log(regex.lastIndex, regex.exec("123abc34def")); //8 null console.log(regex.lastIndex, regex.exec("123abc34def")); //0 ["123", index: 0, input: "123abc34def", groups: undefined]
是的你没看过,明明都是输出相同的东西,每次输出的内容居然还不一样。这是因为当使用 test 或者 exec 方法且正则尾部有 g 时,比如像上面执行多次,下次执行时匹配的起始位置是从上次失败的位置。说直白点,使用这两个方法就像有记忆功能一样,每次执行都是从上次结束的位置开始,比如我们用match方法就不会有这个问题:
var regex = /\d+/g; console.log(regex.lastIndex, "123abc34def".match(regex));//0 ["123", "34"] console.log(regex.lastIndex, "123abc34def".match(regex));//0 ["123", "34"] console.log(regex.lastIndex, "123abc34def".match(regex));//0 ["123", "34"] console.log(regex.lastIndex, "123abc34def".match(regex));//0 ["123", "34"]
我们就通过上面exec来分析正则执行阶段。第一次执行匹配从字符串索引0开始,因为是全局匹配,所以一直匹配到了3,所以匹配结果为123,匹配到a时因为不满足,所以失败了。
第二次开始就是从上次失败的地方开始,所以是从索引3开始,在经历了abc三次失败后,终于遇到了数字34,匹配成功,再往下走时是d,所以又失败了。
第三次匹配开始的起点就是索引8,但因为def都是字母,全部不符合,匹配结果,最后返回了一个null,此时索引被重置为0。
因为起始位置被重置,所以第四次匹配重复了第一次匹配的操作,又是一轮新的开始。
其实看上面exec的例子就反应出了一个问题,每次执行正则都有记录最后匹配失败的位置供下次匹配使用,回溯也是如此,正则会记录多种可能中未尝试过的状态以便回溯使用,这是非常消耗内存的。我们来综合给出几点优化建议:
1.尽量使用具体的字符来替代通配符,减少回溯
比如我们想匹配 123"abc"456 中的 "abc",使用正则 /"[^"]*"/ 的性能要远高于 /".*"/,使用/"\w{3}"/当然更好。
2.使用非捕获型分组
在介绍分组时我们已经说过,正则会记录每个分组的匹配结果。如果我们的分组只是为了单纯起到匹配的作用,而不喜欢正则默认去帮我们记录分组的匹配结果,可以使用非捕获型分组。
'123abc456'.match(/(\w{3})/); console.log(RegExp.$1);//134 //使用非捕获型分组 '123abc456'.match(/(?:\w{3})/); console.log(RegExp.$1);//为空,未记录
3.独立出确定字符
比如我们有正则 /a+/ 可以修改为 /aa*/,因为后者在匹配时能比前者多确定一个字符,不管是失败还是成功,都能更快一部=步确认。
4.提取分支
我们在介绍匹配座机号码与浮点数已经有阐述这一点,将正则共用部分抽离出来,不同部分作为分支,比如将 /this|that/ 修改为 /th(?:is|at)/,这样能减少重复匹配。
5.减少分支数量,缩小匹配范围
虽然推荐抽出共用后使用分支,但有些特殊分支情况能简写复用的还是推荐简写,比如 /red|read/ 可以修改成 /rea?d/。因为分支如果匹配失败,切换到另一条分支时也需要回溯。
伍 ❀ 总
那么到这里,第六章节所有知识全部介绍完毕了。这一章节主要是站在能写正则的基础上,进一步优化正则写法,提升正则匹配的精准性,以及正则运行的性能。共用部分正则,将不同进行分支算是我读下来最大感触的地方,对于优化而言还是需要一定的实战积累,不过先建立优化的观念也不是坏事。那么就说到这里了,今天圣诞节,本来想早点睡觉,结果又写到12点了....晚安,圣诞快乐,本文结束。