Регулярные выражения - не может соответствовать кириллические символы с \ w - PullRequest
1 голос
/ 03 октября 2019

Задача:

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

Ввод: текст (может состоять из латиницы и кириллицы). Вывод: исходный текст, но регистр первого символа каждого слова, который состоит из трех или более символов, должен быть инвертирован.

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

Пример входных данных

When I was younger
I never needed
Прощай, со всех вокзалов поезда
уходят в Дальние Края

Пример выходных данных

when I Was Younger
I Never Needed
прощай, со Всех Вокзалов Поезда
Уходят в дальние края

Моя попытка:

public static String convert(String input) {
    StringBuilder sb = new StringBuilder(input);
    Pattern p = Pattern.compile("[\\W&&[\\d]]?[\\w&&[\\D]]+");
    Matcher m = p.matcher(input);
    while (m.find()) {
        if (m.group().length() >= 3) {
            if (Character.isUpperCase(sb.charAt(m.start()))) {
                sb.setCharAt(m.start(), Character.toLowerCase(sb.charAt(m.start())));
            } else {
                sb.setCharAt(m.start(), Character.toUpperCase(sb.charAt(m.start())));
            }

        }
    }
    return sb.toString();
}

Мне нужен вывод:

when I Was Younger
I Never Needed
прощай, со Всех Вокзалов Поезда
Уходят в дальние края

, но у меня есть:

when I Was Younger
I Never Needed
Прощай, со всех вокзалов поезда
уходят в Дальние Края

Ответы [ 3 ]

1 голос
/ 03 октября 2019

Устранение неполадок

\w не соответствует символам кириллицы. Я решил это, напечатав соответствующие группы в вашем цикле while:

System.out.println(m.group());

Напечатано:

Когда
I
был
моложе
I
никогда
необходимо

Ни одно из других слов не найдено.

Решение 1

Для сопоставления символов кириллицы вы также можете использовать \p{L}. Если вы используете {3} для соответствия трем символам, вы можете избежать проверки длины в цикле. \b соответствует граничному символу. Все вместе:

public static String convert(String input) {
    StringBuilder sb = new StringBuilder(input);
    Pattern p = Pattern.compile("\\b\\p{L}{3}");
    Matcher m = p.matcher(input);
    while (m.find()) {
        char firstChar = sb.charAt(m.start());
        if (Character.isUpperCase(firstChar)) {
            sb.setCharAt(m.start(), Character.toLowerCase(firstChar));
        } else {
            sb.setCharAt(m.start(), Character.toUpperCase(firstChar));
        }
    }
    return sb.toString();
}

Производит:

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

Решение 2

Или, если вы хотите быть действительно swish, используйте позитивный взгляд (группа без захвата) и совпадение replaceAllметод, который принимает лямбду:

public static String convert(String input) {
    Pattern p = Pattern.compile("\\b(\\p{L})(?=\\p{L}{2})");
    Matcher m = p.matcher(input);
    return m.replaceAll(match -> {
        char ch = match.group().charAt(0);
        if (Character.isUpperCase(ch)) {
            return "" + Character.toLowerCase(ch);
        }
        return "" + Character.toUpperCase(ch);
    });
}

Также выдает:

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

0 голосов
/ 03 октября 2019

Основная проблема в том, что по умолчанию \w принимает только английский алфавит. Чтобы он соответствовал буквенным символам других языков, вам нужно добавить Pattern.UNICODE_CHARACTER_CLASS flag:

Pattern p = Pattern.compile("[\\W&&[\\d]]?[\\w&&[\\D]]+", Pattern.UNICODE_CHARACTER_CLASS);
//                                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Также ваш шаблон не выглядит правильным (или он просто не читается, так как оннепонятно, какая основная идея стоит за этим). Поскольку в этом примере вы хотите изменить только слова, содержащие не менее 3 символов, вы можете описать их как

Pattern p = Pattern.compile("(\\p{Alpha})(\\p{Alpha}{2,})", Pattern.UNICODE_CHARACTER_CLASS);

, где

  • в группе 1 мы сохраним первый символ
  • в группе 2 мы будем хранить остальные символы.

Таким образом, ваш код может выглядеть следующим образом:

public static String convert(String input) {
    Pattern p = Pattern.compile("(\\p{Alpha})(\\p{Alpha}{2,})", Pattern.UNICODE_CHARACTER_CLASS);
    Matcher m = p.matcher(input);
    return m.replaceAll(match -> {
        char firstChar = match.group(1).charAt(0);
        if (Character.isUpperCase(firstChar)) {
            return Character.toLowerCase(firstChar) + match.group(2);
        } else {
            return Character.toUpperCase(firstChar) + match.group(2);
        }
    });
}
0 голосов
/ 03 октября 2019
if (Character.isUpperCase(sb.charAt(m.start()))) {
    sb.setCharAt(m.start(), Character.toLowerCase(sb.charAt(m.start())));
} else {
    sb.setCharAt(m.start(), Character.toUpperCase(sb.charAt(m.start())));
}

Вам нужно отладить этот кусок кода. Один из способов сделать это - разбить сложность и уменьшить количество повторений, назначив промежуточные результаты их собственным переменным:

char firstChar = sb.charAt(m.start());
if (Character.isUpperCase(firstChar)) {
    char lowerChar = Character.toLowerCase(firstChar);
    sb.setCharAt(m.start(), lowerChar);
} else {
    char upperChar = Character.toUpperCase(firstChar);
    sb.setCharAt(m.start(), upperChar);
}

Теперь вы можете распечатать значения этих переменных для их проверки.

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