javascript - 正規表示式中 ?= 和 ?: 的差異到底在哪裡
天蓬老师
天蓬老师 2017-06-15 09:22:42
0
5
1065

直接上例子:每三個數字中間加逗號

"123456789".replace(/(\d{3})(?:[^$])/g, ",");
//"123,567,9"

"123456789".replace(/(\d{3})(?=[^$])/g, ",");
//"123,456,789"

再上一個之前論壇裡出現過的例子,也是每三個數字中間加逗號

先看看 (?=pattern) 的使用,下面这个是正确的:

function groupByCommas(n) {
  return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
console.log(groupByCommas(1234567));    //1,234,567

如果我们把 ?= 换成 ?: 的话:

function groupByCommas(n) {
  return n.toString().replace(/\B(?:(\d{3})+(?!\d))/g, ",");
}
console.log(groupByCommas(1234567));    //1,

兩者的概念不用多說,查到有回答說:區別在於?= 是正向肯定斷言,進行的匹配是不佔查詢長度的;而?: 是非獲取匹配,進行的匹配是佔據查詢長度的。

但是還是不是很理解這裡的查詢佔據長度的說法,對著例子解釋,難道是說第一個例子(?=[^$])匹配的是非結尾,所以123之後的非結尾的長度最小是1個字符,所以把4給一起替代了?那怎麼不直接取代到結尾呢?第二個例子(?=(\d{3}) (?!\d))匹配的是3或3的倍數個數字,直接配對到了結尾,所以把234567也直接取代了?所以我的理解肯定是不對的

理解的不是很透徹,歡迎各位對著例子來解答一下我的困惑,不勝感激!

天蓬老师
天蓬老师

欢迎选择我的课程,让我们一起见证您的进步~~

全部回覆(5)
刘奇
  1. "123456789".replace(/(d{3})(?:[^$])/g, "$1,");
    ()表示捕獲型括號,(?:)(?表示非捕獲型括號,所以第一個括號匹配的內容會放在$1中,第二個括號匹配的內容不會放在$2中。 d{3}表示連續三個數字,[^$]表示匹配一個字符,只要這個字符不是$符號,需要注意的是[]表示匹配裡面的任意一個字符,但是肯定是要有一個的,所以[]匹配出來的字符的長度肯定是1,不存在0的情況,另外在[$]裡面的$符號是沒有特殊含義的,就是$這個字符,而不是匹配字串的結尾。
    因為d{3}匹配三個字符,​​[^$]匹配一個字符,所以這個正則匹配4個字符;來看匹配過程,首先"1234"是滿足的,"123"匹配d {3},"4"配對[^$],此時$1="123",所以"1234"被替換成"123,"。然後從5開始下次匹配,類似的"5678"滿足條件,$1="567",所以"5678"被替換成"567,"。然後從9開始匹配,下面沒有匹配了,匹配結束,結果為"123,567,9"。

  2. "1234567".replace(/B(?:(d{3})+(?!d))/g, ",");
    B匹配非單字邊界,也是一個位置,沒有寬度,(d{3})+匹配3的倍數數字序列,且個數至少是3個,+是量詞,表示1到多次,預設是貪婪的,貪婪就是盡可能多的匹配,(?!d)表示這個位置後面不是數字。
    看例子,首先B不匹配行首,所以匹配的位置移動到"1"後面的位置,此時B匹配1後面的位置,然後"234", "567"匹配d{3} ,因為是貪婪匹配,所以(d{3})+匹配"234567",然後因為7後面是字符串的結尾了,所以滿足斷言(?!d)不是數字,所以整個正則的匹配結果是"234567",所以"234567"被替換成了","。 1不動,所以"1234567"變成了"1,"。

  3. "123456789".replace(/(d{3})(?=[^$])/g, "$1,");
    這個正規表示式不滿足"千分位加號"的需求,"123456789"只是個特例(位數剛好是3的倍數),換成"12345678"結果是"123,456,78"。

typecho

佔據或"消耗"的意思是說匹配的部分是否可被其他正則 (後面的斷言,或/ /g的下一次) 再匹配。如果"消耗"掉了就不能再配對了。

"123456789".replace(/(\d{3})(?=[^$])/g, (m, ) => `{matched=${m} =${}}`);
// => '{matched=123 =123}{matched=456 =456}789'
// (?=) 第一次没有消耗掉4, 第二次会从456..开始

"123456789".replace(/(\d{3})(?:[^$])/g, (m, ) => `{matched=${m} =${}}`);
// => '{matched=1234 =123}{matched=5678 =567}9'
// (?:) 第一次消耗掉4,第二次会从567..开始

另外例1 [^$]是匹配一個非$的字符,和行末無關。

巴扎黑

(?=456)符合一個位置,這個位置後面跟了456
例如123(?=456)會匹配123456中的123,而不會匹配123457中的123,不佔用後面的並不會被佔用掉。 123456 配對的是123456
, 而123(?=456)456 同樣匹配123456 後面加了 同樣匹配123456 後面加了(56)。 正規中可以用括號改變優先權等,另外,對於加括號的部分,會從左到右分配遞增的分配一個編號,在後面可以用編號引用這一部分匹配到的文本。在JS

replace

裡,替換的部分可以用$1之類的引用這一部分的匹配。 例如(a)1
會匹配兩個連續的a,([A-Z])1匹配兩個連續相同的大小字母,(A-Z)1([a-z])2匹配兩個連續的大小字母,後面跟兩個連續的小寫字母(大小寫字母可以不同)。 有時候,我們只想改變優先權,不想分配編號(很少用到),就用

(?:)

比如(a)(?:b)(c)12
匹配abcac,但是(a)(b)(c)12匹配abcab. http://zonxin.github.io/post/...

世界只因有你

()括住的是可以使用$1(到$9)去配對的

如下面這個,可以使用$1去匹配(捕獲)到括號裡的匹配到的值,

a = '123,456';
a.match(/\d{3}(,)/)

使用(?:)則是不捕獲這一個。就是不能透過$1,去取得括號所得到的值

a.match(/\d{3}(?:,)/)

上面兩個都是匹配出含,的值
來看下面這個,(?=)是用來匹配,但是不出現在結果裡的。下面這個的結果就沒有,

a.match(/\d{3}(?=,)/)

三個程式碼的結果如圖

這個寫法是很有用的,有些內容不希望出現在結果裡,但是不用它又匹配不全,就可以用這個了

世界只因有你

這2個功能毫不相干
模式1pattern1(?=pattern2)正向肯定斷言;模式2(?:pattern3)非捕獲性分組

1.模式1: pattern2 本身不參與匹配,對pattern1的匹配結果(ret1)進行斷言:字符串中 ret1之後的內容是否匹配pattern2?若是,則ret1為模式1匹配結果,若否,則ret1不是模式1匹配結果。當然,不符合pattern1,則不符合模式1

舉例:

  var str1='abc',str2='acb';var reg=/a(?=b)/
  console.log(reg.test(str1),reg.test(str2)) //=>true false
  //因为reg.test(str1)为真,输出匹配结果
  console.log(str1.match(reg)[0])  //=>a

2.模式2 主要用於區別捕獲性分組(pattern4),記為模式3

在數學中小括號用於進行一次優先運算;而模式3 ,除了對代碼進行隔離,pattern4參與了匹
配,且對pattern4的匹配結果進行了存儲

對於模式2(?:pattern3)的非捕獲性分組,則表示不會對pattern3的結果進行存儲,但本身
pattern3參與了匹配,主要用於對代碼進行隔離。也就是要表現()本來的意義,而()在正規
表達式中有了捕獲性分組的定義,於是增加一個?:以示區別。這和轉義符有異曲同工的妙處。

舉例

var  str='abc'
var  reg1=/(\w)(\w)(\w)/
var  reg2=/(\w)(?:\w)(\w)/
//捕获了3次,RegExp.,RegExp.,RegExp....依次存储捕获结果
var ret1=str.match(reg1)
console.log(RegExp.,RegExp.,RegExp.)//=>a b c
var ret2=str.match(reg2)
//捕获了2次
console.log(RegExp.,RegExp.) //=>a c

@luckness 對題主的內容進行了詳細解答,而我對題主標題進行了解答。

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!