概要

銀行間の振込データを扱う全銀フォーマットでは、使用できる文字が JIS X 0201 片仮名用図形文字集合に基づく厳格な範囲に制限されています。許容されるのは半角カナ(ア-ン、濁点゙、半濁点゚)、半角英大文字(A-Z)、半角数字(0-9)、および一部の半角記号(スペース、括弧、ハイフン、ピリオド、スラッシュなど)のみです。この制限を満たさない文字を含む振込データを送信すると、銀行システムが受け付けずに振込が失敗します。実務では、画面入力やCSV取込で全角カナや全角英数字が混入するケースが日常的に発生するため、全角→半角の変換処理と禁則文字の検出を事前に行う必要があります。この記事では、全銀フォーマットの許容文字判定、全角カナから半角カナへの変換テーブル(濁音・半濁音の2文字分解を含む)、禁則文字の検出とレポートを Pure Java で実装します。全国銀行協会の仕様書に基づく文字セット定義と、Shift_JIS(JIS X 0208)との対応関係も整理します。

使いどころ

振込依頼人名(カナ)のバリデーション: 画面入力された全角カナを半角カナに変換し、全銀許容文字のみで構成されているか検証する

受取人名の全角→半角変換: 取引先マスタに登録された全角カタカナの受取人名を、全銀フォーマット送信用の半角カナに一括変換する

総合振込ファイル生成時の文字チェック: CSV や DB から取得した振込データに禁則文字(漢字・ひらがな・英小文字など)が含まれていないかを送信前に検出し、エラー行を一覧で返す

給与振込の依頼人名・受取人名の正規化: 給与システムから出力されたデータの全角スペースや全角記号を半角に統一し、全銀フォーマットに準拠させる

口座振替データの事前検証: 収納企業から受け取った口座振替依頼データに対し、フォーマット送信前に文字セットの整合性チェックを行う

コード例

ZenginCharsetValidator.java
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 全銀フォーマット文字セットのバリデーションと全角→半角カナ変換。
 * JIS X 0201 片仮名用図形文字集合に基づく許容文字の判定、
 * 全角カナ→半角カナの変換(濁音・半濁音の2文字分解を含む)、
 * 禁則文字の検出とレポートを Pure Java で実装する。
 */
public class ZenginCharsetValidator {

    // ========================================
    // 全角カナ → 半角カナ変換テーブル
    // ========================================

