如何在 JavaScript 中将长正则表达式拆分为多行?

IT技术 javascript regex jslint expression readability
2021-01-15 19:03:56

我有一个很长的正则表达式,我希望在我的 JavaScript 代码中将其拆分为多行,以根据 JSLint 规则保持每行长度为 80 个字符。我认为,它更适合阅读。这是模式示例:

var pattern = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
6个回答

扩展@KooiInc 答案,您可以避免使用对象source属性手动转义每个特殊字符RegExp

例子:

var urlRegex= new RegExp(''
  + /(?:(?:(https?|ftp):)?\/\/)/.source     // protocol
  + /(?:([^:\n\r]+):([^@\n\r]+)@)?/.source  // user:pass
  + /(?:(?:www\.)?([^\/\n\r]+))/.source     // domain
  + /(\/[^?\n\r]+)?/.source                 // request
  + /(\?[^#\n\r]*)?/.source                 // query
  + /(#?[^\n\r]*)?/.source                  // anchor
);

或者,如果您想避免重复该.source属性,您可以使用以下Array.map()函数:

var urlRegex= new RegExp([
  /(?:(?:(https?|ftp):)?\/\/)/      // protocol
  ,/(?:([^:\n\r]+):([^@\n\r]+)@)?/  // user:pass
  ,/(?:(?:www\.)?([^\/\n\r]+))/     // domain
  ,/(\/[^?\n\r]+)?/                 // request
  ,/(\?[^#\n\r]*)?/                 // query
  ,/(#?[^\n\r]*)?/                  // anchor
].map(function(r) {return r.source}).join(''));

在 ES6 中,map 函数可以简化为: .map(r => r.source)

绝对的,这个!超级好,能够评论每个子正则表达式。
2021-03-17 19:03:56
非常聪明。谢谢,这个想法对我帮助很大。顺便说一句:我将整个事情封装在一个函数中,使其更加清晰:combineRegex = (...regex) => new RegExp(regex.map(r => r.source).join(""))用法:combineRegex(/regex1/, /regex2/, ...)
2021-03-19 19:03:56
正是我要找的,超级干净。谢谢!
2021-03-23 19:03:56
谢谢,它有助于将源代码放入正则表达式函数中
2021-03-26 19:03:56
这对于向长正则表达式添加注释非常方便。但是,它受到在同一行上有匹配括号的限制。
2021-04-07 19:03:56

您可以将其转换为字符串并通过调用创建表达式new RegExp()

var myRE = new RegExp (['^(([^<>()[\]\\.,;:\\s@\"]+(\\.[^<>(),[\]\\.,;:\\s@\"]+)*)',
                        '|(\\".+\\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.',
                        '[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\\.)+',
                        '[a-zA-Z]{2,}))$'].join(''));

笔记:

  1. 表达式文字转换为字符串时,您需要转义所有反斜杠,因为在评估字符串文字时会消耗反斜杠(有关更多详细信息,请参阅 Kayo 的评论。)
  2. RegExp 接受修饰符作为第二个参数

    /regex/g => new RegExp('regex', 'g')

[添加ES20xx(标记模板)]

在 ES20xx 中,您可以使用标记模板请参阅片段。

笔记:

  • 这里缺点是,你不能在正则表达式字符串使用纯空格(经常使用\s\s+\s{1,x}\t\n等)。

(() => {
  const createRegExp = (str, opts) => 
    new RegExp(str.raw[0].replace(/\s/gm, ""), opts || "");
  const yourRE = createRegExp`
    ^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|
    (\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|
    (([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$`;
  console.log(yourRE);
  const anotherLongRE = createRegExp`
    (\byyyy\b)|(\bm\b)|(\bd\b)|(\bh\b)|(\bmi\b)|(\bs\b)|(\bms\b)|
    (\bwd\b)|(\bmm\b)|(\bdd\b)|(\bhh\b)|(\bMI\b)|(\bS\b)|(\bMS\b)|
    (\bM\b)|(\bMM\b)|(\bdow\b)|(\bDOW\b)
    ${"gi"}`;
  console.log(anotherLongRE);
})();

更为清晰的方式做,这是创建命名变量保持有意义的小节,并加入那些为字符串或数组。这使您RegExp可以以更容易理解的方式构建
2021-03-20 19:03:56
此外,MDN 建议在正则表达式保持不变时使用文字符号,而在正则表达式可以更改时使用构造函数符号。developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
2021-03-22 19:03:56
Anew RegExp是多行正则表达式的好方法。您可以只使用字符串连接运算符,而不是连接数组:var reg = new RegExp('^([a-' + 'z]+)$','i');
2021-04-03 19:03:56
注意:使用上述答案可以将正则表达式文字分成多行。但是它需要小心,因为您不能简单地复制正则表达式文字(用 定义//)并将其作为字符串参数粘贴到 RegExp 构造函数。这是因为在评估字符串文字时会消耗反斜杠字符示例:/Hey\sthere/不能被替换new RegExp("Hey\sthere")相反,它应该被替换为new RegExp("Hey\\sthere")注意额外的反斜杠!因此我更喜欢在一个长行上留下一个很长的正则表达式文字
2021-04-09 19:03:56

使用字符串new RegExp很尴尬,因为你必须转义所有的反斜杠。您可以编写较小的正则表达式并将它们连接起来。

让我们拆分这个正则表达式

/^foo(.*)\bar$/

稍后我们将使用一个函数让事物变得更漂亮

function multilineRegExp(regs, options) {
    return new RegExp(regs.map(
        function(reg){ return reg.source; }
    ).join(''), options);
}

现在让我们摇滚

var r = multilineRegExp([
     /^foo/,  // we can add comments too
     /(.*)/,
     /\bar$/
]);

由于它有成本,尝试只构建一次真正的正则表达式,然后使用它。

不过有一个警告:您需要确保您的子正则表达式是自包含的,或者将每个子正则表达式包装在一个新的括号组中。示例:multilineRegExp([/a|b/, /c|d])结果为/a|bc|d/,而您的意思是(a|b)(c|d)
2021-04-01 19:03:56
这非常酷——不仅您不必进行额外的转义,而且您还保留了子正则表达式的特殊语法高亮!
2021-04-09 19:03:56

感谢模板文字的奇妙世界,您现在可以在 ES6 中编写大的、多行的、注释良好的甚至语义嵌套的正则表达式。

//build regexes without worrying about
// - double-backslashing
// - adding whitespace for readability
// - adding in comments
let clean = (piece) => (piece
    .replace(/((^|\n)(?:[^\/\\]|\/[^*\/]|\\.)*?)\s*\/\*(?:[^*]|\*[^\/])*(\*\/|)/g, '$1')
    .replace(/((^|\n)(?:[^\/\\]|\/[^\/]|\\.)*?)\s*\/\/[^\n]*/g, '$1')
    .replace(/\n\s*/g, '')
);
window.regex = ({raw}, ...interpolations) => (
    new RegExp(interpolations.reduce(
        (regex, insert, index) => (regex + insert + clean(raw[index + 1])),
        clean(raw[0])
    ))
);

使用它,您现在可以像这样编写正则表达式:

let re = regex`I'm a special regex{3} //with a comment!`;

输出

/I'm a special regex{3}/

或者多线呢?

'123hello'
    .match(regex`
        //so this is a regex

        //here I am matching some numbers
        (\d+)

        //Oh! See how I didn't need to double backslash that \d?
        ([a-z]{1,3}) /*note to self, this is group #2*/
    `)
    [2]

输出hel,整洁!
“如果我需要实际搜索换行符怎么办?”,然后使用\n愚蠢的!
在我的 Firefox 和 Chrome 上工作。


好的,“来点更复杂的东西怎么样?”
当然,这是我正在研究的对象解构 JS 解析器的一部分

regex`^\s*
    (
        //closing the object
        (\})|

        //starting from open or comma you can...
        (?:[,{]\s*)(?:
            //have a rest operator
            (\.\.\.)
            |
            //have a property key
            (
                //a non-negative integer
                \b\d+\b
                |
                //any unencapsulated string of the following
                \b[A-Za-z$_][\w$]*\b
                |
                //a quoted string
                //this is #5!
                ("|')(?:
                    //that contains any non-escape, non-quote character
                    (?!\5|\\).
                    |
                    //or any escape sequence
                    (?:\\.)
                //finished by the quote
                )*\5
            )
            //after a property key, we can go inside
            \s*(:|)
      |
      \s*(?={)
        )
    )
    ((?:
        //after closing we expect either
        // - the parent's comma/close,
        // - or the end of the string
        \s*(?:[,}\]=]|$)
        |
        //after the rest operator we expect the close
        \s*\}
        |
        //after diving into a key we expect that object to open
        \s*[{[:]
        |
        //otherwise we saw only a key, we now expect a comma or close
        \s*[,}{]
    ).*)
$`

它输出 /^\s*((\})|(?:[,{]\s*)(?:(\.\.\.)|(\b\d+\b|\b[A-Za-z$_][\w$]*\b|("|')(?:(?!\5|\\).|(?:\\.))*\5)\s*(:|)|\s*(?={)))((?:\s*(?:[,}\]=]|$)|\s*\}|\s*[{[:]|\s*[,}{]).*)$/

并通过一个小演示运行它?

let input = '{why, hello, there, "you   huge \\"", 17, {big,smelly}}';
for (
    let parsed;
    parsed = input.match(r);
    input = parsed[parsed.length - 1]
) console.log(parsed[1]);

成功输出

{why
, hello
, there
, "you   huge \""
, 17
,
{big
,smelly
}
}

请注意成功捕获带引号的字符串。
我在 Chrome 和 Firefox 上测试过,效果很好!

如果好奇,你可以检出我在做什么,以及它的示范
虽然它只适用于 Chrome,因为 Firefox 不支持反向引用或命名组。所以请注意,这个答案中给出的例子实际上是一个绝育版本,可能很容易被欺骗接受无效的字符串。

虽然我自己从未做过,但这里有一个非常详尽的教程:zellwk.com/blog/publish-to-npm我建议在页面末尾检查 np。我从未使用过它,但 Sindre Sorhus 是这些东西的魔术师,所以我不会放弃它。
2021-03-25 19:03:56
@Siddharth 去吧。我似乎还没有解决它。github 上的Hashbrown777也是
2021-03-25 19:03:56
2021-03-25 19:03:56
你应该考虑将它导出为一个 NodeJS 包,这太棒了
2021-04-07 19:03:56
嘿@Hashbrown,你介意我把它做成一个包裹吗?我当然会给你归属
2021-04-12 19:03:56

这里有很好的答案,但为了完整起见,有人应该提到 Javascript 的核心特性,即使用原型链继承像这样的事情说明了这个想法:

RegExp.prototype.append = function(re) {
  return new RegExp(this.source + re.source, this.flags);
};

let regex = /[a-z]/g
.append(/[A-Z]/)
.append(/[0-9]/);

console.log(regex); //=> /[a-z][A-Z][0-9]/g

这是这里最好的答案。
2021-03-13 19:03:56