Перейти к основному содержимому

Преобразование Camel Case и Title Case в слова в Java

· 5 мин. чтения

1. Обзор

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

Мы можем захотеть разобрать эти строки обратно на слова, чтобы обработать их.

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

2. Варианты использования для разбора строк с заглавными буквами

Распространенным вариантом использования для обработки строк верблюжьего регистра могут быть имена полей в документе. Допустим, в документе есть поле «firstName» — мы можем захотеть отобразить его на экране как «First name» или «First Name» .

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

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

Для ясности:

  • thisIsAnExampleOfCamelCase
  • ThisIsTitleCase
  • thisHasASingleLetterWord

Теперь, когда мы знаем типы идентификаторов, которые нам нужно анализировать, давайте воспользуемся регулярным выражением для поиска слов.

3. Найдите слова, используя регулярные выражения

3.1. Определение регулярного выражения для поиска слов

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

Pattern WORD_FINDER = Pattern.compile("(([A-Z]?[a-z]+)|([A-Z]))");

Это выражение предоставляет обработчику регулярных выражений две опции. Первый использует «[AZ]?» означает «необязательная первая заглавная буква», а затем «[az]+» означает «одну или несколько строчных букв». После этого есть «|» символ для обеспечения или логики, за которым следует выражение «[AZ]» , что означает «одна заглавная буква».

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

3.2. Поиск слов в строке

Мы определим метод для использования этого регулярного выражения:

public List<String> findWordsInMixedCase(String text) {
Matcher matcher = WORD_FINDER.matcher(text);
List<String> words = new ArrayList<>();
while (matcher.find()) {
words.add(matcher.group(0));
}
return words;
}

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

Это должно извлечь все, что соответствует нашему определению слова. Давайте проверим это.

3.3. Тестирование поисковика слов

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

assertThat(findWordsInMixedCase("some words"))
.containsExactly("some", "words");

Этот тест проходит и показывает нам, что наш алгоритм работает. Далее мы попробуем случай с верблюдом:

assertThat(findWordsInMixedCase("thisIsCamelCaseText"))
.containsExactly("this", "Is", "Camel", "Case", "Text");

Здесь мы видим, что слова извлекаются из верблюжьего регистра String и выходят с неизменной заглавной буквой. Например, «Is» начинается с заглавной буквы в исходном тексте и при извлечении становится заглавной.

Мы также можем попробовать это с заголовком:

assertThat(findWordsInMixedCase("ThisIsTitleCaseText"))
.containsExactly("This", "Is", "Title", "Case", "Text");

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

assertThat(findWordsInMixedCase("thisHasASingleLetterWord"))
.containsExactly("this", "Has", "A", "Single", "Letter", "Word");

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

4. Преобразование списка слов в удобочитаемый формат

После извлечения списка слов мы, вероятно, захотим использовать такие методы, как toUpperCase или toLowerCase , для их нормализации. Затем мы можем использовать String.join , чтобы снова объединить их в одну строку с разделителем. Давайте рассмотрим несколько способов достижения реальных вариантов использования с ними.

4.1. Преобразовать в предложение

Предложения начинаются с заглавной буквы и заканчиваются точкой«.» . Нам нужно, чтобы слово начиналось с заглавной буквы:

private String capitalizeFirst(String word) {
return word.substring(0, 1).toUpperCase()
+ word.substring(1).toLowerCase();
}

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

public String sentenceCase(List<String> words) {
List<String> capitalized = new ArrayList<>();
for (int i = 0; i < words.size(); i++) {
String currentWord = words.get(i);
if (i == 0) {
capitalized.add(capitalizeFirst(currentWord));
} else {
capitalized.add(currentWord.toLowerCase());
}
}
return String.join(" ", capitalized) + ".";
}

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

Давайте проверим это:

assertThat(sentenceCase(Arrays.asList("these", "Words", "Form", "A", "Sentence")))
.isEqualTo("These words form a sentence.");

4.2. Преобразовать в заглавный регистр

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

Мы можем добиться этого, определив наши стоп-слова:

Set<String> STOP_WORDS = Stream.of("a", "an", "the", "and", 
"but", "for", "at", "by", "to", "or")
.collect(Collectors.toSet());

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

if (i == 0 || 
!STOP_WORDS.contains(currentWord.toLowerCase())) {
capitalized.add(capitalizeFirst(currentWord));
}

Алгоритм объединения слов тот же, но мы не добавляем точку в конце.

Давайте проверим это:

assertThat(capitalizeMyTitle(Arrays.asList("title", "words", "capitalize")))
.isEqualTo("Title Words Capitalize");

assertThat(capitalizeMyTitle(Arrays.asList("a", "stop", "word", "first")))
.isEqualTo("A Stop Word First");

5. Вывод

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

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

Как всегда, пример кода можно найти на GitHub .