1. 概要

文字列には通常、単語と他の区切り文字が混在しています。 場合によっては、これらの文字列は、空白のない大文字と小文字の変更によって単語を区切ることがあります。 たとえば、キャメルケースは最初のの後の各単語を大文字にし、タイトルケース(またはパスカルケース)はすべての単語を大文字にします。

これらの文字列を処理するために、これらの文字列を解析して単語に戻したい場合があります。

この短いチュートリアルでは、正規表現を使用して大文字と小文字が混在する文字列内の単語を検索する方法と、それらを文またはタイトルに変換する方法について説明します。

2. 大文字の文字列を解析するためのユースケース

キャメルケース文字列を処理するための一般的な使用例は、ドキュメント内のフィールド名です。 ドキュメントに「firstName」– フィールドがあるとします。これを、画面上に「Firstname」または「FirstName」として表示したい場合があります。

同様に、リフレクションを介してアプリケーションのタイプまたは関数をスキャンする場合、それらの名前を使用してレポートを作成するために、変換したいキャメルケースまたはタイトルケースの識別子を見つけるのが一般的です。

これらの式を解析するときに解決する必要がある追加の問題は、1文字の単語が連続した大文字を引き起こすことです。

明確にするために:

  • thisIsAnExampleOfCamelCase
  • ThisIsTitleCase
  • thisHasASingleLetterWord

解析する必要のある識別子の種類がわかったので、正規表現を使用して単語を見つけましょう。

3. 正規表現を使用して単語を検索する

3.1. 単語を検索するための正規表現の定義

通常式を定義して、小文字のみ、単一の大文字とそれに続く小文字、または単一の大文字のみで構成される単語を検索してみましょう。

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

この式は、正規表現エンジンに2つのオプションを提供します。 最初は「[AZ]?」を使用して「オプションの最初の大文字」を意味し、次に「[az]+」を使用して「1つ以上の小文字」を意味します。 その後、「|」があります 文字はまたはロジックを提供し、その後に「[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;
}

これは、正規表現のPatternによって作成されたMatcherを使用して、単語を見つけるのに役立ちます。 マッチがまだある間にマッチャーを反復処理し、それらをリストに追加します。

これにより、単語の定義を満たすものがすべて抽出されます。 テストしてみましょう。

3.3. WordFinderのテスト

私たちのワードファインダーは、単語以外の文字や大文字と小文字の変更で区切られた単語を見つけることができるはずです。 簡単な例から始めましょう:

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

このテストは合格し、アルゴリズムが機能していることを示しています。 次に、キャメルケースを試してみましょう。

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

ここでは、単語がキャメルケースの文字列から抽出され、大文字と小文字が変更されていないことがわかります。 たとえば、「Is」は元のテキストの大文字で始まり、抽出時に大文字になります。

タイトルケースでこれを試すこともできます:

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

さらに、1文字の単語が意図したとおりに抽出されていることを確認できます。

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

これまでのところ、単語抽出機能を構築しましたが、これらの単語は、出力に理想的ではない可能性のある方法で大文字になっています。

4. 単語リストを人間が読める形式に変換する

単語のリストを抽出した後、toUpperCaseやtoLowerCaseなどのメソッドを使用してそれらを正規化することをお勧めします。 次に、 String.join を使用して、区切り文字を使用してそれらを1つの文字列に結合し直すことができます。 これらを使用して実際のユースケースを実現するためのいくつかの方法を見てみましょう。

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. 結論

この短い記事では、正規表現を使用してString内の単語を検索する方法について説明しました。 この正規表現を定義して、単語の境界として大文字を使用してさまざまな単語を見つける方法を見てきました。

また、単語のリストを取得し、それらを文またはタイトルの正しい大文字に変換するためのいくつかの簡単なアルゴリズムについても調べました。

いつものように、サンプルコードはGitHubにあります。