深入讲解 JavaScript 中如何利用 String.prototype.match() 与 RegExp.prototype.exec() 将字符串提取转换为数组。系统解析全局修饰符 g、括号捕获组、lastIndex 游标推进机制以及现代 matchAll 的最佳实践。
js字符串怎么用match转数组/.exec
时间:2026-11-13
浏览:446
文章目录
/g 的 str.match(regexp) 可以直接输出目标一维数组;若需要提取每一处匹配中的局部细节(捕获组),则应使用 regexp.exec(str) 配合 while 循环逐次推进游标装配数组,或在现代环境中使用 Array.from(str.matchAll(regexp)) 进行一步转换。
一、使用 match() 方法将字符串转为数组
String.prototype.match() 是字符串实例上的原生检索方法。调用 str.match(regexp) 时,返回值的形态与正则表达式是否声明了全局标志 g 存在直接绑定关系。掌握这种分流机制,是正确获取数组的第一步。
1. 全局匹配模式:直接提取所有匹配项为一维数组
当正则表达式末尾附加 g 修饰符时,match() 会扫描整个字符串,将所有符合规则的子串依次提取并放入一个新数组中返回。
const rawText = "订单号: SO202401, 金额: 199元; 订单号: SO202402, 金额: 350元";
// 提取所有连续订单号(以 SO 开头后接数字)
const orderNumbers = rawText.match(/SO\d+/g);
console.log(orderNumbers);
console.log(Array.isArray(orderNumbers));
[ 'SO202401', 'SO202402' ]
true
此时返回的 orderNumbers 是纯正的 JavaScript 数组,可以直接调用 map()、filter() 或通过 .length 获取匹配项总数。
match() 不会返回空数组 [],而是返回 null。若直接对结果调用数组方法(如 str.match(...).map(...)),会抛出 TypeError: Cannot read properties of null。在实际业务中应始终提供默认空数组回退:
const safeList = text.match(/\d+/g) || [];
2. 单次匹配模式:提取首个匹配项及内部捕获组
若正则没有加 g 标志,match() 只查找第一个匹配项。此时返回的虽然仍是一个类数组对象(Array.isArray() 返回 true),但其元素排列遵循特殊的结构:索引 0 为完整匹配子串,索引 1 到 n 对应正则中各个小括号 () 所声明的捕获组。
const logItem = "2026-11-10 [ERROR] Database timeout";
const matchResult = logItem.match(/(\d{4}-\d{2}-\d{2})\s+\[([A-Z]+)\]\s+(.*)/);
// 使用解构赋值直接转换为常规变量或数组
const [fullMatch, date, level, message] = matchResult || [];
console.log(matchResult);
console.log("日期:", date, "级别:", level);
[
'2026-11-10 [ERROR] Database timeout',
'2026-11-10',
'ERROR',
'Database timeout',
index: 0,
input: '2026-11-10 [ERROR] Database timeout',
groups: undefined
]
日期: 2026-11-10 级别: ERROR
二、match() 与 RegExp.prototype.exec() 的底层区别
在很多场景中,读者会发现 match() 和 exec() 可以得到类似的结果,但二者的调用主体、状态维护和返回机制有本质差别:
- 调用方差异:
str.match(reg)是字符串方法,由字符串驱动;reg.exec(str)是正则表达式实例的方法,由正则对象驱动。 - 无
g标志时完全等价:当不包含/g时,str.match(reg)底层直接委托调用reg.exec(str),返回包含捕获组及index、input、groups属性的同构数组。 - 带
g标志时行为分化:match(/.../g)一次性遍历整个字符串,但抛弃所有捕获组;而exec()无论是否带g,单次调用永远只返回一个匹配及其完整捕获组。
三、用 regexp.exec() 循环提取完整数组的标准步骤
当业务要求既要提取字符串中“所有出现的匹配项”,又要获取每一项里的“括号捕获组”时,match(/.../g) 无法完成任务。此时的经典解决方案是使用 regexp.exec(str) 配合 while 循环。
1. 游标 lastIndex 与 while 循环机制
当正则表达式设置了 g 修饰符时,正则实例对象内部会维护一个名为 lastIndex 的整数属性,记录下一次匹配扫描的起始索引。
- 第一次执行
reg.exec(str),从索引 0 开始匹配,找到后将lastIndex设为该匹配项末尾的下一个字符位置,并返回匹配详情数组。 - 第二次调用
reg.exec(str),自动从上一次停下的lastIndex位置继续向后搜索。 - 当找不到更多匹配时,
exec()返回null,同时自动将lastIndex重置为 0。
2. 编写标准化提取代码
以下代码演示如何从一段样式文本中,提取所有的 CSS 键值对,组装成结构化对象数组:
const cssString = "color: #333; font-size: 14px; background: #fff;";
// 必须带上 g 标志,并用圆括号声明捕获组
const ruleRegex = /([a-z-]+)\s*:\s*([^;]+);/g;
// 准备接收结果的目标数组
const parsedRules = [];
let match;
// 赋值表达式放在条件判断中,直到返回 null 终止
while ((match = ruleRegex.exec(cssString)) !== null) {
parsedRules.push({
property: match[1].trim(),
value: match[2].trim(),
startIndex: match.index
});
}
console.log(parsedRules);
[
{ property: 'color', value: '#333', startIndex: 0 },
{ property: 'font-size', value: '14px', startIndex: 13 },
{ property: 'background', value: '#fff', startIndex: 31 }
]
1. 遗漏
/g 标志:如果正则没有声明 g,ruleRegex.lastIndex 永远保持为 0,exec() 每次都命中第一个匹配,while 循环将陷入死循环,耗尽 CPU 导致页面或进程卡死。2. 正则实例全局复用污染:若将带
/g 的正则对象保存在全局或模块顶层变量中,并在多次解析中复用,一旦前一次匹配中途使用 break 退出,lastIndex 将停留在中间位置。下一次解析其他字符串时,会直接从该非零位置开始匹配,造成前面部分内容神秘丢失。在循环前手动将 ruleRegex.lastIndex = 0; 是一种良好的防御习惯。
四、现代最佳实践:ES2020 的 matchAll() 转数组
ECMAScript 2020 (ES11) 为字符串引入了 matchAll() 方法。它专门用于解决 match(/.../g) 丢失捕获组、以及 exec() 需手动维护 while 循环繁琐易错的问题。
str.matchAll(regexp) 返回一个迭代器对象,正则表达式必须包含 /g 标志(否则直接抛出 TypeError)。配合展开运算符 [...iter] 或 Array.from(),可以瞬间转换为包含每项匹配及其所有捕获组的二维数组。
const dataStr = "apple: $5.5, banana: $3.2, orange: $4.0";
const itemRegex = /([a-z]+):\s*\$([0-9.]+)/g;
// 方式 A:使用 Array.from 直接转为常规数组
const allMatches = Array.from(dataStr.matchAll(itemRegex));
// 方式 B:结合 map 进一步纯化提取捕获组
const pureList = allMatches.map(([full, fruit, price]) => ({
fruit,
price: Number(price)
}));
console.log(pureList);
[
{ fruit: 'apple', price: 5.5 },
{ fruit: 'banana', price: 3.2 },
{ fruit: 'orange', price: 4 }
]
五、实战方案对比与选型建议
针对不同的业务开发需求,开发者可以根据下表迅速确定最简便、最安全的转换方式:
| 需求场景 | 推荐方案 | 代码写法示例 | 主要优势与注意 |
|---|---|---|---|
| 仅提取所有符合格式的文本片段 | str.match(/.../g) |
str.match(/\d+/g) || [] |
代码最短、可读性极高。注意使用 || [] 防止空指针。 |
| 提取单个目标及其各级捕获组 | str.match(/.../)(无 g) |
const [all, p1] = str.match(/(a)(b)/) || [] |
开箱即用,通过数组解构直接获取括号对应内容。 |
| 现代工程提取所有项及捕获组 | Array.from(str.matchAll(/.../g)) |
[...str.matchAll(/(\w+)=(\w+)/g)] |
无死循环风险,状态安全,现代浏览器与 Node.js 12+ 全面支持。 |
| 兼容老旧运行环境或大文本流式处理 | reg.exec(str) 配合 while |
while ((m = reg.exec(str))) { ... } |
完全兼容旧版 ES5 环境,可随时通过 break 中断,节约内存。 |
实用工具推荐
绿化、压缩、噪点和虚拟水印,自由调节做旧程度。
选择画质和输出格式,预览压缩前后的大小。