问题:
我有一个搜索ODBC转义序列的函数, 然后用它们的原生等价物递归替换它们。
例如,给定ODBC转义序列,如: {fn concat(column1,column2)}
我将其替换为本机SQL等效项
if (StringComparer.OrdinalIgnoreCase.Equals("concat", strFunctionName)) {
strArguments = strArguments;
string[] astrArguments = strArguments.Split(',');
string strTerm = astrArguments[0] + " || " + astrArguments[1];
return strTerm;
}
产生column1 || column2(PostGreSql语法)
一般来说,我必须解决字符串“arglist_comma_separated” {fn whatever(arglist_comma_separated)}
进入其组件
毋庸置疑,对于我目前的版本,它不起作用 如果arglist中的参数1类似于
'hello, world'
更糟糕的是,如果我以家庭和名字为例,它可能是 {fn what('d'Amato','Alberto')}
我如何最好地分割论点?
如何使用正则表达式或解析?
编辑:
嗯,再想一想,并有一个出色的想法(用一个逃脱角色代替'')
有了这个,对于非正则表达式解决方案,问题的复杂性从中高到降级为:
附录:实际上,只是几乎无足轻重,我忘了考虑功能嵌套在分裂 - 愚蠢。
protected static string[] GetArguments(string strAllArguments)
{
string EscapeCharacter = System.Convert.ToChar(8).ToString();
strAllArguments = strAllArguments.Replace("''", EscapeCharacter);
bool bInString = false;
int iLastSplitAt = 0;
int iInFunction = 0;
System.Collections.Generic.List<string> lsArguments = new System.Collections.Generic.List<string>();
for (int i = 0; i < strAllArguments.Length; i++)
{
char strCurrentChar = strAllArguments[i];
if (strCurrentChar == '\'')
bInString = !bInString;
if (bInString)
continue;
if (strCurrentChar == '(')
iInFunction++;
if (strCurrentChar == ')')
iInFunction--;
if (strCurrentChar == ',')
{
if(iInFunction == 0)
{
string strExtract = strAllArguments.Substring(iLastSplitAt, i - iLastSplitAt);
strExtract = strExtract.Replace(EscapeCharacter, "''");
lsArguments.Add(strExtract);
iLastSplitAt = i;
}
}
}
string strExtractLast = strAllArguments.Substring(iLastSplitAt + 1);
strExtractLast = strExtractLast.Replace(EscapeCharacter, "''");
lsArguments.Add(strExtractLast);
string[] astrResult = lsArguments.ToArray();
lsArguments.Clear();
lsArguments = null;
return astrResult;
}
答案 0 :(得分:1)
(编辑:我清理过这个答案很多,因为很多反馈迭代;下面是我对这个主题的结论)
解析复杂的语言是一项艰巨的任务,因此我假设您将问题缩小到处理以逗号分隔的标记值列表(例如字符串,数字,简单标识符等 - 而不是复杂的表达式)。如果我弄错了,你手中的问题可能比你想象的要大。在这种情况下,我建议this question作为起点。
最简单的解决方案 - 在,
上拆分 - 主要不是因为字符串,因为逗号可以出现在字符串中。解析字符串是一项简单的任务,假设您正确处理转义字符:它以引号开头,具有零个或多个字符,并以另一个引号结束。
在大多数语言中,如果字符串由'
分隔,则可以使用\'
转义其中的引号。 SQL将字符串中的''
解释为转义引用。如果您知道只有其中一种形式存在,您可以忽略另一种形式。在下面的答案中,我决定将两者都包括在内。
此外,某些语言接受单引号('
)和双引号("
)来分隔字符串。关于转义字符的相同观察结果适用。我的解决方案也涉及两种形式。
除了字符串之外,指定参数的有效字符也很重要。为简单起见,我认为它将是“任何不是逗号”。出于同样的原因,我提出的解决方案将接受任意数量的字符串和非字符串,并将它们组合在一起,将它们作为单个实体返回(重申,如果需要复杂表达式,则应采用更通用的解析技术这个简单的解决方案)。
实现这一点的一种方法是在应用上述逻辑时循环查看字符,就像在最近的更新中所做的那样。另一种是使用正则表达式。正则表达式具有更好的性能(通常)和更清晰的代码,更不容易出错。主要的骗局是正则表达式本身的复杂性,因为“密集”格式可能更难理解/维护。
我建议的正则表达式将是(为了便于阅读而添加空格/换行符):
(
(?: \' (?: ['\\]\' | [^'] )* \' |
\" (?: ["\\]\" | [^"] )* \" |
[^,'"]
)+
)
(?: \, | $)
简短格式:
((?:\'(?:['\\]\'|[^'])*\'|\"(?:["\\]\"|[^"])*\"|[^,'"])+)(?:\,|$)
每个字符串都接受转义引号('
或\
后跟'
)或任何非引号的“字符”。匹配(大捕获组)必须后跟,
或输入结束。
上面的正则表达式的实例可以看出here(该示例使用Ruby,但在C#中应该同样有效)。只要整个输入匹配(即不存在不匹配的子字符串),每个匹配将正确捕获参数。 警告:格式错误的输入会产生错误的输出,因此上面的正则表达式必须不用于验证。
要在C#代码中使用此解决方案,您可以使用Regex.Matches
:
MatchCollection matches = Regex.Matches(strArguments, "((?:\'(?:['\\]\'|[^'])*\'|\"(?:["\\]\"|[^"])*\"|[^,'"])+)(?:\,|$)");
string[] arguments = from m in matches select m.Captures[1].Value;
如上所述,您还必须确保匹配涵盖整个输入。我把它作为读者的练习......;)
备注:强>
Matches
的结果不重叠;如果我弄错了,上面的代码必须适应从前一个结束的索引开始的每个匹配;