概要
銀行間の振込データを扱う全銀フォーマットでは、使用できる文字が JIS X 0201 片仮名用図形文字集合に基づく厳格な範囲に制限されています。許容されるのは半角カナ(ア-ン、濁点゙、半濁点゚)、半角英大文字(A-Z)、半角数字(0-9)、および一部の半角記号(スペース、括弧、ハイフン、ピリオド、スラッシュなど)のみです。この制限を満たさない文字を含む振込データを送信すると、銀行システムが受け付けずに振込が失敗します。実務では、画面入力やCSV取込で全角カナや全角英数字が混入するケースが日常的に発生するため、全角→半角の変換処理と禁則文字の検出を事前に行う必要があります。この記事では、全銀フォーマットの許容文字判定、全角カナから半角カナへの変換テーブル(濁音・半濁音の2文字分解を含む)、禁則文字の検出とレポートを Pure Java で実装します。全国銀行協会の仕様書に基づく文字セット定義と、Shift_JIS(JIS X 0208)との対応関係も整理します。
使いどころ
振込依頼人名(カナ)のバリデーション: 画面入力された全角カナを半角カナに変換し、全銀許容文字のみで構成されているか検証する
受取人名の全角→半角変換: 取引先マスタに登録された全角カタカナの受取人名を、全銀フォーマット送信用の半角カナに一括変換する
総合振込ファイル生成時の文字チェック: CSV や DB から取得した振込データに禁則文字(漢字・ひらがな・英小文字など)が含まれていないかを送信前に検出し、エラー行を一覧で返す
給与振込の依頼人名・受取人名の正規化: 給与システムから出力されたデータの全角スペースや全角記号を半角に統一し、全銀フォーマットに準拠させる
口座振替データの事前検証: 収納企業から受け取った口座振替依頼データに対し、フォーマット送信前に文字セットの整合性チェックを行う
コード例
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));
}
}
}Version Coverage
Map.ofEntries() でイミュータブルな変換テーブルを宣言的に構築できる。var による型推論で記述量が減り、record で Violation 情報を保持できる。formatted() メソッドで文字列整形も簡潔になる。
// 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
注意点
濁音・半濁音の分解に注意: 全角の「ガ」は半角では「ガ」(清音+濁点)の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
全銀フォーマットの標準仕様では「ヴ」は許容文字に含まれていません。銀行によっては「ヴ」(ウ+濁点)への変換を許容するケースもありますが、仕様書を確認したうえで「ヴ」に変換するか、エラーとして返すかを判断してください。
Shift_JIS では半角カナは1バイト(0xA1-0xDF)ですが、UTF-8 では3バイト(0xEFBDA5-0xEFBE9F)になります。全銀フォーマットの固定長フィールドは通常 Shift_JIS 前提のバイト数で定義されるため、UTF-8 で処理する場合はバイト数計算に注意が必要です。
ほぼ一致しますが、全銀フォーマットでは一部の制御文字や JIS X 0201 のラテン文字集合のうち英小文字が除外されます。また、銀行ごとに微妙な差異がある場合があるため、実装時は利用する銀行の仕様書で許容文字を最終確認してください。