JavaScript使用正则表达式

注:纯手打,如有错误欢迎评论区交流!
转载请注明出处:https://blog.csdn.net/testleaf/article/details/144537779
编写此文是为了更好地学习前端知识,如果损害了有关人的利益,请联系删除!
本文章将不定时更新,敬请期待!!!
欢迎点赞、收藏、转发、关注,多谢!!!

一、正则表达式的定义

正则表达式(Regular Expression,在代码中常简写为regexregexpRE)使用单个字符串来描述、匹配一系列符合某个句法规则的字符串搜索模式。搜索模式可用于文本搜索和文本替换。

二、正则表达式的创建

在JavaScript中,可以通过两种方式创建正则表达式:

1、字面量创建

使用斜杠/之间的模式,例如:

const re = /ab+c/;

这种方式在脚本加载时提供正则表达式的编译,如果正则表达式保持不变,使用这种方式可以提高性能。

2、构造函数创建

RegExp构造函数可以接受不同形式的参数来创建正则表达式对象:
2.1、接受一个字符串参数(表示正则表达式模式)

const re1 = new RegExp("abc");
// 等同于直接使用字面量形式 /abc/

这里创建的re1正则表达式对象用于匹配字符串中连续出现的"abc"字符序列。
2.2、接受两个参数,第一个是字符串形式的正则表达式模式,第二个是字符串形式的标志(flags)
常见的标志有:
g(global):全局匹配,即匹配所有出现的符合模式的子串,而不只是第一个。
i(ignoreCase):忽略大小写进行匹配。
m(multiline):多行匹配,会改变^$的行为,使其能匹配每行的开头和结尾(在多行文本中)。
示例:

const re2 = new RegExp("abc", "gi");
// 等同于 /abc/gi ,会忽略大小写地全局匹配"abc"

re2正则表达式对象在匹配时,无论字符串中的"abc"是大写还是小写形式,都会进行匹配,并且会匹配所有出现的情况。
2.3、根据变量动态创建正则表达式
正如前面提到的,构造函数的优势在于可以基于动态的内容来创建正则表达式,比如根据用户输入:

const userInput = "hello";
const re3 = new RegExp(userInput + ".*");
// 假设从用户那里获取到"hello",创建一个匹配以"hello"开头的任意字符序列的正则表达式
const str = "hello world, hello JavaScript";
const result = str.match(re3);
console.log(result);

在上述代码中:
首先获取了用户输入的字符串userInput(这里假设为"hello")。
然后使用RegExp构造函数创建了一个正则表达式re3,它的模式是userInput + ".*",也就是根据用户输入的内容拼接上".*".*表示匹配任意字符零次或多次,可以理解为匹配以用户输入内容开头的任意后续内容)。
接着用str.match(re3)在给定的字符串str中查找匹配re3正则表达式的内容,并将结果打印出来,这里会返回匹配到的以"hello"开头的部分字符串。
2.4、注意事项
转义问题:当通过构造函数传入的字符串模式中包含正则表达式的特殊字符(如\^$*等)时,需要特别注意转义。例如,如果要匹配字符串中实际的\字符,在构造函数的字符串参数里需要写成"\\\\"(因为在字符串里\本身也是转义字符):

const re4 = new RegExp("\\\\");
// 用于匹配字符串中的反斜杠字符
const strWithBackslash = "This is a \\ character";
const matchResult = strWithBackslash.match(re4);
console.log(matchResult);

与字面量形式对比:虽然构造函数提供了动态创建的灵活性,但对于一些固定的、已知的正则表达式模式,使用字面量形式(如/abc/)往往更加直观、简洁,并且在代码可读性方面可能更好。

总之,RegExp构造函数在需要根据运行时情况灵活构建正则表达式时是非常有用的工具,合理运用它能更好地处理各种文本匹配相关的任务。

三、正则表达式的修饰符

修饰符是可选的,用于指定正则表达式的匹配模式。常见的修饰符包括:

  • i:ignoreCase,匹配忽视大小写。
  • m:multiline,多行匹配。
  • g:global,全局匹配。

1、i 修饰符(忽略大小写)

使用 i 修饰符,正则表达式在匹配时会忽略大小写差异。

const str = "Hello World!";
const regex = /hello/i; // 忽略大小写匹配 "hello"
const match = str.match(regex);
console.log(match); // 输出: [ 'Hello', index: 0, input: 'Hello World!', groups: undefined ]

