Java Tips | 文字列処理:単語分割

Java Java
スポンサーリンク

単語分割は「検索・集計・ハイライト」の入口になる処理

単語分割は、1本の文章を「単語ごと」にバラす処理です。
英語なら "I love Java programming"
["I", "love", "Java", "programming"] のように分けるイメージです。

業務だと、検索キーワードの分解、ログの解析、タグの入力補助、
「スペース区切りで複数指定できます」といった画面の裏側など、
地味に出番が多い処理です。

ここを丁寧に押さえておくと、「文字列を“意味のあるかたまり”として扱う感覚」が身につきます。


いちばん素直なやり方:split(" ") でスペース区切り

まずは「半角スペース1個で区切る」から

一番シンプルな単語分割は、String#split(" ") を使う方法です。

public final class WordSplitter {

    private WordSplitter() {}

    public static String[] splitBySpace(String text) {
        if (text == null || text.isEmpty()) {
            return new String[0];
        }
        return text.split(" ");
    }
}
Java

使い方はこうです。

String text = "I love Java programming";
String[] words = WordSplitter.splitBySpace(text);

for (String w : words) {
    System.out.println("[" + w + "]");
}
Java

出力イメージはこうなります。

[I]
[love]
[Java]
[programming]

ここでまず押さえておきたいのは、「null や空文字のときに空配列を返す」という方針です。
これを決めておくと、呼び出し側で null チェックを毎回書かなくて済みます。

ただし、この実装にはすぐにぶつかる弱点があります。

String text = "I  love   Java"; // スペースが連続している
String[] words = WordSplitter.splitBySpace(text);

System.out.println(words.length); // 4 ではなく 5 や 6 になる
Java

" " で分割すると、「連続したスペースの間に空文字が挟まる」問題が出ます。
ここをどうするかが、次のステップです。


実務で使うなら:正規表現で「連続する空白」を1つの区切りにする

split("\s+") で「空白1個以上」をまとめて扱う

「スペースが1個とは限らない」「タブも混ざるかも」という現実を考えると、
「空白文字が1個以上続いたところを区切りとする」のが実務的です。

そのときに使うのが、正規表現 \\s+ です。
\s は「空白文字(スペース、タブなど)」、+ は「1回以上の繰り返し」を意味します。

public final class WordSplitter {

    private WordSplitter() {}

    public static String[] splitByWhitespace(String text) {
        if (text == null || text.isBlank()) {
            return new String[0];
        }
        return text.trim().split("\\s+");
    }
}
Java

使い方はこうです。

String text = "  I   love\tJava   programming  ";
String[] words = WordSplitter.splitByWhitespace(text);

for (String w : words) {
    System.out.println("[" + w + "]");
}
Java

出力イメージはこうなります。

[I]
[love]
[Java]
[programming]

ここで深掘りしたいポイントは三つです。

1つ目:trim() で前後の空白を落としていること。
これをしないと、先頭や末尾の空白が原因で、
先頭や末尾に空文字の要素ができてしまいます。

2つ目:isBlank() で「空白だけの文字列」を空扱いにしていること。
isEmpty() だと長さ0のときだけ true ですが、
" " のような「空白だけ」の文字列は false になります。
isBlank() は「空白だけ」も true になるので、
「単語は1つもない」と判断して空配列を返せます。

3つ目:"\\s+" で「連続する空白を1つの区切り」として扱っていること。
スペースが1個でも3個でも、タブでも、
「とにかく空白が続いたところで区切る」という挙動になります。
これが、実務での「単語分割」の基本形だと思ってください。


配列より List<String> で返す形にしておく

後続処理で扱いやすくするための小さな工夫

業務コードでは、String[] より List<String> の方が扱いやすいことが多いです。
ストリームAPIを使ったり、forEach したり、後から追加・削除したりしやすくなります。

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public final class WordSplitter {

    private WordSplitter() {}

    public static List<String> splitToList(String text) {
        if (text == null || text.isBlank()) {
            return Collections.emptyList();
        }
        String[] array = text.trim().split("\\s+");
        return Arrays.asList(array);
    }
}
Java

使い方はこうです。

String text = "I   love Java programming";

for (String w : WordSplitter.splitToList(text)) {
    System.out.println("[" + w + "]");
}
Java

ここでのポイントは、「空のときは空リストを返す」という方針です。
これにしておくと、呼び出し側は

for (String w : WordSplitter.splitToList(text)) {
    ...
}
Java

と書くだけでよく、nullチェックが不要になります。


記号や句読点をどう扱うかを決める

