【问题标题】:Java RegEx API "Look-behind group does not have an obvious maximum length near index ..."Java RegEx API“后视组在索引附近没有明显的最大长度......”
【发布时间】:2011-02-12 22:35:01
【问题描述】:

我正在处理一些 SQL where 子句解析并设计了一个工作正则表达式,以使用使用 .NET API 的“Rad Software Regular Expression Desginer”来查找字符串文字之外的列。为了确保设计的 RegEx 也适用于 Java,我当然使用 API(1.5 和 1.6)对其进行了测试。但你猜怎么着,这行不通。我收到了消息

“后视组在索引 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 似乎不支持无限后视,但我仍然无法让它工作。
从搜索偏移量到当前搜索位置,后视总是对自身施加限制,这不是正确的吗?所以它会导致类似“位置 - 偏移”的东西?

【问题讨论】:

  • 这很像问如何用正则表达式解析 HTML... 你需要这个做什么?验证 SQL?
  • 我想将它用于 String.replaceAll(String, String) 以用“Alias.ColumnName”替换这些列,因为它们很简单。当然,我宁愿更正传入的字符串以消除需要此“修复”的根本原因,但不幸的是我无法触及。

标签: java regex negative-lookbehind


【解决方案1】:

我终于找到了解决方案,因为我在这里提出了这个问题,所以我当然会与你分享。

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”保持不变。 (我认为记住这一点很重要,这就是为什么我在这里为面临类似问题的任何人提到它。)
不过,我认为这只是一种解决方法,如果您可以避免这种逻辑的需要,最好这样做。

好的,无论如何感谢您的帮助,如果有的话,我很乐意回答您即将提出的问题。

【讨论】:

    猜你喜欢
    • 2023-03-03
    • 1970-01-01
    • 2014-09-12
    • 1970-01-01
    • 1970-01-01
    • 2010-12-04
    • 1970-01-01
    • 2018-04-11
    • 1970-01-01
    相关资源
    最近更新 更多