在这个例子中,即使字符串中的 “Hello” 是大写的,由于使用了 i 修饰符,正则表达式仍然能够匹配到它。

2、m 修饰符(多行匹配)

使用 m 修饰符,正则表达式中的 ^$ 符号将能够匹配字符串中每一行的开始和结束,而不仅仅是整个字符串的开始和结束。

const str = `Hello
World
!`;
const regex = /^hello$/gm; // 多行匹配,全局匹配,但这里其实不需要全局匹配也能看出效果
const matches = str.match(regex);
// 注意:在没有 `m` 修饰符的情况下,这个正则表达式不会匹配到任何内容,
// 因为 `^` 和 `$` 默认匹配整个字符串的开始和结束。
// 但由于我们的字符串有多行,并且我们使用了 `m` 修饰符,
// 所以这个正则表达式会尝试匹配每一行的开始和结束。
// 然而,这个例子中的正则表达式实际上是不会匹配的,
// 因为我们要求整行必须是 "hello"(忽略大小写也不会匹配),
// 而实际行内容是 "Hello"、"World" 和 "!"。
// 这里只是为了说明 `m` 修饰符的用法,正确的测试应该使用会匹配多行的模式,
// 比如查找每行是否以字母开头。

// 一个更好的例子:
const str2 = `hello
Hello
WORLD
!hello`;
const regex2 = /^hello/img; // 多行匹配,忽略大小写,查找每行是否以 "hello" 开头
const matches2 = str2.match(regex2);
console.log(matches2); // 输出: ["hello", "Hello"],注意 "WORLD" 不匹配因为不是以小写 h 开头

在这个修正后的例子中,我们使用了 ^hello 并加上了 im 修饰符来匹配每一行是否以 “hello”(忽略大小写)开头。

3、g 修饰符(全局匹配)

使用 g 修饰符,正则表达式将匹配字符串中所有可能的子串,而不是在找到第一个匹配后就停止。

const str = "The quick brown fox jumps over the lazy dog. The dog is quick.";
const regex = /quick/g; // 全局匹配 "quick"
const matches = str.match(regex);
console.log(matches); // 输出: ["quick", "quick"]

在这个例子中,由于使用了 g 修饰符,正则表达式匹配到了字符串中的所有 “quick” 子串。

4、综合例子

const str = `Hello
WORLD
hello world
HeLLo WoRLd`;
const regex = /hello/gim; // 全局匹配,多行匹配,忽略大小写
const matches = str.match(regex);
console.log(matches); // 输出: ["Hello", "hello", "HeLLo"]

在这个综合例子中,正则表达式匹配到了所有忽略大小写、出现在任意一行的 “hello”(或其变体)子串。

四、正则表达式的元字符

正则表达式包含一些具有特殊含义的字符,这些字符被称为元字符。常见的元字符包括:

.:匹配除换行符以外的任意字符。
\.:匹配.字符。
\b:代表单词边界(word boundary),它是一个零宽断言(zero-width assertion),意味着它本身不匹配任何字符,而是用来界定一个单词的开始和结束位置。单词边界通常是由空格、标点符号、字符串的开始或结束位置,或者是换行符等分隔符来定义的,这些分隔符都不是单词字符(通常指字母、数字或下划线)。
\B\B\b 的反义,它表示非单词边界。换句话说,\B 匹配的位置是单词字符之间或单词字符与非单词字符之间的位置。
\w:匹配一个单词字符(字母、数字或下划线),等价于[A-Za-z0-9_]
\W:匹配非数字、字母、下划线。
\d:匹配一个数字字符,等价于[0-9]
\D:匹配非数字。
\s:匹配一个空白字符(包括空格、制表符、换页符等)。
\S:匹配非空白字符。
\n:匹配换行符。
\/:匹配正斜杠/
\\:匹配反斜杠\
[]:用于查找某个范围内的字符。例如,[a-z]查找小写字母,[A-Z]查找大写字母,[0-9]查找数字,[abc]匹配 abc 之中的任意一个字符。
^:匹配输入字符串的开始位置。如果出现在字符类[]的开头,则表示取反。例如,[^abc]匹配 abc 以外的任意一个字符。
$:匹配输入字符串的结束位置。
^abc$:完全匹配字符串 abc
{}:用于指定前面的字符或子表达式出现的次数。例如,{3}表示恰好出现3次,{2,}表示至少出现2次,{2,5}表示出现2到5次。
*:匹配前面的子表达式零次或多次。
+:匹配前面的子表达式一次或多次。
?:匹配前面的子表达式零次或一次。
|:表示选择。例如,a|b表示匹配a或b。
():用于分组。分组后,可以引用该组匹配的子字符串。例如,(abc)表示匹配abc,并可以引用该匹配的子字符串。

