Группа просмотра назад Java RegEx API не имеет очевидной максимальной длины рядом с индексом

Я нахожусь на некотором синтаксическом анализе предложения SQL, и разработал рабочее регулярное выражение для поиска столбца вне строковых литералов с помощью «Rad Software Regular Expression Desginer», который использует .NET API. Чтобы убедиться, что разработанный 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, похоже, не поддерживает неограниченное количество просмотров, но мне все равно не удалось заставить его работать.
Разве это не правильно, что взгляд -behind всегда ограничивает себя от смещения поиска до текущей позиции поиска? Значит, это приведет к чему-то вроде «позиция - смещение»?


person Foo Inc    schedule 27.04.2010    source источник
comment
Это очень похоже на вопрос, как разбирать HTML с помощью регулярного выражения ... Зачем вам это нужно? Проверить SQL?   -  person BalusC    schedule 27.04.2010
comment
Я хочу использовать его для String.replaceAll (String, String), чтобы заменить эти столбцы на Alias.ColumnName, потому что они идут в простом виде. Конечно, я бы предпочел исправить входящую строку, чтобы устранить основную причину необходимости этого исправления, но, к сожалению, я недосягаем.   -  person Foo Inc    schedule 27.04.2010


Ответы (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». При замене важно обеспечить полные имена столбцов. Итак, если бы мы заменили столбец "Column_1" в предложении where

WHERE Column_1 = Column_2 AND Column_11 = Column_22

"Столбец_11" оставить нетронутым. (Я думаю, это важно иметь в виду, поэтому я упоминаю об этом здесь для всех, кто сталкивается с подобной проблемой.)
Тем не менее, я думаю, что это всего лишь обходной путь, и если вы можете избежать необходимости в этой логике, лучше так и поступить.

Хорошо, в любом случае спасибо за помощь, и я буду рад ответить вам на следующие вопросы, если таковые будут.

person Foo Inc    schedule 28.04.2010