我将为您完善正则表达式方法的文档,详细介绍JavaScript中的正则表达式相关方法。
---
title: 正则表达式方法
icon: javascript
order: 2
---
# 正则表达式方法
JavaScript提供了多种使用正则表达式的方法,包括RegExp对象的方法和String对象的正则相关方法。本文将详细介绍这些方法的用法和特点。
## RegExp对象方法
RegExp对象提供了两个主要方法用于执行正则表达式匹配。
### test()
`test()`方法用于测试字符串是否匹配正则表达式模式,返回布尔值。
**语法:**
```javascript
regexObj.test(str)
参数:
str
:要测试的字符串。
返回值:
- 如果字符串中存在匹配,则返回
true
;否则返回false
。
示例:
// 检查字符串是否包含数字
const hasNumber = /\d/.test('abc123');
console.log(hasNumber); // true
// 检查是否是有效的电子邮件格式
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
console.log(emailRegex.test('user@example.com')); // true
console.log(emailRegex.test('invalid-email')); // false
// 使用全局标志时的注意事项
const regex = /a/g;
console.log(regex.test('abc')); // true
console.log(regex.test('abc')); // false - 第二次调用从上次匹配位置继续
console.log(regex.test('abc')); // true - 第三次调用从头开始
注意
当使用带有全局标志(g
)的正则表达式时,test()
方法会更新正则表达式对象的lastIndex
属性。连续调用test()
会从上次匹配结束的位置继续搜索,这可能导致意外结果。
exec()
exec()
方法用于在字符串中执行匹配搜索,返回一个结果数组或null
。
语法:
regexObj.exec(str)
参数:
str
:要搜索的字符串。
返回值:
- 如果匹配成功,返回一个数组,其中包含匹配的文本、捕获组和其他属性。
- 如果匹配失败,返回
null
。
返回数组的属性:
[0]
:匹配的完整文本。[1], [2], ...
:捕获组匹配的文本(如果有)。index
:匹配在原字符串中的起始位置。input
:原始字符串。groups
:命名捕获组对象(ES2018+)。
示例:
// 基本使用
const regex = /a(b)c/;
const result = regex.exec('abc');
console.log(result);
// 输出: ["abc", "b", index: 0, input: "abc", groups: undefined]
// 使用命名捕获组
const dateRegex = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const dateResult = dateRegex.exec('2023-05-15');
console.log(dateResult.groups);
// 输出: {year: "2023", month: "05", day: "15"}
// 使用全局标志进行多次匹配
const globalRegex = /a/g;
let match;
while ((match = globalRegex.exec('ababa')) !== null) {
console.log(`找到 ${match[0]} 在位置 ${match.index}`);
}
// 输出:
// 找到 a 在位置 0
// 找到 a 在位置 2
// 找到 a 在位置 4
提示
使用带有全局标志(g
)的正则表达式时,可以通过循环调用exec()
方法来查找所有匹配项。每次调用都会更新lastIndex
属性,直到找不到更多匹配项。
String对象的正则方法
String对象提供了多个使用正则表达式的方法,用于搜索、替换和分割字符串。
match()
match()
方法用于检索字符串与正则表达式匹配的结果。
语法:
str.match(regexp)
参数:
regexp
:正则表达式对象或字面量。如果传入的不是正则表达式,会隐式地使用new RegExp(regexp)
转换。
返回值:
- 如果使用全局标志(
g
),返回所有匹配项的数组。 - 如果不使用全局标志,返回与
RegExp.exec()
相同的结果(第一个匹配及其捕获组)。 - 如果没有匹配,返回
null
。
示例:
// 不使用全局标志
const str = 'JavaScript is awesome';
const result = str.match(/a(w)e/);
console.log(result);
// 输出: ["awe", "w", index: 13, input: "JavaScript is awesome", groups: undefined]
// 使用全局标志
const globalResult = str.match(/a/g);
console.log(globalResult);
// 输出: ["a", "a", "a"]
// 没有匹配
const noMatch = str.match(/xyz/);
console.log(noMatch); // null
matchAll() (ES2020+)
matchAll()
方法返回一个包含所有匹配结果的迭代器,每个结果类似于RegExp.exec()
的返回值。
语法:
str.matchAll(regexp)
参数:
regexp
:必须是带有全局标志(g
)的正则表达式,否则会抛出TypeError。
返回值:
- 返回一个迭代器,可以通过
for...of
循环或转换为数组来访问所有匹配结果。
示例:
const str = 'test1test2test3';
const regex = /test(\d)/g;
const matches = [...str.matchAll(regex)];
console.log(matches.length); // 3
for (const match of matches) {
console.log(`完整匹配: ${match[0]}`);
console.log(`捕获组: ${match[1]}`);
console.log(`位置: ${match.index}`);
}
// 输出:
// 完整匹配: test1
// 捕获组: 1
// 位置: 0
// 完整匹配: test2
// 捕获组: 2
// 位置: 5
// 完整匹配: test3
// 捕获组: 3
// 位置: 10
search()
search()
方法用于查找字符串中与正则表达式匹配的第一个子字符串的位置。
语法:
str.search(regexp)
参数:
regexp
:正则表达式对象或字面量。
返回值:
- 返回第一个匹配项的索引;如果没有匹配,则返回-1。
示例:
const str = 'JavaScript is awesome';
// 查找第一个数字的位置
console.log(str.search(/\d/)); // -1 (没有数字)
// 查找第一个空格的位置
console.log(str.search(/\s/)); // 10
// 查找特定单词的位置
console.log(str.search(/awesome/)); // 13
注意
search()
方法总是从字符串的开头开始搜索,即使正则表达式带有全局标志(g
)也是如此。
replace()
replace()
方法用于替换字符串中与正则表达式匹配的子字符串。
语法:
str.replace(regexp|substr, newSubstr|function)
参数:
regexp
:正则表达式对象或字面量。substr
:要被替换的字符串(不使用正则表达式时)。newSubstr
:用于替换的字符串。function
:用于生成替换字符串的函数。
返回值:
- 返回替换后的新字符串。原字符串不会被修改。
示例:
// 基本替换
const str = 'Hello, world!';
console.log(str.replace(/world/, 'JavaScript'));
// 输出: "Hello, JavaScript!"
// 使用全局标志替换所有匹配项
const str2 = 'apple, banana, apple';
console.log(str2.replace(/apple/g, 'orange'));
// 输出: "orange, banana, orange"
// 使用捕获组引用
const name = 'Smith, John';
console.log(name.replace(/(\w+), (\w+)/, '$2 $1'));
// 输出: "John Smith"
// 使用命名捕获组
const date = '2023-05-15';
console.log(date.replace(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/, '$<month>/$<day>/$<year>'));
// 输出: "05/15/2023"
// 使用函数生成替换文本
const prices = 'Price: $10, $20, $30';
console.log(prices.replace(/\$(\d+)/g, (match, price) => {
return `$${parseInt(price) * 2}`;
}));
// 输出: "Price: $20, $40, $60"
替换函数参数:
当使用函数作为replace()
的第二个参数时,该函数会接收以下参数:
match
:匹配的子字符串。p1, p2, ...
:捕获组匹配的字符串(如果有)。offset
:匹配在原字符串中的位置。string
:原始字符串。groups
:命名捕获组对象(ES2018+)。
const text = 'John: 30, Jane: 25';
const result = text.replace(/(\w+): (\d+)/g, function(match, name, age, offset, string) {
return `${name} is ${age} years old`;
});
console.log(result);
// 输出: "John is 30 years old, Jane is 25 years old"
replaceAll() (ES2021+)
replaceAll()
方法类似于replace()
,但会替换所有匹配项,即使正则表达式没有全局标志。
语法:
str.replaceAll(regexp|substr, newSubstr|function)
参数:
- 与
replace()
方法相同。 - 如果第一个参数是正则表达式,必须带有全局标志(
g
),否则会抛出TypeError。
示例:
const str = 'apple, banana, apple';
// 使用字符串替换所有匹配项
console.log(str.replaceAll('apple', 'orange'));
// 输出: "orange, banana, orange"
// 使用正则表达式替换所有匹配项
console.log(str.replaceAll(/a/g, 'o'));
// 输出: "opple, bonono, opple"
split()
split()
方法使用正则表达式或固定字符串将字符串分割成数组。
语法:
str.split([separator[, limit]])
参数:
separator
:正则表达式或字符串,用于确定分割点。limit
:可选,指定返回数组的最大长度。
返回值:
- 返回分割后的字符串数组。
示例:
// 使用字符串分割
const str = 'apple,banana,orange';
console.log(str.split(','));
// 输出: ["apple", "banana", "orange"]
// 使用正则表达式分割
const str2 = 'apple,banana;orange|grape';
console.log(str2.split(/[,;|]/));
// 输出: ["apple", "banana", "orange", "grape"]
// 使用捕获组保留分隔符
const str3 = 'apple,banana;orange';
console.log(str3.split(/([,;])/));
// 输出: ["apple", ",", "banana", ";", "orange"]
// 限制返回数组长度
const str4 = 'one,two,three,four';
console.log(str4.split(',', 2));
// 输出: ["one", "two"]
正则表达式属性
除了方法外,正则表达式对象还有一些重要的属性,可以提供有关匹配状态的信息。
lastIndex
lastIndex
属性指定下一次匹配开始的位置,仅对设置了全局(g
)或粘性(y
)标志的正则表达式有效。
const regex = /a/g;
const str = 'banana';
console.log(regex.lastIndex); // 0 (初始值)
regex.test(str); // true
console.log(regex.lastIndex); // 2 (第一个'a'之后的位置)
regex.test(str); // true
console.log(regex.lastIndex); // 4 (第二个'a'之后的位置)
regex.test(str); // true
console.log(regex.lastIndex); // 6 (第三个'a'之后的位置)
regex.test(str); // false (没有更多匹配)
console.log(regex.lastIndex); // 0 (重置为初始值)
注意
手动修改lastIndex
属性可能导致意外的匹配行为。如果需要重置搜索位置,可以将lastIndex
设置为0:
const regex = /a/g;
regex.lastIndex = 0; // 重置搜索位置到字符串开头
source, flags, global, ignoreCase, multiline, sticky, unicode
正则表达式对象还有一些只读属性,提供有关正则表达式模式和标志的信息:
const regex = /hello/gimsy;
console.log(regex.source); // "hello" (正则表达式的模式文本)
console.log(regex.flags); // "gimsy" (所有标志的字符串)
console.log(regex.global); // true (是否设置了g标志)
console.log(regex.ignoreCase); // true (是否设置了i标志)
console.log(regex.multiline); // true (是否设置了m标志)
console.log(regex.sticky); // true (是否设置了y标志)
console.log(regex.unicode); // true (是否设置了u标志)
正则表达式方法的实际应用
表单验证
function validateForm(form) {
// 电子邮件验证
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!emailRegex.test(form.email)) {
return { valid: false, error: '请输入有效的电子邮件地址' };
}
// 密码强度验证(至少8位,包含大小写字母和数字)
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/;
if (!passwordRegex.test(form.password)) {
return { valid: false, error: '密码必须至少8位,包含大小写字母和数字' };
}
// 手机号码验证(中国)
const phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(form.phone)) {
return { valid: false, error: '请输入有效的手机号码' };
}
return { valid: true };
}
// 使用示例
const formData = {
email: 'user@example.com',
password: 'Password123',
phone: '13812345678'
};
console.log(validateForm(formData));
提取URL参数
function getUrlParams(url) {
const params = {};
const queryString = url.split('?')[1];
if (!queryString) return params;
// 使用正则表达式匹配参数
const paramRegex = /([^&=]+)=([^&]*)/g;
let match;
while ((match = paramRegex.exec(queryString)) !== null) {
const key = decodeURIComponent(match[1]);
const value = decodeURIComponent(match[2]);
params[key] = value;
}
return params;
}
// 使用示例
const url = 'https://example.com/search?query=javascript&page=2&sort=desc';
console.log(getUrlParams(url));
// 输出: { query: "javascript", page: "2", sort: "desc" }
格式化文本
// 将驼峰命名转换为短横线命名
function camelToKebab(str) {
return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
console.log(camelToKebab('backgroundColor')); // "background-color"
console.log(camelToKebab('fontSize')); // "font-size"
// 格式化电话号码
function formatPhoneNumber(phone) {
return phone.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3');
}
console.log(formatPhoneNumber('13812345678')); // "138-1234-5678"
// 高亮文本中的关键词
function highlightKeywords(text, keywords) {
const regex = new RegExp(`(${keywords.join('|')})`, 'gi');
return text.replace(regex, '<span class="highlight">$1</span>');
}
const text = 'JavaScript是一种流行的编程语言,常用于Web开发。';
console.log(highlightKeywords(text, ['JavaScript', '编程', 'Web']));
// 输出: "<span class="highlight">JavaScript</span>是一种流行的<span class="highlight">编程</span>语言,常用于<span class="highlight">Web</span>开发。"
解析CSV数据
function parseCSV(csvText) {
const lines = csvText.split('\n');
const result = [];
// 使用正则表达式匹配CSV字段(考虑引号内的逗号)
const fieldRegex = /(?:^|,)(?:"([^"]*(?:""[^"]*)*)"|([^,]*))/g;
for (const line of lines) {
if (!line.trim()) continue;
const fields = [];
let match;
while ((match = fieldRegex.exec(line)) !== null) {
// 移除开头的逗号(如果有)
let field = match[0].startsWith(',') ? match[0].substring(1) : match[0];
// 处理引号字段
if (field.startsWith('"') && field.endsWith('"')) {
field = field.substring(1, field.length - 1).replace(/""/g, '"');
}
fields.push(field);
}
result.push(fields);
}
return result;
}
// 使用示例
const csvData = 'Name,Age,City\n"Smith, John",30,New York\n"Doe, Jane",25,"San Francisco"';
console.log(parseCSV(csvData));
验证和清理用户输入
// 清理HTML标签
function stripHtml(html) {
return html.replace(/<[^>]*>/g, '');
}
console.log(stripHtml('<p>这是<strong>一些</strong>文本</p>'));
// 输出: "这是一些文本"
// 验证并清理用户名(只允许字母、数字和下划线)
function sanitizeUsername(username) {
// 移除非法字符
const sanitized = username.replace(/[^a-zA-Z0-9_]/g, '');
// 验证是否符合要求
const isValid = /^[a-zA-Z][a-zA-Z0-9_]{2,19}$/.test(sanitized);
return {
sanitized,
isValid,
message: isValid ? '用户名有效' : '用户名必须以字母开头,只包含字母、数字和下划线,长度3-20'
};
}
console.log(sanitizeUsername('user123')); // 有效
console.log(sanitizeUsername('123user')); // 无效(不以字母开头)
console.log(sanitizeUsername('user@123')); // 清理后为"user123",有效
正则表达式方法的性能考虑
1. 避免重复创建正则表达式
// 低效:在循环中重复创建正则表达式
function inefficientSearch(texts, pattern) {
const results = [];
for (const text of texts) {
if (new RegExp(pattern).test(text)) {
results.push(text);
}
}
return results;
}
// 高效:在循环外创建正则表达式
function efficientSearch(texts, pattern) {
const regex = new RegExp(pattern);
return texts.filter(text => regex.test(text));
}
2. 使用适当的方法
// 低效:使用match而非test进行简单检查
function inefficientCheck(text) {
return text.match(/pattern/) !== null;
}
// 高效:使用test进行简单检查
function efficientCheck(text) {
return /pattern/.test(text);
}
3. 注意全局标志的副作用
// 潜在问题:共享的全局正则表达式
const globalRegex = /a/g;
function problematicFunction(text) {
// 第一次调用和后续调用可能得到不同结果
return globalRegex.test(text);
}
// 解决方案:每次重置lastIndex或使用非全局正则表达式
function safeFunction(text) {
globalRegex.lastIndex = 0;
return globalRegex.test(text);
}
4. 优化复杂正则表达式
// 低效:过度使用回溯
const inefficientRegex = /a.*b.*c/;
// 高效:减少回溯
const efficientRegex = /a[^c]*b[^c]*c/;
练习
编写一个函数,使用正则表达式验证中国身份证号码(18位)。
创建一个函数,从文本中提取所有URL链接。
实现一个简单的模板引擎,使用正则表达式替换模板中的变量(如
{{name}}
)。编写一个函数,使用正则表达式将数字转换为带千位分隔符的格式(如
1234567
→1,234,567
)。创建一个函数,使用正则表达式验证密码强度,并返回详细的强度评估(如包含大小写字母、数字、特殊字符等)。
总结
JavaScript中的正则表达式方法提供了强大的文本处理能力,可以用于验证、搜索、替换和分割字符串。RegExp对象的test()
和exec()
方法以及String对象的match()
、matchAll()
、search()
、replace()
、replaceAll()
和split()
方法各有特点和适用场景。
掌握这些方法的使用,结合正则表达式的语法,可以高效地处理各种文本操作任务。同时,了解正则表达式的性能考虑和最佳实践,可以帮助你编写更高效、更可靠的代码。
通过本文的学习和练习,你应该能够自信地在JavaScript项目中使用正则表达式方法,解决各种文本处理需求。