注意事项:
1、[]里面的字符需要转义吗?

  • 一般情况:
    • 在字符类[]内部,许多在正则表达式其他位置需要转义的特殊字符通常不需要转义。例如,在[]内,*+?^(在开头以外的位置)、$(在结尾以外的位置)等字符会被当作普通字符处理。
    • 比如,[*+?]这个字符类会匹配字符*+或者?,它们在字符类内部不需要转义,因为字符类主要是定义一个字符的集合范围。
  • 特殊情况:连字符-和右方括号]
    • 连字符-
      • 当连字符在字符类的开头或者结尾时,它被当作普通字符。例如,[-abc]会匹配字符-ab或者c[abc-]会匹配abc或者-
      • 但如果连字符在字符类的中间,它用于定义一个范围,比如[a-z]用于匹配从az的所有小写字母。如果要匹配连字符本身作为一个普通字符,而不是用于定义范围,就需要把它放在字符类的开头或者结尾,或者对它进行转义(如[\-])。
    • 右方括号]
      • 如果要在字符类中匹配右方括号本身,需要将其放在字符类的开头(如[]abc])或者对它进行转义(如[\]]),否则它会被当作字符类的结束标记。

对于正斜杠/和反斜杠\,在字符类内部它们通常也当作普通字符处理(/几乎不需要在字符类内特殊考虑,\如果不是和其他转义序列有关,也当作普通字符),但要根据具体的匹配需求来判断。例如,[\-/\\]这个字符类可以匹配连字符、正斜杠或者反斜杠。

五、正则表达式的使用

在JavaScript中,正则表达式通常与RegExp对象的方法和字符串的方法一起使用。常见的方法包括:

1、exec()

exec() 方法在一个指定字符串中执行一个搜索匹配。它返回一个数组(如果没有匹配到结果,则返回 null)。

const regex = /(\d{4})-(\d{2})-(\d{2})/;
const str = "Today's date is 2023-10-05.";
const result = regex.exec(str);

if (result !== null) {
  console.log(`Full match: ${result[0]}`);    // Full match: 2023-10-05
  console.log(`Year: ${result[1]}`);          // Year: 2023
  console.log(`Month: ${result[2]}`);         // Month: 10
  console.log(`Day: ${result[3]}`);           // Day: 05
} else {
  console.log("No match found.");
}

2、test()

test() 方法用于测试一个字符串是否匹配某个模式,如果字符串中含有匹配的文本,则返回 true,否则返回 false

const regex = /hello/;
const str = "hello world";
const result = regex.test(str);

console.log(result); // 输出: true

3、match()

match() 方法检索返回一个字符串匹配正则表达式的结果。它返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回 null

const str = "The quick brown fox jumps over the lazy dog.";
const regex = /\b\w{4}\b/g; // 匹配四个字母的单词,\b 确保匹配的四个字母序列(\w{4})前后都不是单词字符的一部分。
const matches = str.match(regex);

console.log(matches); // 输出: [ 'over', 'lazy' ]
// 正则表达式 \b\w{4}\b 会匹配字符串中所有恰好由四个单词字符组成且前后都是单词边界的序列

4、matchAll()

matchAll() 方法返回一个包含所有匹配正则表达式及分组捕获结果的迭代器。
注意:matchAll() 是一个较新的方法,可能在一些旧版本的JavaScript中不受支持。

const str = "The quick brown fox jumps over the lazy dog. The dog barks.";
const regex = /(\b\w+\b) (barks|jumps)/g;
const matches = str.matchAll(regex);

for (const match of matches) {
  console.log(`Full match: ${match[0]}, Word: ${match[1]}, Action: ${match[2]}`);
}
// 输出:
// Full match: fox jumps, Word: fox, Action: jumps
// Full match: dog barks, Word: dog, Action: barks

5、search()

search() 方法执行正则表达式和字符串之间的搜索匹配。如果搜索到匹配项,则返回匹配项在字符串中的位置(索引,从0开始),否则返回 -1

