Java regex: заменить все символы на `+`, кроме экземпляров данной строки - PullRequest
0 голосов
/ 13 сентября 2018

У меня есть следующая проблема, которая гласит

Заменить все символы в строке на символ +, кроме экземпляров данной строки в методе

, поэтому дляНапример, если приведенная строка была abc123efg, и они хотят, чтобы я заменил каждый символ, кроме каждого экземпляра 123, тогда он стал бы +++123+++.

Я подумал, что регулярное выражение, вероятно, лучше для этого иЯ придумал это.

str.replaceAll("[^str]","+") 

, где str - переменная, но она не позволяет мне использовать метод, не помещая его в кавычки.Если я просто хочу заменить переменную строку str, как я могу это сделать?Я запустил его со строкой, набранной вручную, и она работала над методом, но могу ли я просто ввести переменную?

на данный момент я считаю, что она ищет строку "str", а не строку переменной.

Здесь выводится его право для очень многих случаев, кроме двух: (

enter image description here

Список открытых тестовых случаев:

plusOut("12xy34", "xy") → "++xy++"
plusOut("12xy34", "1") → "1+++++"
plusOut("12xy34xyabcxy", "xy") → "++xy++xy+++xy"
plusOut("abXYabcXYZ", "ab") → "ab++ab++++"
plusOut("abXYabcXYZ", "abc") → "++++abc+++"
plusOut("abXYabcXYZ", "XY") → "++XY+++XY+"
plusOut("abXYxyzXYZ", "XYZ") → "+++++++XYZ"
plusOut("--++ab", "++") → "++++++"
plusOut("aaxxxxbb", "xx") → "++xxxx++"
plusOut("123123", "3") → "++3++3"

Ответы [ 7 ]

0 голосов
/ 21 сентября 2018

Чтобы сделать эту работу, вам нужен шаблонный зверь. Допустим, вы работаете над следующим тестовым примером:

plusOut("abXYxyzXYZ", "XYZ") → "+++++++XYZ"

То, что вам нужно сделать, это создать серию предложений в вашем шаблоне, чтобы соответствовать одному символу за раз:

  • Любой символ, который НЕ является «X», «Y» или «Z» - [^XYZ]
  • Любой «X», за которым не следует «YZ» - X(?!YZ)
  • Любой «Y», которому не предшествует «X» - (?<!X)Y
  • Любой «Y», за которым не следует «Z» - Y(?!Z)
  • Любой «Z», которому не предшествует «XY» - (?<!XY)Z

Пример этой замены можно найти здесь: https://regex101.com/r/jK5wU3/4

Вот пример того, как это может работать (скорее всего, не оптимизировано, но работает):

import java.util.regex.Pattern;

public class Test {

    public static void plusOut(String text, String exclude) {

        StringBuilder pattern = new StringBuilder("");
        for (int i=0; i<exclude.length(); i++) {

            Character target    = exclude.charAt(i);
            String prefix       = (i > 0) ? exclude.substring(0, i) : "";
            String postfix      = (i < exclude.length() - 1) ? exclude.substring(i+1) : "";

            // add the look-behind (?<!X)Y
            if (!prefix.isEmpty()) {
                pattern.append("(?<!").append(Pattern.quote(prefix)).append(")")
                        .append(Pattern.quote(target.toString())).append("|");
            }

            // add the look-ahead X(?!YZ)
            if (!postfix.isEmpty()) {
                pattern.append(Pattern.quote(target.toString()))
                        .append("(?!").append(Pattern.quote(postfix)).append(")|");
            }

        }

        // add in the other character exclusion
        pattern.append("[^" + Pattern.quote(exclude) + "]");

        System.out.println(text.replaceAll(pattern.toString(), "+"));

    }

    public static void main(String  [] args) {

        plusOut("12xy34", "xy");
        plusOut("12xy34", "1");
        plusOut("12xy34xyabcxy", "xy");
        plusOut("abXYabcXYZ", "ab");
        plusOut("abXYabcXYZ", "abc");
        plusOut("abXYabcXYZ", "XY");
        plusOut("abXYxyzXYZ", "XYZ");
        plusOut("--++ab", "++");
        plusOut("aaxxxxbb", "xx");
        plusOut("123123", "3");

    }

}

ОБНОВЛЕНИЕ: Даже это не совсем работает, потому что оно не может иметь дело с исключениями, которые являются просто повторяющимися символами, такими как "xx". Регулярные выражения, безусловно, не самый подходящий инструмент для этого, но я подумал, что это возможно. После осмотра я не уверен, что существует шаблон, который мог бы сделать эту работу.

0 голосов
/ 23 сентября 2018

Проблема в вашем решении в том, что вы положили набор строки экземпляра str.replaceAll("[^str]","+"), который исключит любой символ из переменной str и который не решит вашу проблему

EX : при попытке str.replaceAll("[^XYZ]","+") это исключит любую комбинацию символа X, символа Y и символа Z из метода замены, так что вы получите "++XY+++XYZ" ,

На самом деле вы должны исключить последовательность символов вместо str.replaceAll.

Вы можете сделать это, используя захватить группу символов, таких как (XYZ), затем использовать отрицательный прогноз , чтобы сопоставить строку, которая не содержит последовательность символов: ^((?!XYZ).)*$

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

Я нашел два простых решения этой проблемы:

Решение 1 :

Вы можете реализовать метод для замены всех символов на +, за исключением экземпляра данной строки:

String exWord = "XYZ";
String str = "abXYxyzXYZ";

for(int i = 0; i < str.length(); i++){
    // exclude any instance string of exWord from replacing process in str
    if(str.substring(i, str.length()).indexOf(exWord) + i == i){
        i = i + exWord.length()-1;
    }
    else{
        str = str.substring(0,i) + "+" + str.substring(i+1);//replace each character with '+' symbol
    }
}             

Примечание : str.substring(i, str.length()).indexOf(exWord) + i это если оператор исключит любую строку экземпляра exWord из процесса замены в str.

выход

+++++++XYZ

Решение 2 :

Вы можете попробовать этот подход, используя метод ReplaceAll , и он не нуждается ни в каком сложном регулярном выражении:

String exWord = "XYZ";
String str = "abXYxyzXYZ";

str = str.replaceAll(exWord,"*"); // replace instance string with * symbol
str = str.replaceAll("[^*]","+"); // replace all characters with + symbol except * 
str = str.replaceAll("\\*",exWord); // replace * symbol with instance string

Примечание : Это решение будет работать, только если ваша входная строка str не содержит символов *.

Также вы должны экранировать любой символ со специальным значением в регулярном выражении в строке экземпляра фразы exWord, например: exWord = "++".

0 голосов
/ 13 сентября 2018

Абсолютно просто для удовольствия, решение, использующее CharBuffer (неожиданно потребовалось гораздо больше, на что я изначально надеялся):

private static String plusOutCharBuffer(String input, String match) {
    int size = match.length();
    CharBuffer cb = CharBuffer.wrap(input.toCharArray());
    CharBuffer word = CharBuffer.wrap(match);

    int x = 0;
    for (; cb.remaining() > 0;) {
        if (!cb.subSequence(0, size < cb.remaining() ? size : cb.remaining()).equals(word)) {
            cb.put(x, '+');
            cb.clear().position(++x);
        } else {
            cb.clear().position(x = x + size);
        }
    }

    return cb.clear().toString();
}
0 голосов
/ 13 сентября 2018

Похоже, что это проблема plusOut на CodingBat.

У меня было 3 решения этой проблемы, и я написал новое потоковое решение просто для удовольствия.

Решение 1: Цикл иcheck

Создайте StringBuilder из входной строки и проверяйте слово в каждой позиции.Замените символ, если он не совпадает, и пропустите длину слова, если найдено.

public String plusOut(String str, String word) {
  StringBuilder out = new StringBuilder(str);

  for (int i = 0; i < out.length(); ) {
    if (!str.startsWith(word, i))
      out.setCharAt(i++, '+');
    else
      i += word.length();
  }

  return out.toString();
}

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

Решение 2. Замените слово маркером, замените остальное, затем восстановите слово

public String plusOut(String str, String word) {
    return str.replaceAll(java.util.regex.Pattern.quote(word), "@").replaceAll("[^@]", "+").replaceAll("@", word);
}

Не является правильным решением, поскольку предполагается, что определенный символ или последовательность символов не появляются в строке.

Обратите внимание на использование Pattern.quote для предотвращения интерпретации word как синтаксиса регулярных выражений replaceAll method.

Решение 3: регулярное выражение с \G

public String plusOut(String str, String word) {
  word = java.util.regex.Pattern.quote(word);
  return str.replaceAll("\\G((?:" + word + ")*+).", "$1+");
}

Создайте регулярное выражение \G((?:word)*+)., которое более или менее соответствует решению 1:

  • \G гарантирует, что матч начинается с того места, где заканчивается предыдущий матч
  • ((?:word)*+) выбирает 0 или более экземпляров word - если таковые имеются, чтобы мы могли сохранить их в замене$1.Ключевым моментом здесь является собственнический квантификатор *+, который заставляет регулярное выражение сохранять любой экземпляр word, который он найдет.В противном случае, регулярное выражение не будет работать правильно, когда в конце строки появится word, так как обратное выражение для соответствия .
  • . не будет частью какого-либо word, так какпредыдущая часть уже выявляет все последующие появления word и запрещает возврат.Мы заменим это на +

Решение 4: Потоковое

public String plusOut(String str, String word) {
  return String.join(word, 
    Arrays.stream(str.split(java.util.regex.Pattern.quote(word), -1))
      .map((String s) -> s.replaceAll("(?s:.)", "+"))
      .collect(Collectors.toList()));
}

Идея состоит в том, чтобы разделить строку на word, выполнить замену для остальных иобъедините их с помощью word, используя метод String.join.

  • Как и выше, нам нужно Pattern.quote, чтобы избежать split интерпретации word как регулярного выражения.Поскольку split по умолчанию удаляет пустую строку в конце массива, нам нужно использовать -1 во втором параметре, чтобы split оставил эти пустые строки в покое.
  • Затем мы создаем поток измассива и замените остальные как строки +.В Java 11 вместо этого мы можем использовать s -> String.repeat(s.length()).
  • Остальное - просто преобразовать Stream в Iterable (в нашем случае это List) и объединить их для получения результата
0 голосов
/ 13 сентября 2018

Вы можете сделать это в одну строку:

input = input.replaceAll("((?:" + str + ")+)?(?!" + str + ").((?:" + str + ")+)?", "$1+$2");

Опционально захватывает «123» с каждой стороны от каждого символа и возвращает их обратно (пусто, если нет «123»):

0 голосов
/ 13 сентября 2018

Таким образом, вместо создания регулярного выражения, соответствующего отсутствию строки. Мы могли бы просто сопоставить выбранную фразу и добавить + количество пропущенных символов.

StringBuilder sb = new StringBuilder();
Matcher m = Pattern.compile(Pattern.quote(str)).matcher(input);
while (m.find()) {
    for (int i = 0; i < m.start(); i++) sb.append('+');
    sb.append(str);
}
int remaining = input.length() - sb.length();
for (int i = 0; i < remaining; i++) {
    sb.append('+');
}
0 голосов
/ 13 сентября 2018

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

(?<=^|123)(?!123).*?(?=123|$)

https://regex101.com/r/EZWMqM/1/

То есть - посмотрите за началом строки или «123», убедитесь, что за текущей позицией не следует 123, затем лениво повторяйте любой символ, пока заглядывание не совпадет с «123» или концом строки. Это будет соответствовать всем символам, которые не входят в подстроку «123». Затем вам нужно заменить каждый символ на +, после чего вы можете использовать appendReplacement и StringBuffer для создания строки результата:

String inputPhrase = "123";
String inputStr = "abc123efg123123hij";
StringBuffer resultString = new StringBuffer();
Pattern regex = Pattern.compile("(?<=^|" + inputPhrase + ")(?!" + inputPhrase + ").*?(?=" + inputPhrase + "|$)");
Matcher m = regex.matcher(inputStr);
while (m.find()) {
    String replacement = m.group(0).replaceAll(".", "+");
    m.appendReplacement(resultString, replacement);
}
m.appendTail(resultString);
System.out.println(resultString.toString());

Выход:

+++123+++123123+++

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

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