「単語+記号」か、「記号を除いた単語」か

英語の文章を単語分割するとき、
"Java,""programming." のように、単語の後ろに記号がくっついていることがあります。

split("\\s+") だけだと、これらはそのまま1単語として扱われます。

String text = "I love Java, programming.";
String[] words = WordSplitter.splitByWhitespace(text);

for (String w : words) {
    System.out.println("[" + w + "]");
}
Java

出力イメージ:

[I]
[love]
[Java,]
[programming.]

これで問題ない場面も多いですが、
「カンマやピリオドは除いて、純粋な単語だけ欲しい」という要件もあります。

その場合は、分割後に「前後の記号を削る」処理を挟みます。

public static List<String> splitAndStripPunctuation(String text) {
    if (text == null || text.isBlank()) {
        return Collections.emptyList();
    }
    String[] raw = text.trim().split("\\s+");
    List<String> result = new java.util.ArrayList<>();
    for (String w : raw) {
        String cleaned = w.replaceAll("^[\\p{Punct}]+|[\\p{Punct}]+$", "");
        if (!cleaned.isEmpty()) {
            result.add(cleaned);
        }
    }
    return result;
}
Java

使い方はこうです。

String text = "I love Java, programming.";
List<String> words = WordSplitter.splitAndStripPunctuation(text);

for (String w : words) {
    System.out.println("[" + w + "]");
}
Java

出力イメージ:

[I]
[love]
[Java]
[programming]

ここでの重要ポイントは、「分割」と「クリーニング(正規化)」を分けて考えることです。
単語分割そのものは空白で区切るだけ、
そのあとで「前後の記号を落とす」「大文字小文字を揃える」などの前処理を重ねていきます。


日本語の「単語分割」は別物だと割り切る

ひらがな・漢字・カタカナは「スペースで区切られない」

ここまでの話は、英語や「スペースで単語が区切られる言語」を前提にしています。

日本語は、基本的に単語の間にスペースが入りません。
"私はJavaが好きです" を「私 / は / Java / が / 好き / です」のように分けるには、
形態素解析(MeCab など)といった専用の仕組みが必要になります。

業務で「日本語の単語分割」を本気でやる場合は、
外部ライブラリや検索エンジン(Elasticsearch など)の世界になります。

ここで押さえておきたいのは、「スペースで区切る単語分割」と「日本語の単語分割」は別物 だということです。
今回のユーティリティは、主に英数字やスペース区切りの入力(検索キーワード、タグ、IDのリストなど)を対象にしている、と理解しておくとよいです。


実務での単語分割の使いどころ

例1:検索キーワードをスペース区切りで受け取る

「スペース区切りで複数キーワードを指定できます」という検索画面を考えます。

サーバ側では、こんな感じで使えます。

String keywordInput = request.getParameter("q");
List<String> keywords = WordSplitter.splitToList(keywordInput);

for (String kw : keywords) {
    // 各キーワードを AND 条件で検索に使う、など
    System.out.println("検索キーワード: " + kw);
}
Java

ここでのポイントは、「入力が空でも null でも、安全にループできる」ことです。
ユーティリティ側で空リストを返すようにしているので、
呼び出し側のコードがシンプルになります。

例2:タグ入力をスペース区切りで受け取る

「タグをスペース区切りで入力してください」というUIもよくあります。

String tagInput = "java spring backend";
List<String> tags = WordSplitter.splitToList(tagInput);

// DBにタグを保存したり、重複チェックしたり
tags.forEach(tag -> System.out.println("タグ: " + tag));
Java

ここでも、「連続スペース」「前後の空白」を気にせず、
素直に splitToList を呼ぶだけで済むのがユーティリティの価値です。


まとめ:単語分割ユーティリティで身につけたい感覚

単語分割は、「1本の文字列を“意味のあるかたまり”に分解する」処理です。

実務目線で押さえておきたいのは、

改行ではなく「空白」で区切る
split(" ") ではなく trim().split("\\s+") を使う
null や空文字のときは空配列/空リストを返す
必要に応じて、分割後に記号や大文字小文字を正規化する

という感覚です。

もしあなたのコードのどこかに、

String[] words = text.split(" ");
Java

のような行がそのまま書かれていたら、
それを題材にして、ここで作った WordSplitter.splitByWhitespacesplitToList に置き換えてみてください。

その小さな改善が、
「入力の揺れに強くて、後続処理を書きやすい文字列ユーティリティ」を扱えるエンジニアへの、確かな一歩になります。

タイトルとURLをコピーしました