const str = "For more information, visit Mozilla Developer Network.";
const regex = /mozilla/i; // 不区分大小写的匹配
const index = str.search(regex);

console.log(index); // 输出: 28

6、replace()

replace() 方法用于在字符串中查找匹配正则表达式的子字符串,并替换它们。

const str = "Visit Microsoft!";
const regex = /microsoft/i; // 不区分大小写的匹配
const newStr = str.replace(regex, "Mozilla");

console.log(newStr); // 输出: Visit Mozilla!

7、replaceAll()

replaceAll() 方法用于在字符串中查找所有匹配正则表达式的子字符串,并替换它们。与 replace() 方法不同,replaceAll() 会替换所有匹配的子字符串,而不仅仅是第一个。
注意:replaceAll() 是一个较新的方法,可能在一些旧版本的JavaScript中不受支持。

const str = "The quick brown fox jumps over the lazy dog. The fox is quick.";
const regex = /fox/gi; // 全局且不区分大小写的匹配
const newStr = str.replaceAll(regex, "cat");

console.log(newStr); // 输出: The quick brown cat jumps over the lazy dog. The cat is quick.

8、split()

split() 方法根据匹配正则表达式的子字符串来分割字符串,并返回一个数组。如果找不到匹配项,则返回包含整个字符串的单一元素数组。
语法:

string.split(separator, limit)
  • separator:一个正则表达式或字符串,表示要用来分割字符串的分隔符。
  • limit:(可选)一个整数,表示返回数组的最大长度。如果设置了该参数,返回的子字符串不会多于这个参数指定的数组长度。余下的字符串将会放在数组的最后一个元素中。
    示例:

使用字符串作为分隔符:

const str = "apple,banana,orange";
const fruits = str.split(",");

console.log(fruits); // 输出: [ 'apple', 'banana', 'orange' ]

使用正则表达式作为分隔符:

const str = "apple1banana2orange3";
const fruitsWithNumbers = str.split(/\d/);

console.log(fruitsWithNumbers); // 输出: [ 'apple', 'banana', 'orange', '' ]
// 注意:最后一个元素是一个空字符串,因为字符串末尾有一个数字3,它后面没有字符了。

使用正则表达式并设置全局标志:

const str = "apple1banana2orange3";
const fruits = str.split(/\d+/g); // 使用全局标志来匹配所有数字

console.log(fruits); // 输出: [ 'apple', 'banana', 'orange', '' ]
// 全局标志匹配了所有连续的数字,并将它们作为分隔符。

使用 limit 参数:

const str = "apple,banana,orange,grape";
const limitedFruits = str.split(",", 2);

console.log(limitedFruits); // 输出: [ 'apple', 'banana' ]
// 只返回了两个元素,因为设置了 limit 参数为 2。

六、经典示例

在JavaScript中,正则表达式(RegExp)被广泛应用于字符串处理。以下是一些经典的正则表达式案例,这些案例展示了正则表达式在不同场景下的强大功能。

1、验证电子邮件地址

const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
const email = "example@example.com";
console.log(emailRegex.test(email)); // 输出: true

2、验证URL

const urlRegex = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w\.-]*)*\/?$/;
const url = "https://www.example.com/path/to/resource";
console.log(urlRegex.test(url)); // 输出: true

3、验证手机号码(以中国手机号码为例)

const phoneRegex = /^1[3-9]\d{9}$/;
const phoneNumber = "13800138000";
console.log(phoneRegex.test(phoneNumber)); // 输出: true
const invalidPhoneNumber = "12345678901"; // 假设这不是一个有效的手机号码,因为第二位不是3-9
console.log(phoneRegex.test(invalidPhoneNumber)); // 输出: false

4、验证IP地址(IPv4)

const ipRegex = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
const ip = "192.168.1.1";
console.log(ipRegex.test(ip)); // 输出: true

5、验证邮政编码(以中国邮政编码为例)

const zipRegex = /^\d{6}$/;
const zipCode = "100000"; // 这是一个有效的北京邮政编码
console.log(zipRegex.test(zipCode)); // 输出: true
const invalidZipCode = "12345"; // 这是一个无效的邮政编码,因为它只有5位数字
console.log(zipRegex.test(invalidZipCode)); // 输出: false
const invalidZipCodeWithLetters = "12345A"; // 这也是一个无效的邮政编码,因为它包含字母
console.log(zipRegex.test(invalidZipCodeWithLetters)); // 输出: false

