我正在使用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的railroad diagrams来编写正则表达式。
答案 0 :(得分:6)
您无法在字符类中引用匹配的组:(['"])[^\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 :(得分: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
答案 2 :(得分: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' );