Java Tips | 文字列処理:全角→半角

Java Java
スポンサーリンク

全角→半角は「バラバラな入力を“同じもの”として扱う」ための技

業務システムで一番よくある地味トラブルの一つが、これです。

  • 「1234」と「1234」が別物として扱われてしまう
  • 「ABC」と「ABC」で検索結果が変わる
  • ユーザーがフォームに全角スペースを入れて、意図しない空白になる

人間の目には「同じ」に見えるのに、プログラム的には違う文字。
これを揃えるための基本テクニックが 全角→半角変換 です。

ここをユーティリティとしてきちんと用意しておくと、検索・比較・キー生成・ログ出力などが一気に安定します。


まずは「何を全角→半角にしたいか」を決める

全角と半角の代表例をざっくり整理する

よく対象になるのは、だいたいこのあたりです。

  • 数字:0123…90123…9
  • 英字:ABC…Zabc…zA…Z a…z
  • 記号:!#$%&()=~…!#$%&()=~...
  • スペース:全角スペース → 半角スペース

カタカナ(全角↔半角)は別テーマとして扱うことが多いので、
ここでは「数字・英字・記号・スペース」を中心にします。

重要なのは、「どこまでを変換対象にするかをプロジェクトとして決める」ことです。
「数字と英字だけ」「スペースも含める」「記号はそのまま」など、用途によって変わります。


シンプルな実装:自前マッピングで全角→半角

「全角のコード範囲」を知ってしまえば、変換は実は単純

全角の英数字は、Unicode 上で連続した範囲に並んでいます。
例えば、全角の は、半角の 09 に一定のオフセットを足した位置にあります。

これを利用すると、こんな感じのユーティリティが書けます。

public final class ZenkakuConverter {

    private ZenkakuConverter() {}

    public static String toHankakuAscii(String text) {
        if (text == null || text.isEmpty()) {
            return text;
        }
        StringBuilder sb = new StringBuilder(text.length());
        for (int i = 0; i < text.length(); i++) {
            char ch = text.charAt(i);
            // 全角英数字・記号(!〜~)の範囲
            if (ch >= '!' && ch <= '~') {
                // Unicode 上で 0xFEE0 だけずらすと半角になる
                ch = (char) (ch - 0xFEE0);
            } else if (ch == ' ') {
                // 全角スペースは個別対応
                ch = ' ';
            }
            sb.append(ch);
        }
        return sb.toString();
    }
}
Java

使い方はこうです。

String s = "Hello! 1234ABCabc";
String converted = ZenkakuConverter.toHankakuAscii(s);
System.out.println(converted);
// Hello! 1234ABCabc
Java

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

一つ目は、「全角英数字・記号は、Unicode 上で“0xFEE0 ずらす”だけで半角になる」という性質を使っていることです。
'!''~' の範囲に入っている文字だけを対象にして、- 0xFEE0 してあげると、対応する半角に変わります。

二つ目は、「全角スペースだけは範囲外なので、個別に ' ' → ' ' と変換している」ことです。
全角スペースは見落とされがちですが、検索やトリム処理でかなりの地雷になるので、
全角→半角ユーティリティでは真っ先に対応しておくべき存在です。


例題:検索用に「英数字だけ半角に揃える」

入力値を正規化してから検索する

例えば、ユーザーが「商品コード」を検索する画面を考えます。

  • DB には ABC123 で保存されている
  • ユーザーは「ABC123」「ABC123」「ABC123」など、好きなように入力してくる

このとき、「検索前に英数字を半角に揃える」だけで、ヒット率が一気に上がります。

public final class SearchNormalizer {

    private SearchNormalizer() {}

    public static String normalizeCodeInput(String input) {
        if (input == null) {
            return null;
        }
        // 英数字・記号・スペースを半角に
        String hankaku = ZenkakuConverter.toHankakuAscii(input);
        // 前後の空白を削る
        return hankaku.trim();
    }
}
Java

使い方はこうです。

String raw = " ABC123 ";
String normalized = SearchNormalizer.normalizeCodeInput(raw);

System.out.println("\"" + normalized + "\""); // "ABC123"
Java

