我努力让这种正则表达式完全正确,如果有人有更好的选择,我会对正则表达式以外的其他选项持开放态度。
情况:
我基本上想要解析T-SQL""针对C#中的文本列的子句。所以,我需要采用这样的字符串值:
"'don''t', 'do', 'anything', 'stupid'"
并将其解释为值列表(我稍后会处理双引号):
"don''t"
"do"
"anything"
"stupid"
我有一个适用于大多数情况的正则表达式,但是我很难将它概括为可以接受任何字符或我的组中的双引号单引号:(?:')([a-z0-9\s(?:'(?='))]+)(?:')[,\w]*
我对正则表达式有相当的经验,但很少(如果有的话)发现需要进行环顾(因此降低了对我的正则表达式体验的评估)。
所以,换句话说,我想要取一串逗号分隔的值,每个值都用单引号括起来但可以包含加倍的单引号,并输出每个这样的值。
修改 这是我当前正则表达式的一个非工作示例(我的问题是我需要处理我的分组中的所有字符,当我遇到单引号后没有第二个单引号时停止):
"'don''t', 'do?', 'anything!', '#stupid$'"
答案 0 :(得分:2)
如果您仍然考虑使用基于正则表达式的解决方案,则可以使用以下正则表达式:
'(?:''|[^'])*'
或者"未展开" @sln建议的版本:
'[^']*(?:''[^']*)*'
请参阅demo
这很简单,它捕获双引号或任何不是单引号的内容。无需使用任何后视或前瞻。它没有处理任何转义的实体,但我在你的问题中没有看到这个要求。
此外,此正则表达式将返回易于访问和处理的匹配:
var text = "'don''t', 'do', 'anything', 'stupid'";
var re = new Regex(@"'[^']*(?:''[^']*)*'"); // Updated thanks to @sln, previous (@"'(?:''|[^'])*'");
var match_values = re.Matches(text).Cast<Match>().Select(p => p.Value).ToList();
输出:
答案 1 :(得分:2)
如果您想使用捕捉收藏功能,您可以全部抓取它们 单程。
string strSrc = "\"'don''t', 'do', 'anything', 'stupid'\"";
Regex rx = new Regex(@"""\s*(?:'([^']*(?:''[^']*)*)'\s*(?:,\s*|(?="")))+""");
Match srcMatch = rx.Match(strSrc);
if (srcMatch.Success)
{
CaptureCollection cc = srcMatch.Groups[1].Captures;
for (int i = 0; i < cc.Count; i++)
Console.WriteLine("{0} = '{1}'", i, cc[i].Value);
}
C#代码:
0 = 'don''t'
1 = 'do'
2 = 'anything'
3 = 'stupid'
Press any key to continue . . .
输出:
{{1}}
答案 2 :(得分:1)
为什么不拆分', '
:
Regex regex = new Regex(@"'\s*,\s*'");
string[] substrings = regex.Split(str);
然后通过修剪来处理额外的单引号
答案 3 :(得分:0)
在我看来,你过度思考这个问题。带有转义引号的带引号的字符串看起来就像两个字符串没有转义引号,一个接着另一个(它们之间甚至不是空格)。
Math.sqrt(parseFloat($scope.console).toString();
当然,您必须删除封闭的引号,但无论如何您可能不得不进行一些后处理,以取消转义的引号。
另请注意,我不是要尝试验证输入或解决可能的错误;例如,我不打扰匹配字符串之间的逗号。如果输入结构良好,那么这个正则表达式应该就是您所需要的。
答案 4 :(得分:0)
为了可维护性,我决定不使用正则表达式,并遵循使用状态机的建议。这是我实施的关键:
string currentTerm = string.Empty;
State currentState = State.BetweenTerms;
foreach (char c in valueToParse)
{
switch (currentState)
{
// if between terms, only need to do something if we encounter a single quote, signalling to start a new term
// encloser is client-specified char to look for (e.g. ')
case State.BetweenTerms:
if (c == encloser)
{
currentState = State.InTerm;
}
break;
case State.InTerm:
if (c == encloser)
{
if (valueToParse.Length > index + 1 && valueToParse[index + 1] == encloser && valueToParse.Length > index + 2)
{
// if next character is also encloser then add it and move on
currentTerm += c;
}
else if (currentTerm.Length > 0 && currentTerm[currentTerm.Length - 1] != encloser)
{
// on an encloser and didn't just add encloser, so we are done
// converterFunc is a client-specified Func<string,T> to return terms in the specified type (to allow for converting to int, for example)
yield return converterFunc(currentTerm);
currentTerm = string.Empty;
currentState = State.BetweenTerms;
}
}
else
{
currentTerm += c;
}
break;
}
index++;
}
if (currentTerm.Length > 0)
{
yield return converterFunc(currentTerm);
}