我试图在一行中找到所有引用的文字。
示例:
"Some Text"
"Some more Text"
"Even more text about \"this text\""
我需要得到:
"Some Text"
"Some more Text"
"Even more text about \"this text\""
\"[^\"\r]*\"
给了我除了最后一个之外的所有内容,因为有引号转义。
我已阅读有关\"[^\"\\]*(?:\\.[^\"\\]*)*\"
正常工作的信息,但我在运行时遇到错误:
parsing ""[^"\]*(?:\.[^"\]*)*"" - Unterminated [] set.
我该如何解决这个问题?
答案 0 :(得分:78)
你所拥有的是Friedl的“展开循环”技术的一个例子,但你似乎对如何将它表达为字符串文字有一些困惑。这是它应该如何看待正则表达式编译器:
"[^"\\]*(?:\\.[^"\\]*)*"
初始"[^"\\]*
匹配引号,后跟零或更多除引号或反斜杠之外的任何字符。单独的那一部分以及最终的"
将匹配一个简单的带引号的字符串,没有嵌入的转义序列,如"this"
或""
。
如果 遇到反斜杠,\\.
会消耗反斜杠及其后的任何内容,而[^"\\]*
(再次)会消耗掉下一个反斜杠或引号的所有内容。该部分会根据需要重复多次,直到未转义的引号出现(或者它到达字符串的末尾并且匹配尝试失败)。
请注意,这将与"foo\"-
中的\"foo\"-"bar"
匹配。这似乎暴露了正则表达式中的一个缺陷,但它没有;这是输入无效。目标是匹配引用的字符串,可选地包含嵌入其他文本的反斜杠转义引号 - 为什么引用字符串的外部转义?如果你真的需要支持它,你就会遇到一个更复杂的问题,需要一种非常不同的方法。
正如我所说,上面是正则表达式应该如何看待正则表达式编译器。但是你是以字符串文字的形式写的,而那些往往特别对待某些字符 - 即反斜杠和引号。幸运的是,C#的逐字字符串为您省去了双重逃避反斜杠的麻烦;你只需要用另一个引号转义每个引号:
Regex r = new Regex(@"""[^""\\]*(?:\\.[^""\\]*)*""");
因此规则是C#编译器的双引号和正则表达式编译器的双反斜杠 - 非常简单。这个特殊的正则表达式可能看起来有点尴尬,两端都有三个引号,但请考虑另一种选择:
Regex r = new Regex("\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"");
在Java中,总是必须以这种方式编写它们。 : - (
答案 1 :(得分:12)
用于捕获字符串的正则表达式(使用\
进行字符转义),用于.NET引擎:
(?>(?(STR)(?(ESC).(?<-ESC>)|\\(?<ESC>))|(?!))|(?(STR)"(?<-STR>)|"(?<STR>))|(?(STR).|(?!)))+
这里是一个“友好”的版本:
(?> | especify nonbacktracking
(?(STR) | if (STRING MODE) then
(?(ESC) | if (ESCAPE MODE) then
.(?<-ESC>) | match any char and exits escape mode (pop ESC)
| | else
\\(?<ESC>) | match '\' and enters escape mode (push ESC)
) | endif
| | else
(?!) | do nothing (NOP)
) | endif
| | -- OR
(?(STR) | if (STRING MODE) then
"(?<-STR>) | match '"' and exits string mode (pop STR)
| | else
"(?<STR>) | match '"' and enters string mode (push STR)
) | endif
| | -- OR
(?(STR) | if (STRING MODE) then
. | matches any character
| | else
(?!) | do nothing (NOP)
) | endif
)+ | REPEATS FOR EVERY CHARACTER
基于http://tomkaminski.com/conditional-constructs-net-regular-expressions示例。它依赖于报价平衡。我非常成功地使用它。与Singleline
标志一起使用。
要使用正则表达式,我建议Rad Software Regular Expression Designer,它有一个很好的“语言元素”选项卡,可以快速访问一些基本指令。它基于.NET的正则表达式引擎。
答案 2 :(得分:4)
"(\\"|\\\\|[^"\\])*"
应该有效。匹配转义引号,转义反斜杠或除引号或反斜杠字符之外的任何其他字符。重复。
在C#中:
StringCollection resultList = new StringCollection();
Regex regexObj = new Regex(@"""(\\""|\\\\|[^""\\])*""");
Match matchResult = regexObj.Match(subjectString);
while (matchResult.Success) {
resultList.Add(matchResult.Value);
matchResult = matchResult.NextMatch();
}
编辑:在列表中添加了转义反斜杠,以便正确处理"This is a test\\"
。
说明:
首先匹配引号字符。
然后从左到右评估替代方案。引擎首先尝试匹配转义的引用。如果不匹配,则尝试转义反斜杠。这样,它就可以区分"Hello \" string continues"
和"String ends here \\"
。
如果两者不匹配,则除了引号或反斜杠字符外,还允许其他任何内容。然后重复一遍。
最后,匹配收尾报价。
答案 3 :(得分:3)
答案 4 :(得分:2)
正则表达式
(?<!\\)".*?(?<!\\)"
还将处理以转义引号开头的文本:
\"Some Text\" Some Text "Some Text", and "Some more Text" an""d "Even more text about \"this text\""
答案 5 :(得分:1)
我知道这不是最干净的方法,但是在我的例子中,我会在"
之前检查字符,看看它是否是\
。如果是的话,我会忽略这句话。
答案 6 :(得分:1)
与@Blankasaurus发布的RegexBuddy类似,RegexMagic也有帮助。
答案 7 :(得分:1)
不使用?
的简单答案是
"([^\\"]*(\\")*)*\"
或作为逐字字符串
@"^""([^\\""]*(\\"")*(\\[^""])*)*"""
这只是意味着:
"
\
或"
\"
"
我相信它的效果和@Alan Moore的答案一样好,但对我来说,更容易理解。它也接受无与伦比的(“不平衡”)报价。
答案 8 :(得分:1)
嗯,艾伦摩尔的答案很好,但我会稍微修改它以使其更紧凑。对于正则表达式编译器:
"([^"\\]*(\\.)*)*"
与艾伦摩尔的表达相比:
"[^"\\]*(\\.[^"\\]*)*"
解释与艾伦摩尔的解释非常相似:
第一部分"
与引号匹配。
第二部分[^"\\]*
匹配除引号或反斜杠以外的任何字符中的零个或多个。
最后一部分(\\.)*
匹配反斜杠以及跟随它的任何单个字符。注意*,说这个组是可选的。
描述的部分以及最终的"
(即"[^"\\]*(\\.)*"
)将匹配:“Some Text”和“Even more Text \”“,但不匹配:”更多文字关于“这篇文章”。“
为了使它成为可能,我们需要部分:[^"\\]*(\\.)*
根据需要重复多次,直到未转义的引号出现(或者它到达字符串的末尾并且匹配尝试失败)。所以我用括号括起那个部分并添加了一个星号。现在它匹配:“Some Text”,“更多Text”“,”更多关于\“this text \”“和”Hello \\“的文字。
在C#代码中,它将如下所示:
var r = new Regex("\"([^\"\\\\]*(\\\\.)*)*\"");
顺便说一下,两个主要部分的顺序:[^"\\]*
和(\\.)*
并不重要。你可以写:
"([^"\\]*(\\.)*)*"
或
"((\\.)*[^"\\]*)*"
结果将是相同的。
现在我们需要解决另一个问题:\"foo\"-"bar"
。当前表达式将与"foo\"-"
匹配,但我们希望将其与"bar"
匹配。我不知道
为什么在引用的字符串
之外会有在之外的引号
但是我们可以通过在开头添加以下部分来轻松实现它:(\G|[^\\])
。它表示我们希望匹配从上一个匹配结束的点开始,或者在除反斜杠之外的任何字符之后。我们为什么需要\G
?这适用于以下情况,例如:"a""b"
。
请注意,(\G|[^\\])"([^"\\]*(\\.)*)*"
与-"bar"
中的\"foo\"-"bar"
匹配。因此,要仅获取"bar"
,我们需要指定该组并可选地为其指定名称,例如“MyGroup”。那么C#代码将如下所示:
[TestMethod]
public void RegExTest()
{
//Regex compiler: (?:\G|[^\\])(?<MyGroup>"(?:[^"\\]*(?:\.)*)*")
string pattern = "(?:\\G|[^\\\\])(?<MyGroup>\"(?:[^\"\\\\]*(?:\\\\.)*)*\")";
var r = new Regex(pattern, RegexOptions.IgnoreCase);
//Human readable form: "Some Text" and "Even more Text\"" "Even more text about \"this text\"" "Hello\\" \"foo\" - "bar" "a" "b" c "d"
string inputWithQuotedText = "\"Some Text\" and \"Even more Text\\\"\" \"Even more text about \\\"this text\\\"\" \"Hello\\\\\" \\\"foo\\\"-\"bar\" \"a\"\"b\"c\"d\"";
var quotedList = new List<string>();
for (Match m = r.Match(inputWithQuotedText); m.Success; m = m.NextMatch())
quotedList.Add(m.Groups["MyGroup"].Value);
Assert.AreEqual(8, quotedList.Count);
Assert.AreEqual("\"Some Text\"", quotedList[0]);
Assert.AreEqual("\"Even more Text\\\"\"", quotedList[1]);
Assert.AreEqual("\"Even more text about \\\"this text\\\"\"", quotedList[2]);
Assert.AreEqual("\"Hello\\\\\"", quotedList[3]);
Assert.AreEqual("\"bar\"", quotedList[4]);
Assert.AreEqual("\"a\"", quotedList[5]);
Assert.AreEqual("\"b\"", quotedList[6]);
Assert.AreEqual("\"d\"", quotedList[7]);
}
答案 9 :(得分:0)
您需要做的任何机会:\"[^\"\\\\]*(?:\\.[^\"\\\\]*)*\"
答案 10 :(得分:0)
如果您可以定义开始和结束,则应该可以进行以下操作:
new Regex(@"^(""(.*)*"")$")