ここでのポイントは、「“検索用の正規化”という文脈をユーティリティ名に刻んでいる」ことです。
normalizeCodeInput と名前をつけることで、「これは検索用コード入力のための正規化なんだな」と一目で分かります。

全角→半角はあくまで“正規化の一部”なので、

  • トリム(前後の空白削除)
  • 大文字・小文字の統一
  • 不要な記号の除去

などと組み合わせて、「このシステムではコードをこう扱う」というルールを一箇所に閉じ込めておくのが実務的です。


Java 標準の Normalizer を使うという選択肢

Unicode 正規化(NFKC)で「似た文字」をまとめる

もう少し広く「似た文字を同一視したい」ときに使えるのが、java.text.Normalizer です。

import java.text.Normalizer;

public final class UnicodeNormalizer {

    private UnicodeNormalizer() {}

    public static String toNfkc(String text) {
        if (text == null) {
            return null;
        }
        return Normalizer.normalize(text, Normalizer.Form.NFKC);
    }
}
Java

これを使うと、全角英数字だけでなく、いろいろな「互換文字」が標準的な形に寄せられます。

String s = "Hello! ① Ⅰ ㍻";
System.out.println(UnicodeNormalizer.toNfkc(s));
// Hello! 1 I 平成
Java

ここで深掘りしたいのは、「Normalizer.NFKC は“全角→半角”だけではなく、“互換文字を標準形に寄せる”かなり強力な変換」だということです。

  • 全角英数字 → 半角
  • 丸付き数字 → 通常の数字
  • ローマ数字 → 通常のアルファベット
  • 一部の合成文字 → 分解された形

など、かなり広範囲に影響します。

だからこそ、

  • 「英数字だけ半角にしたい」なら自前マッピング(さっきの ZenkakuConverter
  • 「とにかく“見た目が同じもの”を同一視したい」なら NFKC

というように、用途に応じて使い分けるのが大事です。


例題:ログ出力時に「英数字だけ半角にしておく」

ログを後から grep しやすくするための一手間

ログにユーザー入力をそのまま出すと、全角英数字が混ざっていて grep しづらい、ということがよくあります。
そこで、「ログに出す前に英数字だけ半角に揃える」というユーティリティを挟んでおくと便利です。

public final class LogSafeStrings {

    private LogSafeStrings() {}

    public static String normalizeForLog(String text) {
        if (text == null) {
            return null;
        }
        // 英数字・記号・スペースを半角に
        String hankaku = ZenkakuConverter.toHankakuAscii(text);
        // 制御文字などを消す処理を足してもよい
        return hankaku;
    }
}
Java

使い方はこうです。

String userInput = "ユーザー名: TestUser1";
System.out.println("input=" + LogSafeStrings.normalizeForLog(userInput));
// input=ユーザー名: TestUser1
Java

ここでのポイントは、「ログ用の正規化ルールを一箇所にまとめている」ことです。
あとから「タブをスペースにしたい」「制御文字を消したい」といった要件が出ても、
LogSafeStrings だけ直せば、ログ出力全体の挙動を変えられます。


まとめ:全角→半角ユーティリティで身につけたい感覚

全角→半角は、「人間には同じに見えるけれど、プログラム的には違う文字」を揃えるための、超・基礎テクニックです。

押さえておきたい感覚は、まず「全角英数字・記号は '!'〜'~' の範囲を 0xFEE0 ずらすだけで半角になる」という事実。
次に、「全角スペースは個別に ' ' → ' ' と変換しないと、検索やトリムで地雷になる」ということ。
そして、「検索用・ログ用など、文脈ごとに“どこまで正規化するか”を決め、そのルールをユーティリティクラスに閉じ込めてプロジェクト全体で共有する」ことです。

もしあなたのコードのどこかに、replace(' ', ' ') や「全角と半角が混ざった比較」が散らばっているなら、
それを題材にして、ここで作った ZenkakuConverterSearchNormalizerLogSafeStrings に置き換えてみてください。
それだけで、「検索しやすくて、比較しやすくて、ログも読みやすい文字列処理」に、一段レベルアップできます。

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