Java заменяет символы прописными буквами вокруг (до и после) определенного символа

у меня такой ввод

word w'ord wo'rd

Мне нужно преобразовать в верхний регистр оба символа в начале слова и сразу после символа ' (который может существовать несколько раз).

Мне нужен вывод (используя предыдущий пример):

word W'Ord Wo'Rd

Я пробовал с простым шаблоном

s.replaceAll("(\\w)(\\w*)'(\\w)", "$1");

но я не могу преобразовать группу 1 и 3 в верхний регистр


РЕДАКТИРОВАТЬ: После того, как я обнаружил небольшую ошибку в основном вопросе, я отредактировал код @Wiktor Stribizew, чтобы включить случай, который я пропустил.

Matcher m = Pattern.compile("(\\w)(\\w*)'(\\w)").matcher(s);
StringBuffer result = new StringBuffer();
while (m.find()) {
    m.appendReplacement(result, m.group(1).toUpperCase() + m.group(2) + "'" + m.group(3).toUpperCase());
}
m.appendTail(result);
s = result.toString();

person Deviling Master    schedule 18.04.2017    source источник


Ответы (2)


Вам нужно использовать Matcher#appendReplacement в Java, чтобы иметь возможность обработать совпадение. Вот пример:

String s = "word w'ord wo'rd";
StringBuffer result = new StringBuffer();
Matcher m = Pattern.compile("\\b(\\w)(\\w*)'(\\w(?:'\\w)*)").matcher(s);
while (m.find()) {
    m.appendReplacement(result, 
        m.group(1).toUpperCase()+m.group(2) + "'" + m.group(3).toUpperCase());
}
m.appendTail(result);
System.out.println(result.toString());
// => word W'Ord Wo'Rd

См. демонстрацию Java.

Эквивалент Java 9+ (демонстрация):

String s = "wo'rd w'ord wo'r'd";
Matcher m = Pattern.compile("\\b(\\w)(\\w*)'(\\w(?:'\\w)*)").matcher(s);
System.out.println(
    m.replaceAll(r -> r.group(1).toUpperCase()+r.group(2) + "'" + r.group(3).toUpperCase())
);
//wo'rd w'ord wo'r'd => Wo'Rd W'Ord Wo'R'D
//word w'ord wo'rd => word W'Ord Wo'Rd

Разбивка шаблона:

  • \b - граница ведущего слова
  • (\w) - Группа 1: одно слово char
  • (\w*) - Группа 2: ноль или более символов слова
  • ' - одинарная кавычка
  • (\w(?:'\w)*) - Group 3:
    • \w - a word char
    • (?:'\w)* - zero or more sequences of:
      • ' - a single quote
      • \w - символ слова.

Теперь, если вы хотите сделать шаблон более точным, вы можете изменить \w, которые должны соответствовать строчным буквам, на \p{Ll}, и \w, которые должны соответствовать любой букве, на \p{L}. Шаблон будет выглядеть как "(?U)\\b(\\p{Ll})(\\p{L}*)'(\\p{Ll}(?:'\\p{Ll})*)" - однако вы рискуете оставить буквы в нижнем регистре (после '), если перед строчными стоят прописные (например, wo'r'D's -> Wo'R'D's). (?U) — это встроенный модификатор Pattern.UNICODE_CHARACTER_CLASS, который делает границу слова \b совместимой с Unicode.

person Wiktor Stribiżew    schedule 18.04.2017
comment
Это действительно работает. Запустив этот код и увидев вывод всех строк, которые мне нужно преобразовать, я обнаружил небольшую ошибку в основном вопросе. Теперь я скорректировал основной вопрос, чтобы включить случай, который я пропустил. - person Deviling Master; 18.04.2017
comment
Ошибка для строки: String s = слово слово слово слово слово слово; - person Optional; 18.04.2017
comment
@DevilingMaster: Здесь действует тот же подход: собирайте то, что вам нужно, в группы, а затем управляйте каждой группой, как вам нужно, внутри блока while. - person Wiktor Stribiżew; 18.04.2017
comment
@Необязательно: не проблема, используйте регулярное выражение "\\b(\\w)(\\w*)'(\\w(?:'\\w)*)". - person Wiktor Stribiżew; 18.04.2017
comment
Да, @WiktorStribiżew да, понял - person Optional; 18.04.2017
comment
Хороший вопрос, который можно задать на собеседовании, где они ограничивают использование шаблонов и регулярных выражений :) - person Optional; 18.04.2017

Не так элегантно, как сообщение @Wiktor Stribizew выше, но попытка обойтись без регулярных выражений:

public class HelloWorld{

 public static void main(String []args){
    String s ="word w'ord wo'r'd";
    System.out.println(upperCase(s,'\''));
 }
 private static int x = 1;
 private static String upperCase(String originalString, char delimeter)
 {
     if(originalString.length()==1)
     {
         return originalString;
     }
     int indexOfDelimeter = originalString.indexOf(delimeter);
     StringBuilder result = new StringBuilder();
     if(indexOfDelimeter<0)
     {
         return originalString;
     }
     String newBaseString = originalString.substring(indexOfDelimeter+2);
     if(indexOfDelimeter==0)
     {
         result.append(delimeter).append(Character.toUpperCase(originalString.charAt(indexOfDelimeter+1))).append(newBaseString);
     }
     else
     {
         result.append(originalString.substring(0,indexOfDelimeter-1)).append(Character.toUpperCase(originalString.charAt(indexOfDelimeter-1))).append(delimeter).append(Character.toUpperCase(originalString.charAt(indexOfDelimeter+1)));
     }
     if(indexOfDelimeter<originalString.length())
     {
        result.append(upperCase( newBaseString,delimeter));
     }
     return result.toString();
 }
}
person Optional    schedule 18.04.2017