这个正则中 (?!你)[\u4e00-\u9fa5] 可查询到非“你”的汉字,原理是什么?

// 从头说起:
// 我能简单的理解正则的“前瞻”,下面语句查询了一个:右侧紧邻数字的字母
// 即匹配字母([a-z]),且它的右边必须得是数字\d

'a123, b汉字, c★★'.match(/[a-z](?=\d)/g);
// ["a"]


// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

'你好,你好,世界'.match( /(?!你)[\u4e00-\u9fa5]/g );
//["好", "好", "世", "界"]

// ▲但上边这个正则我没看懂
// 请问1:这把个 (?!你) 放在表达式头部是啥意思?
// 请问2:这个 (?!你)[\u4e00-\u9fa5] 为啥能表示 非“你”的汉字
谢谢老司机指南,感谢帮助!3Q

-

阅读 8k
2 个回答

[\u4e00-\u9fa5] 代表unicode编码规范汉字集合
(?!pattern)代表一个正向否定预检
(?!你)代表你匹配的内容不包括 "你" 这个字
(?!你)[\u4e00-\u9fa5] 就代表匹配所有的 非"你" 汉字了

=============以上是之前的回答,但是感觉有些知识盲点,有必要深究一番,于是有了下面答案==================

[\u4e00-\u9fa5] 代表unicode编码规范汉字集合

这个集合的由来,摘出引用文档的关键几句话:

1984年,ISO的文字编码委员会(ISO/TC 97/SC2)决议制订出一套编码规格(ISO 10646),是以交换文字集的方式来统一处理世界的文字。并成立了工作小组(ISO/TC 97/SC 2/ WG 2)
1993年5月,正式制订了最初的中日韩统一表意文字,位于U+4E00–U+9FFF这个区域,共20,902个字
[\u4E00-\u9FFF] 代表中日韩统一表意文字
而真正20902个汉字的具体位置为U+4E00–U+9FA5.
clipboard.png

所以重点就是 0x4E00–0x9FA5代表汉字编码范围

(?!pattern) 代表一个正向否定预检
但这是一个预检匹配(或者叫零宽断言),预检匹配到的内容是不会被作为整个正则匹配的结果的,是一个匹配条件,为的是匹配出更准确的结果。

这个正向否定预检怎么玩呢?
比如:

"Windows(?!95|98|NT|2000)"能匹配"Windows3.1"中的"Windows",但不能匹配"Windows2000"中的"Windows"

clipboard.png

放在后面正向断言Windows字符串后面不能跟一些字符,否则就不匹配这个Windows。

那么是不是,"(?!windows)PC" 能匹配"macPC"中的"PC",但不能匹配"WindowsPC"中的"PC"

看看结果:

clipboard.png

事实上,"(?!windows)PC" 能匹配"macPC"中的"PC",但也能匹配"WindowsPC"中的"PC"

这又是为什么呢?
这我们要从(?!pattern)这个正则表达式来说说:
引用正则表达式的先行断言(lookahead)和后行断言(lookbehind) 中的说明:

(?!pattern) 负向先行断言
代表字符串中的一个位置,紧接该位置之后的字符序列不能匹配pattern。
例如对”regex represents regular expression”这个字符串,要想匹配除regex和regular之外的re,可以用”re(?!g)”,该表达式限定了re右边的位置,这个位置后面不是字符g。负向和正向的区别,就在于该位置之后的字符能否匹配括号中的表达式。

原来(?!pattern)它的含义是匹配了一个位置。

所谓位置,是指字符串中(每行)第一个字符的左边、最后一个字符的右边以及相邻字符的中间(假设文字方向是头左尾右)。

'你好,你好吗,世界你'.match( /(?!\s)/g ) 就代表了我要匹配全部(匹配全部是因为带了g标识)非空(s代表所有的空字符)字符的位置

clipboard.png

可以看到'你好,你好吗,世界你'正好10个长度,但是位置有11个,也就是我们匹配到数组有11个元素原因,数组中的元素是空字符是因为匹配的结果是位置,没有具体的字符,用11个空字符来表示11个位置。

'你好,你好吗,世界你'这个字符串有 '[1]你[2]好[3],[4]你[5]好[6]吗[7],[8]世[9]界[10]你[11]' 11个位置。

那像题主遇到的以(?!你)开始的匹配又代表什么意思呢?

clipboard.png

这代表去除"你"这个字生成的位置, 去掉三个"你"的三个位置,就剩下8个位置了。

回到题主的问题

'你好,你好吗,世界你'.match( /(?!你)[\u4e00-\u9fa5]/g )

'你好,你好吗,世界你'.match( /(?!你)/g )
匹配到了2,3,5,6,7,8,9,11这8个位置

'你好,你好吗,世界你'.match( /(?!你)[\u4e00-\u9fa5]/g )
(?!你)的位置匹配并不会作为结果被获取,而是作为其他匹配的条件

[\u4e00-\u9fa5]的匹配能够匹配到所有汉字字符
clipboard.png

(?!你)[\u4e00-\u9fa5] 匹配就代表匹配了上面的(?!你)匹配到8个位置后面紧跟的汉字字符。

'[1]你[2]好[3],[4]你[5]好[6]吗[7],[8]世[9]界[10]你[11]'的2,3,5,6,7,8,9,11这8个位置中,只有2,5,6,8,9这五个位置后面的字符有效

clipboard.png

这也就是答案了。

汉字在 UTF-8里的编码段, 起 0x4e00,止9fa5
?表示一个字符, !你表示非你这个字符
?!你表示不能是的任意一个字符
()用来分组, 匹配后,可以用组号引用匹配的字符

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题