正则表达式(下) && 字符串.match方法检查正则表达式中的子字符串是否存在于原字符串,并返回存在的子字符串 && 字符串.replace()方法

正则表达式的test只能检查字符串是否存在,返回true或false,而字符串的match检查是否存在时会返回子字符串。

"Hello, World!".match(/Hello/);
let ourStr = "Regular expressions";
let ourRegex = /expressions/;
ourStr.match(ourRegex);

第一个match返回["Hello"],第二个match返回 ["expressions"]。.match语法与之前使用的.test方法“相反”:

'string'.match(/regex/);
/regex/.test('string');

再看一个例子:

let extractStr = "Extract the word 'coding' from this string.";
let codingRegex = /coding/; 
let result = extractStr.match(codingRegex);  //注意这儿是'字符串.match'而不是'正则表达式.'
console.log(result);
/*控制台输出:
  [ 'coding',
  index: 18,
  input: 'Extract the word \'coding\' from this string.',
  groups: undefined ]*/

如果子字符串在原字符串中多次出现,按上面的办法也只会给出一次:

let testStr = "Repeat, Repeat, Repeat";
let ourRegex = /Repeat/;
let myResult=testStr.match(ourRegex);
console.log(myResult);

/*控制台输出:
  ['Repeat',
  index: 0,
  input: 'Repeat, Repeat, Repeat',
  groups: undefined ]*/

要多次搜索并提取文本形式的话,则要使用全局搜索标志(flag):g。看下面例子:

let testStr = "Repeat, Repeat, Repeat";
let ourRegex = /Repeat/g;
let myResult=testStr.match(ourRegex);
console.log(myResult); //[ 'Repeat', 'Repeat', 'Repeat' ]

看,这样就会全部提取出来。

正则表达式后可以有多个不同的flag(标志):
let twinkleStar = "Twinkle, twinkle, little star";
let starRegex = /Twinkle/ig; //在正则表达式后可以有多个不同的flag(标志);i表示不区分大小写,g表示可多次搜索或提取(全局搜索);不区分前后顺序。
let result = twinkleStar.match(starRegex);  
console.log(result);//[ 'Twinkle', 'twinkle' ]

看,不论大小写、无论几个全部都提取出来了。

除了极端的匹配情况(完全匹配某模式、通配符' . '匹配任何单个字符),正则表达式还有适用于其他情况的形式。

例如,我们想要匹配bag、big、和bug但不匹配 bog,这时可以通过创建正则表达式 /b[aiu]g/ 来实现。[aiu]是仅匹配字符a、i或u的字符类(character class),记得要用中括号括住:

let bigStr = "big";
let bagStr = "bag";
let bugStr = "bug";
let bogStr = "bog";
let bgRegex = /b[aiu]g/;
console.log(bigStr.match(bgRegex)); //[ 'big', index: 0, input: 'big', groups: undefined ]
console.log(bagStr.match(bgRegex)); //[ 'bag', index: 0, input: 'bag', groups: undefined ]
console.log(bugStr.match(bgRegex)); //[ 'bug', index: 0, input: 'bug', groups: undefined ]
console.log(bogStr.match(bgRegex)); //null

看另一个例子。在正则表达式中使用带有元音(a、e、i、o、u)的字符类来查找字符串QuotesSample中的所有元音,不区分大小写:

let quoteSample = "Beware of bugs in the above code; I have only proved it correct, not tried it.";
let vowelRegex = /[aeiou]/ig; // 
let result =quoteSample.match(vowelRegex); //
console.log(result); 
/*控制台输出的结果:
[ 'e',
  'a',
  'e',
  'o',
  'u',
  'i',
  'e',
  'a',
  'o',
  'e',
  'o',
  'e',
  'I',
  'a',
  'e',
  'o',
  'o',
  'e',
  'i',
  'o',
  'e',
  'o',
  'i',
  'e',
  'i' ]
*/
View Code

还有一种情况,当我们需要匹配大范围的字符(例如,字母表中的每个字母)时,就需要大量输入,这很麻烦。所幸有一个内置的特性可以运用。

