Проблема регулярных выражений в Java - PullRequest
2 голосов
/ 23 октября 2009

Я пытаюсь создать регулярное выражение для метода replaceAll в Java. Тестовая строка - abXYabcXYZ, а шаблон - abc. Я хочу заменить любой символ, кроме шаблона, на +. Например, строка abXYabcXYZ и шаблон [^(abc)] должны возвращать ++++abc+++, но в моем случае она возвращает ab++abc+++.

public static String plusOut(String str, String pattern) {
    pattern= "[^("+pattern+")]" + "".toLowerCase();
    return str.toLowerCase().replaceAll(pattern, "+");
}
public static void main(String[] args) {
    String text = "abXYabcXYZ";
    String pattern = "abc";
    System.out.println(plusOut(text, pattern));
}

Когда я пытаюсь заменить шаблон на +, проблем не возникает - abXYabcXYZ с шаблоном (abc) возвращает abxy+xyz. Шаблон (^(abc)) возвращает строку без замены.

Есть ли другой способ записать НЕ (регулярное выражение) или группировать символы как слово?

Ответы [ 6 ]

11 голосов
/ 23 октября 2009

То, что вы пытаетесь достичь, довольно сложно с помощью регулярных выражений, поскольку нет способа выразить «заменить строки, не соответствующие шаблону». Вам нужно будет использовать «положительный» шаблон, сообщающий, что сопоставлять, а не что не сопоставлять.

Кроме того, вы хотите заменить каждого символа на заменяющий символ, поэтому вы должны убедиться, что ваш шаблон соответствует ровно одному символу. В противном случае вы замените целые строки одним символом, возвращая более короткую строку.

Для вашего игрушечного примера, вы можете использовать отрицательные взгляды и взгляды, чтобы выполнить задачу, но это может быть более трудным для реальных примеров с более длинными или более сложными строками, так как вам придется рассматривать каждый символ вашей строки отдельно вместе с его контекстом.

Вот шаблон для «not‘ abc ’»:

[^abc]|a(?!bc)|(?<!a)b|b(?!c)|(?<!ab)c

Он состоит из пяти подэлементов, связанных с «или» (|), каждый из которых соответствует ровно одному символу:

  • [^abc] соответствует каждому символу, кроме a, b или c
  • a(?!bc) соответствует a, если за ним не следует bc
  • (?<!a)b соответствует b, если ему не предшествует a
  • b(?!c) соответствует b, если за ним не следует c
  • (?<!ab)c соответствует c, если ему не предшествует ab

Идея состоит в том, чтобы сопоставить каждый символ, который не входит в целевое слово abc, плюс каждый символ слова, который, согласно контексту, не является частью вашего слова. Контекст может быть исследован с использованием негативных просмотров: (?!...) и взглядов (?<!...).

.

Вы можете себе представить, что эта техника не удастся, если у вас есть целевое слово, содержащее один символ более одного раза, например example. Довольно сложно выразить «соответствует e, если за ним не следуют x и , за которыми не следует l».

Особенно для динамических паттернов гораздо проще выполнить положительный поиск, а затем заменить каждый символ, который не совпадает во втором проходе, как другие предложили здесь.

1 голос
/ 23 октября 2009

[^ ...] будет соответствовать один символ, который не является ни одним из ...

Таким образом, ваш шаблон "[^ (abc)]" говорит "соответствует одному символу, который не является a, b, c или левой или правой скобкой"; и это действительно то, что происходит в вашем тесте.

Трудно сказать "заменить все символы, которые не являются частью строки 'abc'" в одном тривиальном регулярном выражении. Вместо того, чтобы добиться желаемого, вы можете сделать что-то противное, например

while the input string still contains "abc"
   find the next occurrence of "abc"
   append to the output a string containing as many "+"s as there are characters before the "abc"
   append "abc" to the output string
   skip, in the input string, to a position just after the "abc" found
append to the output a string containing as many "+"s as there are characters left in the input

или, возможно, если входной алфавит ограничен, вы можете использовать регулярные выражения, чтобы сделать что-то вроде

replace all occurrences of "abc" with a single character that does not occur anywhere in the existing string
replace all other characters with "+"
replace all occurrences of the target character with "abc"

, который будет более читабельным, но может не работать так же

0 голосов
/ 23 октября 2009

Вместо использования replaceAll(...) я бы выбрал подход Pattern/Matcher:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {

    public static String plusOut(String str, String pattern) {
        StringBuilder builder = new StringBuilder();
        String regex = String.format("((?:(?!%s).)++)|%s", pattern, pattern);
        Matcher m = Pattern.compile(regex).matcher(str.toLowerCase());
        while(m.find()) {
            builder.append(m.group(1) == null ? pattern : m.group().replaceAll(".", "+"));
        }
        return builder.toString();
    }

    public static void main(String[] args) {
        String text = "abXYabcXYZ";
        String pattern = "abc";
        System.out.println(plusOut(text, pattern));
    }

}

Обратите внимание, что вам нужно будет использовать Pattern.quote(...), если ваш String pattern содержит метасимволы регулярных выражений.

Редактировать : я не видел Pattern/Matcher подход, уже предложенный toolkit (хотя и немного другой) ...

0 голосов
/ 23 октября 2009

Вместо одной заменыВсе, вы всегда можете попробовать что-то вроде:

   @Test
    public void testString() {
        final String in = "abXYabcXYabcHIH";
        final String expected = "xxxxabcxxabcxxx";
        String result = replaceUnwanted(in);
        assertEquals(expected, result);
    }

    private String replaceUnwanted(final String in) {
        final Pattern p = Pattern.compile("(.*?)(abc)([^a]*)");
        final Matcher m = p.matcher(in);
        final StringBuilder out = new StringBuilder();
        while (m.find()) {
            out.append(m.group(1).replaceAll(".", "x"));
            out.append(m.group(2));
            out.append(m.group(3).replaceAll(".", "x"));
        }
        return out.toString();
    }
0 голосов
/ 23 октября 2009

Попробуйте решить это без регулярных выражений:

String out = "";
int i;
for(i=0; i<text.length() - pattern.length() + 1; ) {
    if (text.substring(i, i + pattern.length()).equals(pattern)) {
        out += pattern;
        i += pattern.length();
    }
    else {
        out += "+";
        i++;
    }
}
for(; i<text.length(); i++) {
    out += "+";
}
0 голосов
/ 23 октября 2009

Отрицательные регулярные выражения обычно неприятны. Я думаю, что вы можете использовать негативную перспективу. Примерно так может работать:

String pattern = "(?<!ab).(?!abc)";

Я не тестировал его, поэтому он может не работать для вырожденных случаев. И производительность тоже может быть ужасной. Вероятно, лучше использовать многошаговый алгоритм.

Редактировать : Нет, я думаю, это не будет работать для каждого случая. Вероятно, вы потратите больше времени на отладку такого регулярного выражения, чем алгоритмически с дополнительным кодом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...