如何将这两个正则表达式合并成一个?

3

我正在使用JavaScript编写基本的词法分析器,并使用正则表达式。我有两个正则表达式(一个用于单引号字符串,一个用于双引号字符串),希望将它们合并为一个正则表达式。这是我的两个正则表达式(我添加了^$字符进行测试):

var singleQuotedString = /^'(?:[^'\\]|\\'|\\\\|\\\/|\\b|\\f|\\n|\\r|\\t|\\u[0-9A-F]{4})*'$/gi;
var doubleQuotedString = /^"(?:[^"\\]|\\"|\\\\|\\\/|\\b|\\f|\\n|\\r|\\t|\\u[0-9A-F]{4})*"$/gi;

现在,我尝试将它们组合成一个正则表达式,如下:

var string = /^(["'])(?:[^\1\\]|\\\1|\\\\|\\\/|\\b|\\f|\\n|\\r|\\t|\\u[0-9A-F]{4})*\1$/gi;

但是,当我测试输入"Hello"World!"时,它返回true而不是false:
alert(string.test('"Hello"World!"')); //should return false as a double quoted string must escape double quote characters

我发现问题在于[^\1\\]应该匹配除匹配组\1 (这是字符串的定界符,可以是单引号或双引号)和\\ (这是反斜杠字符)之外的任何字符。

正则表达式正确地过滤了反斜杠并匹配了定界符,但它没有过滤掉字符串中的定界符。非常感谢您的帮助。请注意,我参考了 Crockford 的铁路图来编写这些正则表达式。
3个回答

7
你不能在字符类内引用匹配组:(['"])[^\1\\]。请尝试使用类似以下的方式:
(['"])((?!\1|\\).|\\[bnfrt]|\\u[a-fA-F\d]{4}|\\\1)*\1

(你需要添加更多的转义符,但你知道我的意思...)

简单解释一下:

(['"])             # match a single or double quote and store it in group 1
(                  # start group 2
  (?!\1|\\).       #   if group 1 or a backslash isn't ahead, match any non-line break char
  |                #   OR
  \\[bnfrt]        #   match an escape sequence
  |                #   OR
  \\u[a-fA-F\d]{4} #   match a Unicode escape
  |                #   OR
  \\\1             #   match an escaped quote
)*                 # close group 2 and repeat it zero or more times
\1                 # match whatever group 1 matched

我从未知道你可以像 (?!\1|\\). 这样做。在 MDN 上说,'x(?!y)' 只匹配 'x',当且仅当 'x' 后面不跟着 'y'。 - Aadit M Shah
1
@Aadit M Shah - 这个比喻不太恰当。在正则表达式中,您始终处于字符之间的位置,就像光标一样。断言保持不变,(?!)(?=)始终指向当前位置右侧的内容。除非它没有匹配成功,否则左侧的内容无关紧要,因为您不会在这个断言上。 - user557597
@Aadit M Shah - 不需要告诉微软它的错误,这是一个消耗技术(带文档)的公司。只需在谷歌上搜索“仅当'x'后面没有跟着'y'时才使用'x'”。 - user557597
@sin - MDN代表Mozilla开发者网络,而不是微软开发者网络(MSDN)。Mozilla是JavaScript技术的先驱。有自尊心的程序员会去微软寻求任何帮助吗? - Aadit M Shah
@Aadit M Shah - 哈哈,对不起啊。那个用来解释负向先行断言的一行代码,在多种正则表达式中几乎是标准(还有其他很多1行代码)。实际上并没有太多信息。 - user557597

2

这也应该可以使用(原始正则表达式)。
如果速度是一个因素,那么这就是“展开”方法,据说是这种情况下最快的。

(['"])(?:(?!\\|\1).)*(?:\\(?:[\/bfnrt]|u[0-9A-F]{4}|\1)(?:(?!\\|\1).)*)*/1  

扩展

(['"])            # Capture a quote
(?:
   (?!\\|\1).             # As many non-escape and non-quote chars as possible
)*

(?:                       
    \\                     # escape plus,
    (?:
        [\/bfnrt]          # /,b,f,n,r,t or u[a-9A-f]{4} or captured quote
      | u[0-9A-F]{4}
      | \1
    )
    (?:                
        (?!\\|\1).         # As many non-escape and non-quote chars as possible
    )*
)*

/1                # Captured quote

0

你可以通过在较小的正则表达式上使用交替运算符来创建一个更大的正则表达式。

/(?:single-quoted-regex)|(?:double-quoted-regex)/

或者明确地说:

var string = /(?:^'(?:[^'\\]|\\'|\\\\|\\\/|\\b|\\f|\\n|\\r|\\t|\\u[0-9A-F]{4})*'$)|(?:^"(?:[^"\\]|\\"|\\\\|\\\/|\\b|\\f|\\n|\\r|\\t|\\u[0-9A-F]{4})*"$)/gi;

最后,如果你想要避免代码重复,你可以使用new Regex构造器动态构建正则表达式。

var quoted_string = function(delimiter){
    return ('^' + delimiter + '(?:[^' + delimiter + '\\]|\\' + delimiter + '|\\\\|\\\/|\\b|\\f|\\n|\\r|\\t|\\u[0-9A-F]{4})*' + delimiter + '$').replace(/\\/g, '\\\\');
    //in the general case you could consider using a regex excaping function to avoid backslash hell.
};

var string = new RegExp( '(?:' + quoted_string("'") + ')|(?:' + quoted_string('"') + ')' , 'gi' );

我曾经想过那个方法;但是我又想,一定有更好的方法可以做到。=) - Aadit M Shah
你知道为什么[^\1\\]不能按预期工作吗? - Aadit M Shah
@AaditMShah:我认为你不能在类内部像那样使用反向引用。无论如何,我写了一个不重复正则表达式的版本,但现在你必须决定它是否更易读 ;) - hugomg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接