java-recipes

ホーム 文字列 › S-05: 半角カナ↔全角カナ変換

S-05: 半角カナ↔全角カナ相互変換

半角カタカナ(アイウ)と全角カタカナ(アイウ)の相互変換。濁音(ガ→ガ)・半濁音(パ→パ)など 2文字が1文字に結合するケースを正しく処理します。 金融・決済システムの名義人正規化で必須の処理です。

半角カナと全角カナの違い

種別文字例Unicode 範囲主な用途
半角カタカナアイウエオ、ガ(ガ)FF65〜FF9F全銀フォーマット、旧システム入力
全角カタカナアイウエオ、ガ30A0〜30FF現代の一般的な日本語表示

変換の難しさは濁音・半濁音にあります。 半角では「カ」+「゙」の2文字で「ガ」を表すのに対し、全角では「ガ」の1文字です。 この結合処理を正しく実装することが重要です。

サンプルコード

Sample.java
import java.util.HashMap;
import java.util.Map;

public class HalfKanaSample {

    // 半角カナ → 全角カナ の基本変換テーブル(1文字対1文字の変換)
    private static final String HALF_KANA =
        "ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン";
    private static final String FULL_KANA =
        "ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン";

    // 濁音変換テーブル(半角カナ + ゙ → 全角濁音1文字)
    private static final Map<Character, Character> DAKUTEN_MAP = new HashMap<>();
    // 半濁音変換テーブル(半角カナ + ゚ → 全角半濁音1文字)
    private static final Map<Character, Character> HANDAKUTEN_MAP = new HashMap<>();

    static {
        String[] dakutenPairs = {
            "カガ", "キギ", "クグ", "ケゲ", "コゴ",
            "サザ", "シジ", "スズ", "セゼ", "ソゾ",
            "タダ", "チヂ", "ツヅ", "テデ", "トド",
            "ハバ", "ヒビ", "フブ", "ヘベ", "ホボ",
            "ウヴ"
        };
        for (String pair : dakutenPairs) {
            DAKUTEN_MAP.put(pair.charAt(0), pair.charAt(1));
        }
        String[] handakutenPairs = {
            "ハパ", "ヒピ", "フプ", "ヘペ", "ホポ"
        };
        for (String pair : handakutenPairs) {
            HANDAKUTEN_MAP.put(pair.charAt(0), pair.charAt(1));
        }
    }

    /**
     * 半角カナを全角カナに変換する。
     * 濁音(ガ → ガ)・半濁音(パ → パ)の2文字結合も処理する。
     * 変換対象外の文字(英数字・ひらがな・漢字など)はそのまま返す。
     */
    public static String toFullWidth(String input) {
        if (input == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            char next = (i + 1 < input.length()) ? input.charAt(i + 1) : 0;

            // 濁音: 半角カナ + ゙ の2文字 → 全角濁音1文字
            if (next == '゙' && DAKUTEN_MAP.containsKey(c)) {
                sb.append(DAKUTEN_MAP.get(c));
                i++; // ゙ をスキップ
                continue;
            }
            // 半濁音: 半角カナ + ゚ の2文字 → 全角半濁音1文字
            if (next == '゚' && HANDAKUTEN_MAP.containsKey(c)) {
                sb.append(HANDAKUTEN_MAP.get(c));
                i++; // ゚ をスキップ
                continue;
            }
            // 基本変換: 半角カナ1文字 → 全角カナ1文字
            int idx = HALF_KANA.indexOf(c);
            if (idx >= 0) {
                sb.append(FULL_KANA.charAt(idx));
            } else {
                sb.append(c); // 変換対象外はそのまま
            }
        }
        return sb.toString();
    }

    /**
     * 全角カナを半角カナに変換する。
     * 濁音(ガ → ガ)・半濁音(パ → パ)は2文字に展開する。
     */
    public static String toHalfWidth(String input) {
        if (input == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < input.length(); i++) {
            char c = input.charAt(i);
            boolean converted = false;

            // 濁音の逆変換(全角濁音 → 半角カナ + ゙)
            for (Map.Entry<Character, Character> entry : DAKUTEN_MAP.entrySet()) {
                if (entry.getValue() == c) {
                    sb.append(entry.getKey()).append('゙');
                    converted = true;
                    break;
                }
            }
            if (converted) { continue; }
            // 半濁音の逆変換(全角半濁音 → 半角カナ + ゚)
            for (Map.Entry<Character, Character> entry : HANDAKUTEN_MAP.entrySet()) {
                if (entry.getValue() == c) {
                    sb.append(entry.getKey()).append('゚');
                    converted = true;
                    break;
                }
            }
            if (converted) { continue; }
            // 基本変換(全角カナ → 半角カナ)
            int idx = FULL_KANA.indexOf(c);
            if (idx >= 0) {
                sb.append(HALF_KANA.charAt(idx));
            } else {
                sb.append(c); // 変換対象外はそのまま
            }
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        System.out.println("【半角 → 全角】");
        System.out.println(toFullWidth("カナ"));            // カナ
        System.out.println(toFullWidth("ガギグゲゴ"));  // ガギグゲゴ(濁音)
        System.out.println(toFullWidth("パピプペポ"));   // パピプペポ(半濁音)
        System.out.println(toFullWidth("ABC アイウ 123")); // ABC アイウ 123

        System.out.println("\n【全角 → 半角】");
        System.out.println(toHalfWidth("カタカナ"));      // カタカナ
        System.out.println(toHalfWidth("ガギグゲゴ"));    // ガギグゲゴ
        System.out.println(toHalfWidth("パピプペポ"));    // パピプペポ

        System.out.println("\n【往復変換テスト】");
        String original = "ガギグゲゴ";
        String full = toFullWidth(original);
        String back = toHalfWidth(full);
        System.out.println("往復一致: " + original.equals(back));
    }
}

よくあるミス・注意点

⚠️ 濁音・半濁音を1文字と誤認するバグ

半角の「ガ」は2文字(char が2つ)です。 文字列長を使う処理では1文字として扱いたいのに2文字になるため、 全角変換の前に処理するか、変換後の全角文字列で文字数をカウントするよう設計しましょう。

String half = "ガ";          // 2 文字(カ + ゙)
System.out.println(half.length()); // → 2

String full = toFullWidth(half);  // 変換後
System.out.println(full);          // → "ガ"(1 文字)
System.out.println(full.length()); // → 1

📌 ひらがな・漢字・英数字は変換対象外

このサンプルはカタカナのみを変換します。 ひらがな(あいう)・漢字・英数字・記号は変換せずそのまま通過します。 文字列に混在していても安全に処理できます。

📌 金融システムでの活用場面

全銀フォーマット(銀行振込データ)では名義人名を半角カナで指定する仕様になっています。 ユーザーが全角で入力した名義人名を半角カナに変換してから送信データを生成するケースが典型的です。

// 入力: ユーザーが全角で入力した名義人
String input = "ヤマダ タロウ";
// 全銀フォーマット用に半角カナに変換
String zengin = toHalfWidth(input);  // → "ヤマダ タロウ"

テストする観点

  • 濁音(ガ→ガ、ジ→ジなど)が正しく1文字の全角に変換されるか
  • 半濁音(パ→パ、プ→プなど)が正しく1文字の全角に変換されるか
  • 往復変換(半角→全角→半角)で元の文字列と一致するか
  • ひらがな・漢字・英数字が変換されずそのまま通過するか
  • null を渡したとき null が返るか(NullPointerException が発生しないか)
  • 空文字列が変換されると空文字列が返るか
  • 「ヴ」(ヴ)の変換が正しく行われるか

GitHub でソースコードを見る →