在字符集内,我们可以使用连字符( - )定义要匹配的字符范围。例如,要匹配小写字母a到e,可以使用[a-e]:

let catStr = "cat";
let batStr = "bat";
let matStr = "mat";
let bgRegex = /[a-e]at/;
catStr.match(bgRegex);
batStr.match(bgRegex);
matStr.match(bgRegex);

上面三个match调用语句将依次返回值[“cat”]、[“bat”]和null。

看另一个例子。要求匹配字符串QuotesSample中的所有字母,不区分大小写:

let quoteSample = "The quick brown fox jumps over the lazy dog.";
let alphabetRegex = /[a-z]/ig; 
let result =quoteSample.match(alphabetRegex) ; 
console.log(result);
/*控制台输出如下:
[ 'T',
  'h',
  'e',
  'q',
  'u',
  'i',
  'c',
  'k',
  'b',
  'r',
  'o',
  'w',
  'n',
  'f',
  'o',
  'x',
  'j',
  'u',
  'm',
  'p',
  's',
  'o',
  'v',
  'e',
  'r',
  't',
  'h',
  'e',
  'l',
  'a',
  'z',
  'y',
  'd',
  'o',
  'g' ]
*/
View Code

连字符(-)匹配字符范围不限于字母,它还可以匹配一系列数字。我们可以在单个字符集(记得字符集要用中括号括住)中组合一系列字母和数字:

let jennyStr = "Jenny8675309";
let myRegex1 = /[a-z0-9]/ig;
let kk=jennyStr.match(myRegex1);
console.log(kk);//[ 'J', 'e', 'n', 'n', 'y', '8', '6', '7', '5', '3', '0', '9' ]

再看一个例子:

let quoteSample = "Blueberry 3.141592653s are delicious.";
let myRegex = /[h-s2-6]/ig; //记得要用中括号括住!i不区分大小写,g全局搜索。 
let result =quoteSample.match(myRegex);  
console.log(result);
/*控制台输出如下:
[ 'l',
  'r',
  'r',
  '3',
  '4',
  '5',
  '2',
  '6',
  '5',
  '3',
  's',
  'r',
  'l',
  'i',
  'i',
  'o',
  's' ]
*/
View Code

