检测单引号内的两个连续单引号
Detect Two Consecutive Single Quotes Inside Single Quotes
我正在努力使这个正则表达式模式完全正确,如果有人有更好的选择,我愿意接受正则表达式之外的其他选项。
情况:
我基本上是想针对 C# 中的文本列解析 T-SQL "in" 子句。所以,我需要像这样取一个字符串值:
"'don''t', 'do', 'anything', 'stupid'"
并将其解释为值列表(稍后我会处理双引号):
"don''t"
"do"
"anything"
"stupid"
我有一个适用于大多数情况的正则表达式,但我正在努力将其推广到可以接受我组内的任何字符或双引号的地步:(?:')([a-z0-9\s(?:'(?='))]+)(?:')[,\w]*
我对正则表达式相当有经验,但很少(如果有的话)发现需要环顾四周(因此相应地降低我对正则表达式体验的评估)。
因此,换句话说,我想获取一串以逗号分隔的值,每个值都包含在单引号中但可以包含双单引号,并输出每个这样的值。
编辑
这是我当前正则表达式的一个无效示例(我的问题是我需要处理分组中的所有字符,并在遇到没有后跟第二个单引号的单引号时停止):
"'don''t', 'do?', 'anything!', '#stupid$'"
如果您仍然考虑基于正则表达式的解决方案,可以使用以下正则表达式:
'(?:''|[^'])*'
或@sln 建议的 "un-rolled" 版本:
'[^']*(?:''[^']*)*'
见demo
它相当简单,它捕获双单引号或任何非单引号的内容。无需使用任何后视或前视。它不处理任何转义的实体,但我在你的问题中没有看到这个要求。
此外,此正则表达式将return匹配易于访问和处理:
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();
输出:
如果你想使用捕获 Collection 功能,你可以在
中将它们全部捕获
单程。
# @"""\s*(?:'([^']*(?:''[^']*)*)'\s*(?:,\s*|(?="")))+"""
"
\s*
(?:
'
( # (1 start)
[^']*
(?:
'' [^']*
)*
) # (1 end)
'
\s*
(?:
, \s*
| (?= " )
)
)+
"
C# 代码:
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);
}
输出:
0 = 'don''t'
1 = 'do'
2 = 'anything'
3 = 'stupid'
Press any key to continue . . .
你为什么不在 ', '
上拆分:
Regex regex = new Regex(@"'\s*,\s*'");
string[] substrings = regex.Split(str);
然后通过修剪处理多余的单引号
在我看来,您是想多了。带有转义引号的引号字符串看起来就像两个 没有 转义引号的字符串,一个紧接着另一个(它们之间甚至没有空格)。
(?:'[^']*')+
当然,您必须删除引号,但您可能不得不进行一些 post 处理,以取消转义引号。
另请注意,我并不是要验证输入或解决可能的错误;例如,我不会费心去匹配字符串之间的逗号。如果输入格式正确,这个正则表达式应该就是您所需要的。
出于可维护性的考虑,我决定不使用正则表达式,而是听从了使用状态机的建议。这是我实施的关键:
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);
}
我正在努力使这个正则表达式模式完全正确,如果有人有更好的选择,我愿意接受正则表达式之外的其他选项。
情况:
我基本上是想针对 C# 中的文本列解析 T-SQL "in" 子句。所以,我需要像这样取一个字符串值:
"'don''t', 'do', 'anything', 'stupid'"
并将其解释为值列表(稍后我会处理双引号):
"don''t"
"do"
"anything"
"stupid"
我有一个适用于大多数情况的正则表达式,但我正在努力将其推广到可以接受我组内的任何字符或双引号的地步:(?:')([a-z0-9\s(?:'(?='))]+)(?:')[,\w]*
我对正则表达式相当有经验,但很少(如果有的话)发现需要环顾四周(因此相应地降低我对正则表达式体验的评估)。
因此,换句话说,我想获取一串以逗号分隔的值,每个值都包含在单引号中但可以包含双单引号,并输出每个这样的值。
编辑 这是我当前正则表达式的一个无效示例(我的问题是我需要处理分组中的所有字符,并在遇到没有后跟第二个单引号的单引号时停止):
"'don''t', 'do?', 'anything!', '#stupid$'"
如果您仍然考虑基于正则表达式的解决方案,可以使用以下正则表达式:
'(?:''|[^'])*'
或@sln 建议的 "un-rolled" 版本:
'[^']*(?:''[^']*)*'
见demo
它相当简单,它捕获双单引号或任何非单引号的内容。无需使用任何后视或前视。它不处理任何转义的实体,但我在你的问题中没有看到这个要求。
此外,此正则表达式将return匹配易于访问和处理:
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();
输出:
如果你想使用捕获 Collection 功能,你可以在
中将它们全部捕获
单程。
# @"""\s*(?:'([^']*(?:''[^']*)*)'\s*(?:,\s*|(?="")))+"""
"
\s*
(?:
'
( # (1 start)
[^']*
(?:
'' [^']*
)*
) # (1 end)
'
\s*
(?:
, \s*
| (?= " )
)
)+
"
C# 代码:
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);
}
输出:
0 = 'don''t'
1 = 'do'
2 = 'anything'
3 = 'stupid'
Press any key to continue . . .
你为什么不在 ', '
上拆分:
Regex regex = new Regex(@"'\s*,\s*'");
string[] substrings = regex.Split(str);
然后通过修剪处理多余的单引号
在我看来,您是想多了。带有转义引号的引号字符串看起来就像两个 没有 转义引号的字符串,一个紧接着另一个(它们之间甚至没有空格)。
(?:'[^']*')+
当然,您必须删除引号,但您可能不得不进行一些 post 处理,以取消转义引号。
另请注意,我并不是要验证输入或解决可能的错误;例如,我不会费心去匹配字符串之间的逗号。如果输入格式正确,这个正则表达式应该就是您所需要的。
出于可维护性的考虑,我决定不使用正则表达式,而是听从了使用状态机的建议。这是我实施的关键:
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);
}