    /**
     * 全角カナ → 半角カナの変換マップ。
     * Map.ofEntries() でイミュータブルに宣言する(Java 9+)。
     * 濁音は「清音 + 濁点(゙)」、半濁音は「清音 + 半濁点(゚)」の2文字に分解する。
     * これは全銀フォーマットが JIS X 0201 の1バイト文字で濁音を表現するための仕様。
     */
    private static final Map<Character, String> KANA_TABLE = Map.ofEntries(
        // --- 清音: 全角1文字 → 半角1文字 ---
        Map.entry('ア', "ア"), Map.entry('イ', "イ"), Map.entry('ウ', "ウ"),
        Map.entry('エ', "エ"), Map.entry('オ', "オ"),
        Map.entry('カ', "カ"), Map.entry('キ', "キ"), Map.entry('ク', "ク"),
        Map.entry('ケ', "ケ"), Map.entry('コ', "コ"),
        Map.entry('サ', "サ"), Map.entry('シ', "シ"), Map.entry('ス', "ス"),
        Map.entry('セ', "セ"), Map.entry('ソ', "ソ"),
        Map.entry('タ', "タ"), Map.entry('チ', "チ"), Map.entry('ツ', "ツ"),
        Map.entry('テ', "テ"), Map.entry('ト', "ト"),
        Map.entry('ナ', "ナ"), Map.entry('ニ', "ニ"), Map.entry('ヌ', "ヌ"),
        Map.entry('ネ', "ネ"), Map.entry('ノ', "ノ"),
        Map.entry('ハ', "ハ"), Map.entry('ヒ', "ヒ"), Map.entry('フ', "フ"),
        Map.entry('ヘ', "ヘ"), Map.entry('ホ', "ホ"),
        Map.entry('マ', "マ"), Map.entry('ミ', "ミ"), Map.entry('ム', "ム"),
        Map.entry('メ', "メ"), Map.entry('モ', "モ"),
        Map.entry('ヤ', "ヤ"), Map.entry('ユ', "ユ"), Map.entry('ヨ', "ヨ"),
        Map.entry('ラ', "ラ"), Map.entry('リ', "リ"), Map.entry('ル', "ル"),
        Map.entry('レ', "レ"), Map.entry('ロ', "ロ"),
        Map.entry('ワ', "ワ"), Map.entry('ヲ', "ヲ"), Map.entry('ン', "ン"),
        // --- 濁音: 清音 + 濁点(゙) の2文字に分解 ---
        // 例: ガ → カ(U+FF76) + ゙(U+FF9E)
        Map.entry('ガ', "ガ"), Map.entry('ギ', "ギ"), Map.entry('グ', "グ"),
        Map.entry('ゲ', "ゲ"), Map.entry('ゴ', "ゴ"),
        Map.entry('ザ', "ザ"), Map.entry('ジ', "ジ"), Map.entry('ズ', "ズ"),
        Map.entry('ゼ', "ゼ"), Map.entry('ゾ', "ゾ"),
        Map.entry('ダ', "ダ"), Map.entry('ヂ', "ヂ"), Map.entry('ヅ', "ヅ"),
        Map.entry('デ', "デ"), Map.entry('ド', "ド"),
        Map.entry('バ', "バ"), Map.entry('ビ', "ビ"), Map.entry('ブ', "ブ"),
        Map.entry('ベ', "ベ"), Map.entry('ボ', "ボ"),
        // --- 半濁音: 清音 + 半濁点(゚) の2文字に分解 ---
        // 例: パ → ハ(U+FF8A) + ゚(U+FF9F)
        Map.entry('パ', "パ"), Map.entry('ピ', "ピ"), Map.entry('プ', "プ"),
        Map.entry('ペ', "ペ"), Map.entry('ポ', "ポ"),
        // --- 特殊文字 ---
        Map.entry('ー', "ー"),   // 全角長音(U+30FC) → 半角長音(U+FF70)
        Map.entry('・', "・"),    // 全角中点(U+30FB) → 半角中点(U+FF65)
        Map.entry('゛', "゙"),   // 全角濁点(U+309B) → 半角濁点(U+FF9E)
        Map.entry('゜', "゚"),   // 全角半濁点(U+309C) → 半角半濁点(U+FF9F)
        Map.entry('「', "「"),    // 全角鉤括弧(U+300C) → 半角(U+FF62)
        Map.entry('」', "」"),    // 全角鉤括弧(U+300D) → 半角(U+FF63)
        // --- 全角記号 → 半角記号 ---
        Map.entry('(', "("), Map.entry(')', ")"),
        Map.entry('-', "-"), Map.entry('.', "."), Map.entry('/', "/"),
        Map.entry('\u3000', " ") // 全角スペース(U+3000) → 半角スペース(U+0020)
    );

    /**
     * 全角英数字 → 半角英数字の変換マップ。
     * A-Z → A-Z、0-9 → 0-9 のコードポイント差分で変換。
     */
    private static final Map<Character, String> ALNUM_TABLE;
    static {
        var map = new java.util.HashMap<Character, String>();
        // 全角英大文字(U+FF21-U+FF3A) → 半角英大文字(U+0041-U+005A)
        for (char c = '\uff21'; c <= '\uff3a'; c++) {
            map.put(c, String.valueOf((char) ('A' + (c - '\uff21'))));
        }
        // 全角数字(U+FF10-U+FF19) → 半角数字(U+0030-U+0039)
        for (char c = '\uff10'; c <= '\uff19'; c++) {
            map.put(c, String.valueOf((char) ('0' + (c - '\uff10'))));
        }
        ALNUM_TABLE = Map.copyOf(map); // イミュータブルコピーで保護
    }

    // ========================================
    // 全銀許容文字の判定
    // ========================================

    /**
     * 全銀フォーマットで許容される文字かどうかを判定する。
     * JIS X 0201 片仮名用図形文字集合 + 半角英大文字 + 半角数字 + 一部記号が対象。
     *
     * @param c 判定対象の文字
     * @return 全銀フォーマットで許容される場合 true
     */
    public static boolean isZenginAllowed(char c) {
        // 半角数字: 0-9 (U+0030-U+0039)
        if (c >= '0' && c <= '9') return true;
        // 半角英大文字: A-Z (U+0041-U+005A) ※小文字は不可
        if (c >= 'A' && c <= 'Z') return true;
        // 半角スペース (U+0020)
        if (c == ' ') return true;
        // 半角記号: ( ) - . / — 全銀仕様で明示的に許容
        if (c == '(' || c == ')' || c == '-' || c == '.' || c == '/') return true;
        // 半角カナ: ヲ(U+FF66)-ン(U+FF9D)
        if (c >= 0xFF66 && c <= 0xFF9D) return true;
        // 半角濁点(U+FF9E)・半濁点(U+FF9F)
        if (c == 0xFF9E || c == 0xFF9F) return true;
        // 半角長音(U+FF70)
        if (c == 0xFF70) return true;
        // 半角中点(U+FF65) — 全銀仕様で許容される記号
        if (c == 0xFF65) return true;
        // 半角鉤括弧: 「(U+FF62) 」(U+FF63)
        if (c == 0xFF62 || c == 0xFF63) return true;
        // 上記以外は禁則文字
        return false;
    }

