Java Regex для регулярного выражения, чтобы удалить двойные дефисы и сохранить одиночные

Поэтому мне нужно регулярное выражение, которое удаляет все несловарные символы (кроме пробелов и дефисов) из текста, чтобы я мог посчитать количество слов в тексте. Что-то вроде

String().replaceAll("[^\\p{L}+(?:\\-\n?\\p{L}+)* ]", "")

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

Вот пример. Следующее предложение:

Пока я был вне кампуса, я потерял все свои вещи - кошелек, ключи, все!

Должно выглядеть так после регулярного выражения:

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

РЕДАКТИРОВАТЬ - я не заинтересован в использовании двух replaceAll, я хочу сделать это в одном регулярном выражении.

1 ответ

Решение

Ваше оригинальное регулярное выражение нарушено, так как (?:...) а также *, + обрабатываются как буквальный символ внутри класса символов [],

В классе символов в Java, [, ], \ (для escape-последовательностей и свойств), \Q, \E (для цитирования специальных символов в классе символов), ^ (действует только в начале урока), - (действует от 2 символов), && (для пересечения классов символов) являются единственными символами / последовательностями со специальным значением.

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

 Это еще один пример: (скобки) + [скобки] + (звездочки *) 

Вместо удаления недопустимых символов и материалов, вы можете просто сопоставить слова непосредственно с этим регулярным выражением (частью внутри класса символов вашего регулярного выражения):

"\\p{L}+(?:-\n?\\p{L}+)*"

Приведенное выше регулярное выражение можно использовать в цикле Matcher для подсчета количества слов:

Pattern p = Pattern.compile("\\p{L}+(?:-\n?\\p{L}+)*");
Matcher m = p.matcher(input);
int count = 0;

while (m.find()) {
   count++;
}

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

input.replaceAll("(?s).*?(\\p{L}+(?:-\n?\\p{L}+)*)|.+", "$1 ");

(?s).*?(\\p{L}+(?:-\n?\\p{L}+)*) выполняет поиск не-слова-символов перед словом и удаляет их, а после слова добавляется пробел. .+ в конце имеет дело с последовательностью не-словесных символов в конце строки.

Обратите внимание, что он создаст завершающий пробел, если последний символ не является частью слова.

Демо на regex101

Тестовый ввод:

While I was off-campus, I lost all my belongings--wallet, keys, everything!

This is another example: (parentheses) + [brackets] + (asterisks *)

Along-
longl-
onglo-
ngword!

Тестовый вывод (обратите внимание на завершающий пробел в конце):

While I was off-campus I lost all my belongings wallet keys everything This is another example parentheses brackets asterisks Along- longl- onglo- ngword 

Если вы спросите, могу ли я исключить завершающее пространство, изменив приведенное выше регулярное выражение, я бы сказал, что это невозможно в Java. Проблема здесь в том, что -- должен быть заменен пробелом, чтобы отделить 2 "слова", в то время как другие не "слова" символы должны быть заменены пустой строкой.

Другие вопросы по тегам