6、提取字符串中的数字

const str = "There are 123 apples and 456 oranges.";
const numberRegex = /\d+/g;
const numbers = str.match(numberRegex);
console.log(numbers); // 输出: [ '123', '456' ]

7、提取字符串中的URL

const str = "Visit us at https://www.example.com or http://blog.example.com/post/123.";
const urlRegex = /(https?:\/\/[^\s]+)/g;
const urls = str.match(urlRegex);
console.log(urls); // 输出: [ 'https://www.example.com', 'http://blog.example.com/post/123.' ]

8、替换字符串中的特定模式

const str = "Hello World! Welcome to the world of JavaScript.";
const worldRegex = /world/gi; // 全局且不区分大小写的匹配
const newStr = str.replace(worldRegex, "universe");
console.log(newStr); // 输出: Hello universe! Welcome to the universe of JavaScript.

9、匹配和提取日期(例如:YYYY-MM-DD)

const str = "Today's date is 2023-10-05.";
const dateRegex = /(\d{4})-(\d{2})-(\d{2})/;
const match = str.match(dateRegex);
if (match) {
  console.log(`Year: ${match[1]}, Month: ${match[2]}, Day: ${match[3]}`);
  // 输出: Year: 2023, Month: 10, Day: 05
}

10、 匹配和提取时间(例如:HH:MM:SS)

const str = "The meeting starts at 14:30:00.";
const timeRegex = /(\d{2}):(\d{2}):(\d{2})/;
const match = str.match(timeRegex);
if (match) {
  console.log(`Hour: ${match[1]}, Minute: ${match[2]}, Second: ${match[3]}`);
  // 输出: Hour: 14, Minute: 30, Second: 00
}

七、其他

1、正则表达式练习网站

https://imageslr.github.io/regexone-cn

2、正则表达式图解网站

https://jex.im/regulex

3、match和exec的区别

  • match 方法
    • 所属对象: String
    • 用法: string.match(regexp)
    • 返回值:
      • 如果 regexp 没有全局标志 g,返回一个数组,其中第一个元素是匹配的字符串,后面的元素是捕获组的匹配结果(如果有的话),以及额外的索引和输入字符串属性。
      • 如果 regexp 有全局标志 g,返回一个包含所有匹配结果的数组,每个元素都是一个匹配的字符串,没有捕获组的匹配结果和索引信息。
      • 如果没有匹配结果,返回 null
let str = "The quick brown fox jumps over the lazy dog.";
let regex = /the/i;
let result = str.match(regex);
console.log(result); 
// [
//   'The',
//   index: 0,
//   input: 'The quick brown fox jumps over the lazy dog.',
//   groups: undefined
// ]

regex = /the/gi;
result = str.match(regex);
console.log(result); // [ 'The', 'the' ]
  • exec 方法
    • 所属对象: RegExp
    • 用法: regexp.exec(string)
    • 返回值:
      • 返回一个数组,其中第一个元素是匹配的字符串,后面的元素是捕获组的匹配结果(如果有的话)。
      • 数组还包含额外的属性:index(匹配结果的起始位置)和 input(输入字符串)。
      • 如果没有匹配结果,返回 null
      • 每次调用 exec 方法时,它都会更新正则表达式的 lastIndex 属性,从而允许进行连续匹配(这在全局搜索时特别有用)。
let str = "The quick brown fox jumps over the lazy dog.";
let regex = /the/i;
let result = regex.exec(str);
console.log(result);
// [
//   'The',
//   index: 0,
//   input: 'The quick brown fox jumps over the lazy dog.',
//   groups: undefined
// ]

regex.lastIndex = 0; // 重置 lastIndex
result = regex.exec(str); // 再次调用会返回相同的匹配结果 ["The", ...]
regex = /the/gi;
while ((result = regex.exec(str)) !== null) {
  console.log(`Found ${result[0]} at index ${result.index}`);
}
// 输出:
// Found The at index 0
// Found the at index 31

总结:

  • matchString 对象的方法,适合一次性获取所有匹配结果(特别是使用全局标志时)。
  • execRegExp 对象的方法,适合逐个匹配并获取每次匹配的详细信息,同时可以利用 lastIndex 属性进行连续匹配。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

testleaf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值