我正在使用一些SQL where子句解析并设计一个有效的RegEx来使用.NET API使用“Rad Software Regular Expression Desginer”查找字符串文字之外的列。为了确保设计的RegEx也适用于Java,我当然使用API(1.5和1.6)测试它。但猜猜是什么,它不会起作用。我收到了消息
“Look-behind组在索引28附近没有明显的最大长度。”
我正在尝试解析的字符串是
Column_1='test''the''stuff''all''day''long' AND Column_2='000' AND TheVeryColumnIWantToFind = 'Column_1=''test''''the''''stuff''''all''''day''''long'' AND Column_2=''000'' AND TheVeryColumnIWantToFind = '' TheVeryColumnIWantToFind = '' AND (Column_3 is null or Column_3 = ''Not interesting'') AND ''1'' = ''1''' AND (Column_3 is null or Column_3 = 'Still not interesting') AND '1' = '1'
正如您可能已经猜到的那样,我尝试创建某种最坏的情况,以确保RegEx不会在更复杂的SQL where子句中失败。
RegEx本身就像这样
(?i:(?<!=\s*'(?:[^']|(?:''))*)((?<=\s*)TheVeryColumnIWantToFind(?=(?:\s+|=))))
我不确定是否有一个更优雅的RegEx(最有可能是一个),但现在这并不重要,因为它可以解决问题。
用几句话解释RegEx: 如果它找到我之后的列,它会做一个负面的后视来确定列名是否用在字符串文字中。如果是这样,它将不匹配。如果没有,它会匹配。
回到问题。正如我之前提到的,它不适用于Java。什么会起作用并产生我想要的东西?
我发现,Java似乎不支持无限制的后视,但我仍然无法让它工作
从搜索偏移到当前搜索位置,后视总是对自己施加限制,这不是正确的吗?那么它会产生类似“位置 - 偏移”的东西吗?
答案 0 :(得分:0)
我终于找到了解决方案,因为我在这里问了这个问题,我当然会与你分享。
private static final String SQL_STRING_LITERALS_REGEX = "'(?:(?:[^']|(?:''))*)'";
private static final char DOT = '.';
private ArrayList<int[]> getNonStringLiteralRegions(String exclusion) {
ArrayList<int[]> regions = new ArrayList<int[]>();
int lastEnd = 0;
Matcher m = Pattern.compile(SQL_STRING_LITERALS_REGEX).matcher(exclusion);
while (m.find()) {
regions.add(new int[] {lastEnd, m.start()});
lastEnd = m.end();
}
if (lastEnd < exclusion.length())
// We didn't cover the last part of the exclusion yet.
regions.add(new int[] {lastEnd, exclusion.length()});
return regions;
}
protected final String getFixedExclusion(String exclusion, String[] columns, String alias) {
if (alias == null)
throw new NullPointerException("Alias must not be null.");
else if (alias.charAt(alias.length() - 1) != DOT)
alias += DOT;
StringBuilder b = new StringBuilder(exclusion);
ArrayList<int[]> regions = getNonStringLiteralRegions(exclusion);
for (int i = regions.size() - 1; i >= 0; --i) {
// Reverse iteration to keep valid indices for the lower regions.
int start = regions.get(i)[0], end = regions.get(i)[1];
String s = exclusion.substring(start, end);
for (String column : columns)
s = s.replaceAll("(?<=^|[\\W&&\\D])(?i:" + column + ")(?=[\\W&&\\D]|$)", alias + column);
b.replace(start, end, s);
}
return b.toString();
}
这次的诀窍是简单地找到任何SQL字符串文字,并在用“Alias.ColumnName”替换列时避免使用它们。更换时确保整个列名称很重要。因此,如果我们要在where子句
中替换列“Column_1”WHERE Column_1 = Column_2 AND Column_11 = Column_22
“Column_11”将保持不变。 (我认为记住这一点很重要,这就是为什么我在这里提到面对类似问题的人。)
不过,我认为这只是一种解决方法,如果你能避免使用这种逻辑,最好这样做
好的,感谢您的帮助,我很乐意回答您即将提出的问题,如果有的话。