也可以创建一组不想匹配的字符,这种类型的字符集称为否定字符集。创建否定字符集的方法是在字符集里加插入符(尖括号):( ^ )。例如 /[^aeiou]/gi 表示匹配所有不是元音的字符。注意,像( )、( ! )、 ( [ )、( @ )、( / )、空格都会被匹配,只要不是元音字母就会被匹配。看个例子:

let quoteSample = "3 blind mice.";
let myRegex = /[^aeiou^0-9]/gi; //匹配非元音、非数字的字符。
let result = quoteSample.match(myRegex); 
console.log(result); //[ ' ', 'b', 'l', 'n', 'd', ' ', 'm', 'c', '.' ]

PS:当插入符不是放在字符集里,而是放在字符集外,插入符就起到搜索字符串开头文字(模式)的作用。看例子:

//例一:
let rickyAndCal = "Cal and Ricky both like racing.";
let calRegex = /^Cal/; //这里的^并不是置于字符集内!故不是取否之意!
let result = calRegex.test(rickyAndCal);
console.log(result); //true 由于字符串开头正是Cal,故返回true。

//例二:
let rickyAndCal = "Ricky and Cal both like racing.";
let calRegex = /^Cal/; //这里的^并不是置于字符集内!故不是取否之意!
let result = calRegex.test(rickyAndCal);
console.log(result); //false 由于字符串开头不是Cal,故返回false。

PPS:想要搜索字符串结尾文字(模式)的符号为美元符号$,看个例子:

let theEnding = "This is a never ending story";
let storyRegex = /story$/;
storyRegex.test(theEnding); //true
let noEnding = "Sometimes a story will have to end";
storyRegex.test(noEnding); //false

当想要匹配至少出现一次或连续重复出现的字符(或一组字符)时,可以使用加号(+)检查是否如此。

例如,/a+/g将在abc中找到一个匹配项并返回[“a”];将在aabc中找到单个匹配项并返回[“aa”];将在abab中找到两个匹配项并返回[“a”,“a”],因为两个a之间有b隔开了,不连续;在bcd中没有a,因此它无法找到匹配项。看个具体例子:

let difficultSpelling = "Mississsipspi";
let myRegex = /s+/g; //注意因为不是匹配字符集,故这儿不用加[]。
let result = difficultSpelling.match(myRegex);
console.log(result); //[ 'ss', 'sss', 's' ]

再看一个例子:

let reCriminals = /C+/; // 此处用/C+[A-Z]*?/、/C+[a-z]*?/也能达到同样的效果。
/*经测试,测试用的字符串匹配此正则表达式返回情况如下:
P1P5P4CCCcP2P6P3返回CCC 
C返回C
CC返回CC
P6P2P7P4P5CCCCCP3P1返回CCCCC
""返回null
P1P2P3返回null
P2P1P5P4CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP3返回CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
*/

当想要匹配出现零次或多次的字符时,可以使用星号:*。

let soccerWord = "gooooooooal!";
let gPhrase = "gut feeling";
let oPhrase = "over the moon";
let goRegex = /go*/;
soccerWord.match(goRegex);
gPhrase.match(goRegex);
oPhrase.match(goRegex);

按照顺序,三个match调用将返回值[“goooooooo”]、[“g”]和null。

默认情况下,正则表达式是贪婪的。例如,对于字符串“titanic”,匹配正则表达式 /t[a-z]*i/ 将返回[“titani”],它可以找到适合该模式的最大子字符串。我们可以使用'?'字符将正则表达式更改为惰性匹配。“titanic”匹配正则表达式 /t[a-z]*?i/ 则会返回["ti"]。(注:应避免用正则表达式解析HTML,但用正则表达式匹配HTML字符串的模式完全可以)看个例子:

要求返回HTML的<h1>标签:

let text = "<h1>Winter is coming</h1>";
let myRegex = /<h*?1>/; // 这里不能将*改为.,不能将*?去掉,也不能单独把*或?去掉。
let result = text.match(myRegex);
console.log(result); //['<h1>']

 JavaScript中与字母表最匹配的字符类是 \w(w为小写)。此快捷方式等于字符集[A-Za-z0-9_ ]。这个字符类匹配大小写字母、数字和下划线字符(_)。即

let longHand = /[A-Za-z0-9_]+/;  let shortHand = /\w+/;是相等的。看个例子:要求使用速记字符类\w计算字符串中的字母、数字、下划线总数:
let quoteSample = "The five .1_1";
let alphabetRegexV2 = /\w/g; //g为字符串内全局搜索标志
let result = quoteSample.match(alphabetRegexV2).length;
console.log(result); //10

当要匹配与\w相反的模式(除字母、数字以外的所有内容,即匹配所有标点符号和空格)时,使用字符类 \W(w为大写)。此快捷方式等于字符集[^A-Za-z0-9_ ]。

看下面例子:

//例一:
let shortHand = /\W/;
let numbers = "42%";
let sentence = "Coding!";
numbers.match(shortHand); //["%"]
sentence.match(shortHand); //["!"]

//例二:
let quoteSample = "The five boxing wizards jump quickly.";
let nonAlphabetRegex = /\W/g; // 
let result = quoteSample.match(nonAlphabetRegex).length;
console.log(result); //6(即5个空格符1个句点符)

当要查找所有数字时,快捷方式是 \d(d为小写)。这等于字符类[0-9],它查找0到9之间任何数字的单个字符。看个例子:使用速记字符类\d计算字符串中数字的位数:

let movieName = "2001: A Space Odyssey";
let numRegex = /\d/g; //g表示对整个字符串进行搜索。
let result = movieName.match(numRegex).length;
console.log(result); //4

当要查找所有非数字字符时,快捷方式是 \D(d为大写)。这等于字符类[^0-9],它查找不是0到9之间任何数字的单个字符。看个例子:

let movieName = "2001: A Space.";
let noNumRegex = /\D/g; //非数字字符包括空格、标点符号、大小写字母
let result = movieName.match(noNumRegex).length;
console.log(result); //10

下面看一个小小的综合应用:

假设要对用户名进行检查:

用户名只能使用字母数字字符;用户名中的唯一数字必须在末尾,最后可以有零个或多个,用户名不能以数字开头;用户名字母可以是小写和大写;用户名必须至少有两个字符长,两个字符的用户名只能使用字母作为字符。

let username = "JackOfAllTrades";
let userCheck = /^[a-z][a-z]+\d*$|^[a-z]\d\d+$/i; //|是或者的意思            
       //使用这个/^[a-z]([0-9]{2,}|[a-z]+\d*)$/i也是可以的。
let result = userCheck.test(username);
console.log(result);//true

使用\s(小写s)不仅可以匹配空格,还匹配回车符、制表符、换页符和换行符,可以将其视为类似于字符类 [ \r\t\f\n\v]。看例子:

let sample = "  Whitespace is important in separating words";
let countWhiteSpace = /\s/g; 
let result = sample.match(countWhiteSpace);
console.log(result); //[' ', ' ', ' ', ' ', ' ', ' ', ' '] 开头的制表符tab会返回2个单独的空格。

使用\S(大写S)可以搜索除空白之外的所有内容,同时也不匹配回车符、制表符、换页符和换行符,类似于字符类[^ \r\t\f\n\v]。看例子:

let sample = "Whitespace is ";
let countNonWhiteSpace = /\S/g; 
let result = sample.match(countNonWhiteSpace);
console.log(result); //[ 'W', 'h', 'i', 't', 'e', 's', 'p', 'a', 'c', 'e', 'i', 's' ]

使用花括号{  }可以匹配一定范围的模式。例如,要只匹配字符串ah中出现3到5次的字母a,正则表达式应该是 /a{3,5}h/ 。看例子——要求只在当有3到6个字母h时,才能匹配上整个字符串"Ohhh no":

let ohStr = "Ohhh no";
let ohRegex = /oh{3,6} no/i; //正则表达式是区分大小写的!若没加i,结果就是false。还有空格也要按原式写上。
let result = ohRegex.test(ohStr);
console.log(result); //true

当花括号只写前面一个数字,后跟一个逗号时,表示至少要有这个数才能匹配。不需要设置上限。看例子:

let A4 = "haaaah";
let A2 = "haah";
let A100 = "h" + "a".repeat(100) + "h";
let multipleA = /ha{3,}h/;
multipleA.test(A4); //true
multipleA.test(A2); //false
multipleA.test(A100); //true

当花括号里只有一个数字时,表示只有此特定数量的才能匹配上。看例子:

let A4 = "haaaah";
let A3 = "haaah";
let A100 = "h" + "a".repeat(100) + "h";
let multipleHA = /ha{3}h/;
multipleHA.test(A4); //false
multipleHA.test(A3); //true
multipleHA.test(A100); //false

' ? '可以检查前面的元素是否为零或一个。即?前面的元素可有可无,有无都会匹配上。看个例子:

let american = "color";
let british = "colour";
let rainbowRegex= /colou?r/;
rainbowRegex.test(american); //true
rainbowRegex.test(british); //true

' (?=...) '表示正面预测检查,(希望...元素在)若存在元素...,则匹配时返回模式的其余部分。否则返回null。

' (?!...) '表示负面预测检查,(不希望...元素在)若不存在元素...,则匹配时返回模式的其余部分。否则返回null。

看例子:

例一:

let quit = "qu";
let noquit = "qt";
let quRegex= /q(?=u)/;//(希望u元素在)若存在u,则匹配时返回模式的其余部分。
let quRegex1= /q(?!u)/;//若不存在u,则匹配时返回模式的其余部分。否则返回null。
let qRegex = /q(?!u)/;//(不希望u元素在)若不存在u,则匹配时返回模式的其余部分。
let qRegex1 = /q(?=u)/;//若存在u,则匹配时返回模式的其余部分。否则返回null。
console.log(quit.match(quRegex)); //["q"]
console.log(quit.match(quRegex1)); //null
console.log(noquit.match(qRegex)); //["q"]
console.log(noquit.match(qRegex1)); //null

例二:

let sampleWord = "astronaut";
let pwRegex = /(?=\w{6,})(?=\D+\d+\d+)/; //长度大于5个字符且具有两个连续数字的字符串才能匹配上。 
     //也可以用/(?=\w{6})(?=\w*\d{2})/ 达到同样效果。
let result = pwRegex.test(sampleWord);
console.log(result); //false    

小括号' ( ) '可以检查多组字符。比如,要检查Penguin或Pumpkin是否在字符串中,就可以用 /P(engu|umpk)in/g 作为正则表达式,然后用正则表达式的test方法检查所需的字符组是否在字符串中。看例子:

//例一:
let testStr = "Pumpkin";
let testRegex = /P(engu|umpk)in/;
testRegex.test(testStr); //true

//例二:
let myString = "Eleanor Roosevelt";
let myRegex = /(Franklin|Eleanor)\D* Roosevelt/; //区分大小写、检查是否包含Franklin Roosevelt 或 Eleanor Roosevelt、对中间名不做要求。
let result =myRegex.test(myString) ; 
console.log(result); //true

若不知道具体哪个词重复了,捕获组(Capture groups)可用于查找重复的子字符串。捕获组是通过将要捕获的正则表达式模式括在括号中来构造的。例如,要捕获由字母数字字符组成的单词,\w+将被括在括号内:/(\w+)/形成捕获组,捕获组匹配的子字符串保存到一个临时“变量”中,可以使用反斜杠和捕获组的编号(如\1)在同一正则表达式中访问该变量。捕获组会根据其左括号的位置(从左到右)自动编号,从1开始。例如,第一个左括号编号是1,第二个左括号编号就是2了。看例子:

let repeatNum = "42 42 42";
let reRegex = /^(\d+)(\s)\1\2\1$/; // 用/^(\d+) \1 \1$/也可以,或用/^(\d+)\s\1\s\1$/也可以,空格以\s代替。^搜索字符串开头文字,\s搜索空格,\1表示捕获组中对应变量的位置,$搜索字符串结尾文字。
let result = reRegex.test(repeatNum);
let result1 =repeatNum.match(reRegex);
console.log(result); //true
console.log(result1); //['42 42 42','42']

当我们在字符串上使用.replace()方法搜索和替换字符串中的文本时,捕获组可以作为字符串.replace()方法的参数,起到搜索和替换的作用。

先看replace方法,它有两个参数,第一个参数是要搜索的正则表达式,第二个参数是用于替换匹配项的字符串或用于执行某些操作的函数。

看例子:

let wrongText = "The sky is silver.";
let silverRegex = /silver/;
wrongText.replace(silverRegex, "blue"); //返回字符串“The sky is blue.”

使用美元符号($)可以访问第一个参数中的捕获组:

"Code Camp".replace(/(\w+)\s(\w+)/, '$2 $1'); //返回字符串“Camp Code”

再看两个例子:

一、要求将字符串"one two three"倒置为"three two one":

let str = "one two three";
let fixRegex = /(\w+)\s(\w+)\s(\w+)/; //一定要加上加号+,表示至少匹配1个字符,否则不会占据位置。
let replaceText = "$3 $2 $1"; //使用捕获组的默认编号,让顺序倒置。
let result = str.replace(fixRegex, replaceText);//replace会返回替代后的字符串。
console.log(result); //three two one

二、要求将字符串的开头和结尾的空格去掉:

let hello = "   Hello, World!  ";
let wsRegex = /^\s+ | \s+$/g; //\s表示空格,^符号取开头的\s,$符号取结尾的\s,g表示对整个字符串进行搜索。
let result = hello.replace(wsRegex,''); //以空字符""代替空格。
console.log(result); //Hello, World!

。。。

posted @ 2022-09-16 21:27  枭二熊  阅读(416)  评论(0)    收藏  举报