如何用正则表达式从NodeJS函数调用中提取参数?
在Node.js中提取函数调用参数:正则表达式方案可行吗?
问题需求
需要从Node.js的函数调用字符串中提取参数,示例如下:
`foo0()` => [""] `foo1(1)` => ["1"] `foo2(1, "HelloThere")` => ["1", "\"HelloThere\""] `foo3(foo1(1), foo2(foo1(1), 2), 3)` => ["foo1(1)", "foo2(foo1(1), 2)", "3"] `foo4( // 123456 foo1(1), abcdef, foo2(1, 2) + foo3(1+2, 333, "Hello"), "\nRandom,((,,,Dummy )),, ,, ,String\n" )` => ["// 123456\n foo1(1)", "abcdef", "foo2(1, 2) + foo3(1+2, 333, \"Hello\")", "\"\nRandom,((,,,Dummy )),, ,, ,String\n\"\n"]
现有尝试
最初尝试用简单逗号分割,但无法处理嵌套括号场景:
> `foo2(1, "HelloThere")`.split(/\s?,\s/g) Array [ "foo2(1", '"HelloThere")' ]
即使先剥离函数名和外层括号,面对嵌套结构依然失效:
> `foo3(foo1(1), foo2(foo1(1), 2), 3)` Array(4) [ "foo3(foo1(1)", "foo2(foo1(1)", "2)", "3)" ]
之后实现了字符遍历解析器,能正确处理括号、引号的平衡,满足需求:
// t 是剥离了外层函数名和括号后的输入字符串 let p0 = 0, p1 = 0, i = 0; pts = 0, // 括号计数 brs = 0, // 方括号计数 qt0 = 0, // 反引号引号状态 qt1 = 0, // 单引号状态 qt2 = 0 // 双引号状态 ; for (const c of t) { switch (c) { case "(": if (!qt0 && !qt1 && !qt2) { pts++; } break; case ")": if (!qt0 && !qt1 && !qt2) { pts--; } break; case "[": if (!qt0 && !qt1 && !qt2) { brs++; } break; case "]": if (!qt0 && !qt1 && !qt2) { brs--; } break; case "`": qt0 = !qt0; break; case "'": qt1 = !qt1; break; case "\"": qt2 = !qt2; break; case ",": if (!pts && !brs && !qt0 && !qt1 && !qt2) { // t.slice(p0, p1) 即为单个参数,可在此处理 p0 = ++p1; continue; } } p1++; } // 处理最后一个参数 const args = [...args, t.slice(p0, p1)];
但希望知道是否可以通过正则表达式直接匹配未被括号、引号包裹的逗号,从而分割参数。
正则表达式解决方案
在ES2018及以上版本(Node.js 10+支持),可以利用正则的递归匹配和正向断言实现需求。核心思路是:匹配逗号时,确保它不在引号内,且当前括号/方括号处于平衡状态。
步骤1:提取函数调用的参数部分
先从完整函数调用字符串中剥离外层函数名和括号:
const funcCall = 'foo3(foo1(1), foo2(foo1(1), 2), 3)'; const paramsStr = funcCall.match(/^\w+\(([\s\S]*)\)$/)?.[1] || '';
步骤2:用正则分割参数
使用递归正则表达式分割参数:
const splitRegex = /,(?=(?:(?:[^`'"\(\)\[\]]|`[^`]*`|'[^']*'|"[^"]*"|\((?R)\)|\[(?R)\]))*$)/;
使用示例
const paramsStr = ` // 123456 foo1(1), abcdef, foo2(1, 2) + foo3(1+2, 333, "Hello"), "\nRandom,((,,,Dummy )),, ,, ,String\n" `; const args = paramsStr.split(splitRegex).map(arg => arg.trimStart()); // 处理空参数场景(如foo0()) const finalArgs = paramsStr.trim() === '' ? [''] : args; console.log(finalArgs); // 输出:["// 123456\n foo1(1)", "abcdef", "foo2(1, 2) + foo3(1+2, 333, \"Hello\")", "\"\nRandom,((,,,Dummy )),, ,, ,String\n\""]
正则说明
,(?=...):匹配逗号,且后面内容满足正向断言条件(?:[^'"()[]]|[^]*|'[^']*'|"[^"]*"|\((?R)\)|\[(?R)\])*:匹配任意字符,要么是非引号/括号的普通字符,要么是成对的引号,要么是成对的括号/方括号(通过(?R)递归匹配内部内容)$:确保断言覆盖到字符串结尾,保证逗号不在任何未闭合的引号或括号内
注意事项
- 正则方案依赖ES2018递归匹配特性,Node.js版本需在10及以上
- 极端复杂的嵌套场景(多层嵌套括号+转义引号)下,正则的可读性和维护性不如手动解析器
- 处理空参数(如
foo0())时,需单独判断参数字符串是否为空,返回[""]
内容的提问来源于stack exchange,提问作者imakak
相关产品推荐
相关产品推荐

