Explanation of common characters in regular expressions


1. What is a regular expression?

Regular expression (Regular Expression, regex or regexp, abbreviated as RE), also translated as regular expression, regular expression, is a character pattern used to match specified characters during the search process.

Many programming languages ​​support string manipulation using regular expressions.

The concept of regular expressions was originally popularized by Unix tool software (such as sed and grep).

Programs that support regular expressions such as: locate|find|vim|grep|sec|awk

Second, the use of regular expressions

1. Matching mailboxes, matching ID numbers, mobile phone numbers, bank card numbers
2. Matching certain specific strings, doing specific processing, etc.

3. Explanation of regular nouns

  • metacharacter

Refers to those dedicated characters that have special meanings in regular expressions, such as: point (.), star (*), question mark (?), plus sign (+), etc.

  • leading character

Characters located in front of metacharacters, such as abc*aooo, abc is the leading character.

(1) Metacharacters commonly used in regular expressions

metacharacter Function illustrate
\ Mark the next character as a special character, or a literal character, or a backreference, or an octal escape. For example: 'n' matches the character 'n', '\n' matches a newline. The sequence '\' matches '' ; '\ (' matches '('
. Matches any single character except newline '\n' For example: 'ab.' matches 'ab8', 'abu'; 'f.o' matches any character between the letters 'f' and 'o', such as 'fao', 'f9o', 'f#o';' .end' matches any character before the character end. To match any character including '\n', use a pattern like '[.\n]'.
* Leading characters appear 0 or more times in a row For example: 'ab*' matches 'abbbbbba' or 'a', which means that a or b appears 0 times or consecutively. * is equivalent to {0,}
.* any length character Example: 'ab.*' matches 'abfbfbf'
^ start of line (begins with) ^root
$ end of line (ends with) bash$
^$ blank line
[] Matches any single character or group of single characters within parentheses For example: [abc] can match 'a' in 'plain'
[^] Matches any single character or group of single characters not included in the parentheses For example: [^abc] can match 'p' in 'plain'.
^[] Match starts with any single character or group of single characters enclosed in parentheses For example: ^[12] can match '1', '123', '213'
^ [^] match does not begin with any single character or group of single characters in parentheses ^ [^abc]

(2) Other commonly used metacharacters in regular expressions

metacharacter Function illustrate
< take the head of the word
> take the end of the word
<> exact match <hello>
{n} Matches leading characters appearing n times in a row [0-9]{3}
{n,} Matches at least n occurrences of the leading character [a-z]{4,}
{n,m} Match between n and m occurrences of the leading character go{2,4}
() save the matched characters (hello)
\b Match word boundaries, that is, the position between a word and a space. For example: 'er\b' can match 'er' in "never", but not 'er' in "verb".
\B Matches non-word boundaries. 'er\B' matches 'er' in 'verb', but not 'er' in 'never'.
\d Match digits (grep -P) [0-9]、grep -P \d+
\D match non-digit [^0-9]
\w Match alphanumeric underscores (grep -P) [a-zA-Z0-9_]
\W matches any character except \w [^a-zA-Z0-9_]
\s Matches any whitespace character, including formfeed, newline, carriage return, tab, etc. (grep -P) [\f\n\r\t\v]
\S any character except \s

(3) Metacharacters commonly used in extended classes in regular expressions

metacharacter Function illustrate
+ Match one or more leading characters, at least once. For example: 'bo+' matches 'boo', 'bo', but not 'b'. + is equivalent to {1,}
? 匹配零个或一个前导字符,至多一次。 例如:‘bo?’ 匹配 ‘b’、‘bo’。? 等价于{0,1}
| 例如:‘^root | ^ftp’ 匹配" root开头" 或者 “ftp开头” 的字符
() 组字符(看成整体) (my|your)self:表示匹配myself或匹配yourself
{n} 前导字符重复n次 例如:‘o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。
{n,} 前导字符重复至少n次 例如:‘o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。
{n,m} 前导字符重复n到m次,其中n <= m。请注意在逗号和两个数之间不能有空格。 例如: “o{1,3}” 将匹配 “fooooood” 中的前三个 o。‘o{0,1}’ 等价于 ‘o?'。

(四)非贪婪匹配

元字符 功能 说明
当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。 例如,对于字符串 “oooo”,‘o+?’ 将匹配单个 “o”,而 ‘o+’ 将匹配所有 ‘o’。
(?:pattern) 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 “或” 字符 (|) 来组合一个模式的各个部分是很有用 例如: ‘industr(?:y | ies) 就是一个比 ‘industry|industries’ 更简略的表达式。
(?=pattern) 正向先行断言,非获取匹配。代表字符串中的一个位置,紧接该位置之后的字符序列能够匹配pattern 例如: ‘Windows (?=95|98|NT|2000)’ 能匹配 “Windows 2000” 中的 “Windows” ,但不能匹配 “Windows 3.1” 中的 “Windows”。
(?!pattern) 负向先行断言,非获取匹配。代表字符串中的一个位置,紧接该位置之后的字符序列 不能够匹配 pattern 例如:‘Windows (?!95|98|NT|2000)’ 能匹配 “Windows 3.1” 中的 “Windows”,但不能匹配 “Windows 2000” 中的 “Windows”。
(?<=pattern) 正向后行断言,非获取匹配。代表字符串中的一个位置,紧接该位置之后的字符序列能够匹配pattern ,与正向肯定预查类似,只是方向相反。 例如:“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”
(?<!pattern) 负向后行断言,非获取匹配。代表字符串中的一个位置,紧接该位置之后的字符序列 不能够匹配 pattern,与正向否定预查类似,只是方向相反。 例如:“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。
  • 如何理解正向与负向?
    正向:从左往右按顺序匹配
    负向:从右往左按逆序匹配
  • 如何理解锚点?
    锚点不会匹配实际的文本,而是寻找特定的位置,锚点会去查看前后字符是否符合你的要求,但是并不占用字符。
  • 关于顺序问题
    正则表达式的先行断言和后行断言因为零宽度的子表达式是非互斥的,只匹配某些位置,在匹配过程中,不占用字符,所以被称为"零宽"。
    最后匹配的都是同一个位置,所以先后顺序是不影响最后的匹配结果的,可以任意组合,只是习惯上把逆序环视写在左侧,顺序环视写在右侧。

(?=pattern) :顺序环视,匹配右侧文本。
1、 例如:对 “a regular expression” 这个字符串,要想匹配 regular 中的 re,但不能匹配 expression 中的 re。
限定了 re右边 的位置,这个位置之后是 gular,可以用 re(?=gular) 表达,该表达式并不消耗 gular 这些字符。

2、如果(?=pattern)放在限定字符前面,例如正则表达式: “(?=Jeffery)Jeff”
可以理解为位置定在J(锚点)开始,从左往右循环匹配,但右侧必须是Jeffery,也说是只匹配Jeffery里的Jeff,其余的Jeff不匹配了。

select 'Jeffery' regexp ('(?=Jeffery)Jeff');
> true --首先(?=Jeffery)取得控制权,从位置0开始匹配,向右侧查找6个位置,匹配“Jeffery”成功。控制权交给“Jeff”,从位置0开始匹配,匹配“Jeff”成功。匹配结果位置为6,匹配结果成功。
select 'Jeff' regexp ('(?=Jeffery)Jeff');
> false --首先(?=Jeffery)取得控制权,从位置0开始匹配,匹配不成功。

(?!pattern):否定顺序环视,不匹配右侧文本。
1、例如:对 “regex represents regular expression” 这个字符串,要想匹配 除 regex 和 regular 之外的 re。
限定 re右边 的位置,这个位置不是字符 g,可以用 re(?!g) 表达。

2、如果(?!pattern)放在限定字符前面,例如正则表达式: “(?!Jeffery)Jeff”
可以理解为位置定在J开始,从左往右循环匹配,但右侧必须不是Jeffery,也说不匹配Jeffery里的Jeff,其余的Jeff是匹配

select 'Jeffery' regexp ('(?!Jeffery)Jeff');
> false --从J开始,右侧是Jeffery,所以是false。
匹配过程:首先(?!Jeffery)取得控制权,从位置0开始匹配,向右侧查找7个位置,子表达式“Jeffery”匹配成功,(?!Jeffery)匹配失败。
控制权交给“Jeff”,从位置0开始匹配,匹配“Jeff”成功,因为“Jeffery“要求不能出现,所以为false。
select 'Jeff' regexp ('(?!Jeffery)Jeff');
> true  --从J开始,右侧不是Jeffery,所以是true。

匹配过程:首先(?!Jeffery)取得控制权,从位置0开始匹配,向右侧查找7个位置,子表达式“Jeffery”匹配失败,(?!Jeffery)匹配成功。
控制权交给“Jeff”,从位置0开始匹配,匹配“Jeff”成功,因为“Jeffery“要求不能出现,所以为true。
select 'acb' regexp ('(?!c)ab');
> false --从a开始,右侧不是c,所以是false。
匹配过程:首先(?!c)取得控制权,从位置0开始匹配,向右侧查找2个位置,子表达式“c”匹配成功,(?!c)匹配失败。 是对当前所在位置右侧附加的条件
控制权交给“a”,从位置0开始匹配,匹配“a”成功,(?!c)是对当前所在位置右侧附加的条件,因为“c“要求不能出现,所以为false。

(?<=pattern) :逆序环视,匹配左侧文本。
1、例如:对 regex represents regular expression 这个字符串,有 4 个单词,要想匹配单词内部的 re,但不匹配单词开头的 re。
单词内部的 re ,在 re 前面应该是一个单词字符,可以用(?<=\w)re 表达。

2、如果(?<=pattern)放在限定字符后面,例如正则表达式: “Jeffery(?<=Jeffery)”
可以理解为位置定在y开始,从右往左循环匹配,但左侧必须是Jeffery

select 'Jeffery' regexp ('Jeffery(?<=Jeffery)');
> true 
匹配过程:首先Jeffery取得控制权,从位置0开始匹配,向右侧查找7个位置,在位置6表达式“Jeffery”匹配成功。
控制权交给“(?<=Jeffery)”,从位置6开始匹配,是对当前所在位置左侧附加的条件,要求等于“Jeffery”,匹配成功,所以为true。
select 'Jefferyabc' regexp ('Jeffery(?<=Jeffery)');
> true
匹配过程:首先Jeffery取得控制权,从位置0开始匹配,向右侧查找7个位置,在位置6表达式“Jeffery”匹配成功。
控制权交给“(?<=Jeffery)”,从位置6开始匹配,是对当前所在位置左侧附加的条件,要求等于“Jeffery”,匹配成功,所以为true。
select 'abcJeffery' regexp ('Jeffery(?<=Jeffery)');
> true
匹配过程:首先Jeffery取得控制权,从位置0开始匹配,向右侧查找10个位置,在位置9表达式“Jeffery”匹配成功。
控制权交给“(?<=Jeffery)”,从位置9开始匹配,是对当前所在位置左侧附加的条件,要求等于“Jeffery”,匹配成功,所以为true。
select 'abcJeffery' regexp ('(?<=Jeffery)Jeffery');
> false 
匹配过程:首先(?<=Jeffery)取得控制权,从位置0开始匹配,向右侧查找7个位置,在位置9子表达式“Jeffery”匹配成功,(?<=Jeffery)匹配成功,匹配结果位置为9。
控制权交给“Jeffery”,从位置9开始匹配,右侧没有任何内容,匹配失败,所以为false。

(?<!pattern) :否定逆序环视,不匹配左侧文本。
1、例如:对 “regex represents regular expression” 这个字符串,要想匹配单词开头的 re。
指不在单词内部的 re,即 re 前面不是单词字符。可以用 (?<!\w)re 表达。

2、如果(?<!pattern)放在限定字符后面,例如正则表达式: “Jeffery(?<!Jeffery)”
可以理解为位置定在y开始,从右往左循环匹配,但左侧必须不是Jeffery,也说不匹配Jeffery本身以及“其他字符+Jeffery”是不匹配

select 'Jeffery' regexp ('Jeffery(?<!Jeffery)');
> false
匹配过程:首先Jeffery取得控制权,从位置0开始匹配,向右侧查找7个位置,在位置6表达式“Jeffery”匹配成功,匹配结果位置为6。
控制权交给(?<!Jeffery),从位置6开始匹配,是对当前所在位置左侧附加的条件,要求不等于“Jeffery”,匹配成功,所以为true。
select 'JeaJeff' regexp ('JeaJeff(?<!Jeff)');
> false --从f开始,从右往左循环匹配,左侧是Jeff,所以false。
匹配过程:首先JeaJeff取得控制权,从位置0开始匹配,向右侧查找7个位置,在位置6表达式“JeaJeff”匹配成功,匹配结果位置为6。
控制权交给(?<!Jeff),从位置6开始匹配,是对当前所在位置左侧附加的条件,要求不等于“Jeff”,子表达式匹配成功,(?<!Jeff)匹配失败,所以为false。
select 'Jeffery' regexp ('Jeffery(?<!Jeff)');
> true --从y开始,从右往左循环匹配,左侧不是Jeff,所以true。
匹配过程:首先Jeffery取得控制权,从位置0开始匹配,向右侧查找7个位置,在位置6表达式“Jeffery”匹配成功,匹配结果位置为6。
控制权交给(?<!Jeff),从位置6开始匹配,是对当前所在位置左侧附加的条件,要求不等于“Jeff”,子表达式匹配失败,(?<!Jeff)匹配成功,所以为true。
select 'thatthis' regexp ('(.(?<!this)');
  • 如何理解非贪婪匹配不消耗字符?

非贪婪匹配不消耗字符,正则表达式是对给定的字符串进行匹配,也就是说,一般匹配一个字符之后,该字符就被消耗,就不能被Regular Expression的其他部分匹配了。

例如:
select ‘abc’ regexp (‘a(?=b)bc’);
select ‘abc’ regexp (‘a(b)bc’);

答案分别是
true
false

原因:
'a(?=b)bc’中的正向肯定预查(?=b)匹配了a后面的字母b,但是并没有消耗它,所以,后面再跟一个“bc”串,这就完整地匹配了字符串“abc”。其实,它的真正意义应该是确定了这个字母a,因为不是每个字母a后面都会跟一个字母b的!
而a(b)bc因为匹配并消耗了字母a后面的b,再来添加一个“bc”串的时候,就变成了“abbc”,就不能匹配字符串“abc”。

五、运算符优先级

正则表达式从左到右进行计算,并遵循优先级顺序,这与算术表达式非常类似。

相同优先级的从左到右进行运算,不同优先级的运算先高后低。下表从最高到最低说明了各种正则表达式运算符的优先级顺序:

运算符 描述
\ 转义符
(), (?: ), (?=), [] 圆括号和方括号
*, +, ?, {n}, {n,}, {n,m} 限定符
^, $, \任何元字符、任何字符 定位点和序列(即:位置和顺序)
| 替换,“或"操作 。字符具有高于替换运算符的优先级,使得"m|food"匹配"m"或"food”。若要匹配"mood"或"food",请使用括号创建子表达式,从而产生"(m|f)ood"。

六、应用场景

  • 匹配手机号码

1、分析字符串特点

手机号码是数字,并且是以1开头,11位长

2、如何找?

2.1、1开头后面跟着数字的表达方式:“1\d” 或者 “1[0-9]”
2.2、数字长度11位的表达方式"1\d{10} " 或者 “1[0-9]{10}” ( {}里面数字,表示它左边字符可以重复出现次数 )
2.3、所有字符必须是11位,因此头尾直接必须满足条件,因此可以是:^1\d{10}$ 了

  • 匹配QQ号码

1、分析字符串特点

号码是 最少是5位数,首位字符非0,最大长度,目前到11位了

2、如何找?

2.1、先定义首位字符,首位字符是1到9,后面是字符,表示为" [1-9]\d "
2.2、后面字符个数在4到10位,表示为" [1-9]\d{4,10} "
2.3、所有字符串必须都满足上面匹配,表示为" ^ [1-9]\d{4,10}$ "

  • 匹配ip地址

1、分析字符串特点

每节 0-255,中间用”.”分割,一共有4节

2、如何找?

2.1、第一个0-255,可以分解为0-9一位数,10-99两位数,100-199三位数,200-249三位数第2节,250-255,第四节。表示为" [0-9] | [1-9][0-9] | 1[0-9]{2} | 2[0-4][0-9] | 25[0-5] " “|”表示或者,计算优先级最低,左右两边可以是多个元字符普通字符组合字符串为一个整体。
2.2、这种的字符有三次重复,中间用分隔符".“分隔开,因为是点字符是元字符,所有需要转义。表示为 " ([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).”
实际上每家一个()字符,都一个子匹配,会在匹配结果里面出现()内容。这里我们加()目的是,让优先计算,因此不需要里面子匹配内容。我们可以加忽略子匹配内容字符:?: ,结果将变为:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).
2.3、重复三次,表示方式:
方法一:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).
方法二:把第一段作为分组,重复3次 ((?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3} ,然后同样忽略子匹配结果,可以变为:(?: (?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}
2.4、最后还有一段0-255匹配,然后在上面再加上头尾限定符,表示为:^(?: (?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$

  • 匹配以广东省区号开头的固定号码

1、分析字符串特点

广东省区号 020、066+(0660-0663、0668)、075+(0750至0759)、076+(0760-0763、0766、0768-0769)

2、如何写?

除了020,其他区号都是四位数,以0开头,第二位数字6-7,第三、四位数字是0-9,区号后面表示是数字,位数不定。表示为:“(‘^0[ 6-7][0-9]{2}[0-9]*$ | ^020[0-9]*$’)”

  • 非贪婪匹配

我们经常用正则表达式来检测一个字符串中包含某个子串或者要表示一个字符串中不包含某个字符或某些字符( [^…] )。
但如果要表示一个字符串中不包含某个子串(由字符序列构成)呢?[^…]这种形式就做不到了。这时需要用到
用 [^…] 这种形式就不行了,这时就要用到(负向)先行断言或后行断言、或同时使用。

例如:判断一句话中包含 this,但不包含 that。
1、这句话中每个字符的 前面都不是 that 或每个字符的 后面都不是 that。
2、正则表达式如下:

^((?<!that).)*this((?<!that).)*$

^(.(?!that))*this(.(?!that))*$

select 'this is runoob test' regexp ('^((?<!that).)*this((?<!that).)*$');
> true
select 'this is runoob test' regexp ('^(.(?!that))*this(.(?!that))*$');
> true

select 'this and that is runoob test' regexp ('^((?<!that).)*this((?<!that).)*$');
> false
select 'this and that is runoob test' regexp ('^(.(?!that))*this(.(?!that))*$');
> false

如果一句话以that开头,以that结尾,that和this连在一起。比如“ runoob thatthis is the case”或者 “this is the case, not that” 等。^((?<!that).)*this((?<!that).)*$ 就没有办法准确地识别。
原因分析如下:

select 'runoob thatthis is the case' regexp ('^(.(?!that))*this(.(?!that))*$');
> false --可以识别,因为"^(.(?<!that))*" 是指 "任意一个字符+不是that的字符串"

select 'runoob thatthis is the case' regexp ('^((?<!that).)*this((?<!that).)*$');
> true  --无法识别,因为"^((?<!that).)*" 是指 "不是that的字符串+任意一个字符",而thatthis是连在一起的。

select 'runoob thatathis is the case' regexp ('^((?<!that).)*this((?<!that).)*$');
> false --"thatathis" that和this中间有一个a,可以判断是否满足"不是that的字符串+任意一个字符",不满足,因此是false。
select 'this is the case, not that' regexp ('^(.(?!that))*this(.(?!that))*$');
> false

select 'this is the case, not that' regexp ('^((?<!that).)*this((?<!that).)*$');
> true --无法识别,因为"((?<!that).)" 是指 "不是that的字符串+任意一个字符" ,而that是最后一个结尾在一起的。

select 'this is the case, not thata' regexp ('^(.(?<!that))*this((?!that).)*$');
> false

以下正则表达式可以:

^(.(?<!that))*this(.(?<!that))*$

^(.(?<!that))*this((?!that).)*$

^((?!that).)*this(.(?<!that))*$

^((?!that).)*this((?!that).)*$

select 'this is the case, not that' regexp ('^(.(?<!that))*this(.(?<!that))*$');
> false
select 'this is the case, not that' regexp ('^(.(?<!that))*this((?!that).)*$');
> false
select 'this is the case, not that' regexp ('^((?!that).)*this(.(?<!that))*$');
> false
select 'this is the case, not that' regexp ('^((?!that).)*this((?!that).)*$');
> false

select 'runoob thatathis is the case' regexp ('^(.(?<!that))*this(.(?<!that))*$');
> false
select 'runoob thatathis is the case' regexp ('^(.(?<!that))*this((?!that).)*$');
> false
select 'runoob thatathis is the case' regexp ('^((?!that).)*this(.(?<!that))*$');
> false
select 'runoob thatathis is the case' regexp ('^((?!that).)*this((?!that).)*$');
> false
    • 如何理解 ^(.(?<!that))*$ 与 ^((?<!that).)*$
select 'that' regexp ('^(.(?<!that))*$');
> false  --可以理解为任意一个字符往前匹配是否为that,包括that本身。
select 'that' regexp ('^((?<!that).)*$');
> true --因为限定符\*和\$的原因,结尾是限定是"不是that的字符串+任意字符",无法识别单个that。

^((?<!that).)*$
是指"不是that的字符串+任意一个字符"是匹配的,是从任意字符.开始,从右往左,前面不是that,可以匹配that本身的(如果没有贪婪匹配*限定符)。
因为限定符*和$的原因,结尾是限定是"不是that的字符串+任意字符",无法识别以that结尾,显示为true。
^((?<!that).)*$ 可以理解为this后面任意不含有that的字符串,但以that结尾无法识别。

>  select 'thisthat' regexp ('^this((?<!that).)$');
>  false --没有*贪婪匹配,可以识别不含有that字符串。
>  select 'thisthat' regexp ('^this((?<!that).)*$'); 
>  true  -- *贪婪匹配,无法识别只以that结尾的字符。
>  select 'thisthat a' regexp ('^this((?<!that).)*$'); 
>  false 
--如果没有限定符$,就是看this前面是否有that
>  select 'thisthat' regexp ('^this((?<!that).)');
>  true 
>  select 'thatthis' regexp ('this((?<!that).)');
>  false 

^(.(?<!that))*$
是指"任意一个字符+不是that的字符串"是匹配的,是从任意字符.开始,从右往左,匹配了不包含that的字符串,包括that本身。

^this(.(?<!that))*$ 可以理解为this后面任意不含有that的字符串,可以识别以that结尾的情况。

>  select 'thisthat' regexp ('^this(.(?<!that))$'); 
>  false --没有*贪婪匹配,可以识别不含有that字符串。
--this后面任意不含有that的字符串,可以识别以that结尾。
>  select 'thisthat' regexp ('^this(.(?<!that))*$'); 
>  false 
>  select 'thisathat' regexp ('^this(.(?<!that))*$');
>  false
>  select 'this that abc' regexp ('^this(.(?<!that))*$');
>  false
    • 如何理解 ^(.(?!that))*$ 与 ^((?!that).)*$
select 'that' regexp ('^(.(?!that))*$');
> true  --因为限定符\*和\$的原因,结尾是限定是"任意字符+不是that的字符串",无法识别单个that。
select 'that' regexp ('^((?!that).)*$');
> false --可以理解为任意一个字符往前匹配是否为that,包括that本身。

^(.(?!that))*$
是指"任意一个字符+不是that的字符串"是匹配的,是从任意字符.开始,从左往右,后面不是that,可以匹配that本身的(如果没有贪婪匹配*限定符)。
因为限定符*和^的原因,开头是限定是"任意字符+不是that的字符串",无法识别以that开头,显示为true。

^(.(?!that))*this 可以理解为this前面任意不含有that的字符串,但以that开头无法识别。

> select 'thatthis' regexp ('^(.(?!that))this');
> false --没有*贪婪匹配,可以识别不含有that字符串。
> select 'thatthis' regexp ('^(.(?!that))*this');
> true  --that开头无法识别。
> select 'that is this' regexp ('^(.(?!that))*this');
> true  --that开头无法识别。
> select 'runoob thatthis' regexp ('^(.(?!that))*this');
> false

^((?!that).)*$
是指"不是that的字符串+任意一个字符"是匹配的,是从任意字符.开始,从左往右,匹配了不包含that的字符串,包括that本身。

^((?!that).)*this 可以理解为this前面任意不含有that的字符串,可以识别以that开头的情况。

> select 'thatthis' regexp ('^((?!that).)this');
> false --没有*贪婪匹配,可以识别不含有that字符串。
> select 'thatthis' regexp ('^((?!that).)*this');
> false --可以识别以this开头的情况。
> select 'thatathis' regexp ('^((?!that).)*this');
> false --可以识别前面任意不含有that的字符串且that为开头。
> select 'as thatathis' regexp ('^((?!that).)*this');
> false --可以识别前面任意不含有that的字符串。

参考文章:
正则大佬主页面,非常有用:https://blog.csdn.net/lxcnn?type=blog
https://www.zhoulujun.cn/html/theory/algorithm/IntroductionAlgorithms/8427.html 《深入正则表达式(2):元字符/关键子/符号说明》

Guess you like

Origin blog.csdn.net/sodaloveer/article/details/128444428