    // ========================================
    // 全角 → 半角変換
    // ========================================

    /**
     * 全角文字を全銀フォーマット用の半角文字に変換する。
     * - 全角カナ → 半角カナ(濁音・半濁音は2文字に分解)
     * - 全角英数字 → 半角英数字
     * - 全角記号 → 半角記号
     * - 変換テーブルにない文字はそのまま残す(後続のバリデーションで検出)
     *
     * @param input 変換対象の文字列
     * @return 半角に変換された文字列
     */
    public static String convertToHalfWidth(String input) {
        // 濁音分解で文字列が伸びる可能性があるため、余裕を持ったバッファを確保
        var result = new StringBuilder(input.length() * 2);
        for (int i = 0; i < input.length(); i++) {
            var c = input.charAt(i);
            // カナ・記号の変換テーブルを先に検索
            var kanaMapped = KANA_TABLE.get(c);
            if (kanaMapped != null) {
                result.append(kanaMapped);
                continue;
            }
            // 全角英数字の変換テーブルを検索
            var alnumMapped = ALNUM_TABLE.get(c);
            if (alnumMapped != null) {
                result.append(alnumMapped);
                continue;
            }
            // どちらにも該当しない → そのまま残す
            result.append(c);
        }
        return result.toString();
    }

    // ========================================
    // 禁則文字の検出
    // ========================================

    /** 禁則文字の情報を保持する record */
    record Violation(int position, char character) {
        @Override
        public String toString() {
            return "位置 %d: '%c' (U+%04X)".formatted(position, character, (int) character);
        }
    }

    /**
     * 文字列内の全銀禁則文字を検出し、位置と文字の一覧を返す。
     * 振込データの送信前バリデーションに使用する。
     *
     * @param input 検査対象の文字列(半角変換済みを想定)
     * @return 禁則文字の一覧(なければ空リスト)
     */
    public static List<Violation> findForbiddenChars(String input) {
        var violations = new ArrayList<Violation>();
        for (int i = 0; i < input.length(); i++) {
            var c = input.charAt(i);
            if (!isZenginAllowed(c)) {
                violations.add(new Violation(i, c));
            }
        }
        return violations;
    }

    // ========================================
    // メイン: 振込依頼人名のバリデーション例
    // ========================================

    public static void main(String[] args) {
        // --- 1. 全角カナ → 半角カナ変換 ---
        // 画面入力で全角カナが混入するケースを想定
        var depositorName = "カブシキガイシャ\u3000デブクラフト";  // 全角スペース含む
        System.out.println("=== 全角→半角変換 ===");
        var converted = convertToHalfWidth(depositorName);
        System.out.println("変換前: " + depositorName);
        System.out.println("変換後: " + converted);
        // 出力: カブシキガイシャ デブクラフト
        // 注意: 「ガ」→「ガ」で2文字に展開される

        // --- 2. 禁則文字チェック ---
        // 英小文字や漢字が混入した場合の検出
        System.out.println("\n=== 禁則文字チェック ===");
        var invalidInput = "abc株式会社デブクラフト";
        var violations = findForbiddenChars(invalidInput);
        if (violations.isEmpty()) {
            System.out.println("禁則文字なし");
        } else {
            System.out.println("禁則文字が見つかりました:");
            violations.forEach(v -> System.out.println("  " + v));
        }

        // --- 3. 変換+バリデーションの組み合わせ ---
        // 実務では「まず変換してからバリデーション」の順で処理する
        System.out.println("\n=== 変換→バリデーションの実務パターン ===");
        var mixedInput = "カブシキガイシャ\u3000ABC商事";
        var halfWidth = convertToHalfWidth(mixedInput);
        System.out.println("変換後: " + halfWidth);
        var afterCheck = findForbiddenChars(halfWidth);
        if (afterCheck.isEmpty()) {
            System.out.println("全銀フォーマット準拠 OK");
        } else {
            // 変換しても残る禁則文字(漢字など変換テーブルにない文字)
            System.out.println("変換後も残る禁則文字:");
            afterCheck.forEach(v -> System.out.println("  " + v));
        }
    }
}

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

Map.ofEntries() でイミュータブルな変換テーブルを宣言的に構築できる。var による型推論で記述量が減り、record で Violation 情報を保持できる。formatted() メソッドで文字列整形も簡潔になる。

Java 17
// Java 17: Map.ofEntries() でイミュータブルに宣言
private static final Map<Character, String> TABLE = Map.ofEntries(
    Map.entry('ア', "ア"),
    Map.entry('ガ', "ガ"),  // 濁音は清音+濁点の2文字
    Map.entry('パ', "パ"),  // 半濁音は清音+半濁点の2文字
    // ... 以下同様
);
// record で禁則文字情報を保持
record Violation(int position, char character, String codePoint) {}
// var + formatted() で簡潔に
var msg = "位置 %d: '%c' (U+%04X)".formatted(pos, c, (int) c);

Library Comparison

Pure Java(Map ベースの変換テーブル)全銀フォーマットの文字セット判定と全角→半角カナ変換のみが必要な場合。変換対象が JIS X 0201 の片仮名・英数字・記号に限定されるため、外部ライブラリなしで完結する。変換テーブルを自前で定義・保守する必要がある。全銀仕様の許容文字が約70種類と限定的なため、テーブルの規模は小さく保守負荷は低い。Unicode 正規化(NFC/NFD)には対応しないため、合成文字が入力される環境では事前に正規化を行う必要がある。
ICU4J(com.ibm.icu.text.Transliterator)全角・半角変換だけでなく、ひらがな→カタカナ変換、ラテン文字→カタカナ変換など、多言語間のトランスリテレーションが必要な場合。ICU4J は約14MB の依存を追加する。Transliterator の「Fullwidth-Halfwidth」ルールで全角→半角変換は一行で書けるが、全銀フォーマット固有の許容文字判定は結局自前で実装する必要がある。全角→半角変換だけが目的なら過剰な依存になる。
Apache Commons Lang(StringUtils / CharUtils)文字列操作ユーティリティを既にプロジェクトで利用しており、半角・全角判定の補助として使いたい場合。Commons Lang には全角→半角カナ変換の機能はない。isAscii() や isNumeric() で基本的な文字種判定は可能だが、半角カナ(JIS X 0201)の判定は含まれないため、全銀バリデーションの核心部分は自前実装が必要。依存を追加する意味が薄い。

注意点

濁音・半濁音の分解に注意: 全角の「ガ」は半角では「ガ」(清音+濁点)の2文字になる。変換後の文字列長が変わるため、固定長フィールドへの格納時にバイト数を再計算すること。

長音記号(ー)の変換: 全角長音「ー」(U+30FC) は半角長音「ー」(U+FF70) に変換する。ハイフン「-」(U+002D) や全角ダッシュ「—」(U+2014) とは別の文字なので混同しないこと。

スペースの全角・半角: 全角スペース(U+3000)は半角スペース(U+0020)に変換する必要がある。変換漏れがあると全銀システムで文字化けやリジェクトの原因になる。

英小文字は全銀フォーマットで不可: 半角英字は大文字(A-Z)のみ許容される。入力値に小文字が含まれる場合は toUpperCase() で変換するか、エラーとして返すかを業務要件に応じて決める。

Shift_JIS と JIS X 0201 の関係: Shift_JIS の半角カナ領域(0xA1-0xDF)は JIS X 0201 の片仮名集合に対応するが、Java の内部エンコーディングは UTF-16 であるため、Unicode のコードポイント(0xFF65-0xFF9F)で判定する。Shift_JIS のバイト値と直接比較しないこと。

FAQ

全角カタカナの「ヴ」(U+30F4)は全銀フォーマットで使えますか?

全銀フォーマットの標準仕様では「ヴ」は許容文字に含まれていません。銀行によっては「ヴ」(ウ+濁点)への変換を許容するケースもありますが、仕様書を確認したうえで「ヴ」に変換するか、エラーとして返すかを判断してください。

半角カナのバイト数は Shift_JIS と UTF-8 で異なりますか?

Shift_JIS では半角カナは1バイト(0xA1-0xDF)ですが、UTF-8 では3バイト(0xEFBDA5-0xEFBE9F)になります。全銀フォーマットの固定長フィールドは通常 Shift_JIS 前提のバイト数で定義されるため、UTF-8 で処理する場合はバイト数計算に注意が必要です。

全銀フォーマットの文字セットと JIS X 0201 の半角カナ集合は完全に一致しますか?

ほぼ一致しますが、全銀フォーマットでは一部の制御文字や JIS X 0201 のラテン文字集合のうち英小文字が除外されます。また、銀行ごとに微妙な差異がある場合があるため、実装時は利用する銀行の仕様書で許容文字を最終確認してください。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます