この記事でわかること
- 全銀フォーマットの4レコード構造(ヘッダ・データ・トレーラ・エンド)を完全解説
-
- JavaScriptで120バイト固定長レコードを生成する実装コード(コピペ可)
-
- よくある実装バグ(文字コード・ゼロ埋め・スペース埋め)の解決法
-
- ブラウザ完結でExcel/CSVから全銀ファイルを生成するOSSツールも紹介
はじめに:なぜ全銀フォーマットをJSで実装するのか
月末の経理担当者なら一度は体験したことがあるはず——**「銀行のシステムにファイルを弾かれた」**という悲劇。
全銀フォーマット(全銀協規定形式)は、日本の銀行間データ伝送の標準規格です。給与振込・総合振込など、日本企業が銀行に一括振込を依頼する際に使うファイル形式で、120バイト固定長のテキストファイルという、現代のエンジニアには少し異質な仕様です。
// 全銀ファイルの見た目(実際はこんな感じ)
1216980001234567890委託者名 05011310001234560000
2001300001234567890受取人名 1001234560000000050000 0
8000000010000000050000
9
これをPythonやJavaではよく実装例が見つかりますが、JavaScriptの完全実装例はほぼ存在しない。この記事ではブラウザ上で動くJS実装を完全公開します。
全銀フォーマットの仕様を理解する
4種類のレコード構造
全銀ファイルは4種類のレコードで構成されます:
| レコード | データ区分 | 役割 | 必須 |
|---|---|---|---|
| ヘッダレコード | 1 | ファイル全体の情報 | ✅ 1件のみ |
| データレコード | 2 | 各振込先の情報 | ✅ 1件以上 |
| トレーラレコード | 8 | 合計件数・合計金額 | ✅ 1件のみ |
| エンドレコード | 9 | ファイル終端 | ✅ 1件のみ |
全レコード共通ルール:
- 各レコードは120バイト固定長(半角文字)
-
- 文字コードは**JIS(Shift_JIS)**が基本
-
- 数字項目(N):右詰め、余白はゼロ埋め
-
- カナ/英数字項目(C):左詰め、余白はスペース埋め
ヘッダレコード(1レコード)の構造
| 項番 | 項目名 | 属性 | 桁数 | 内容 |
|---|---|---|---|---|
| 1 | データ区分 | N | 1 | 1固定 |
| 2 | 種別コード | N | 2 | 21:総合, 11/71:給与, 12/72:賞与 |
| 3 | コード区分 | N | 1 | 0:JIS |
| 4 | 委託者コード | N | 10 | 銀行が採番したコード |
| 5 | 委託者名 | C | 40 | 振込元名(半角カナ) |
| 6 | 取組日 | N | 4 | MMDD形式 |
| 7 | 仕向金融機関番号 | N | 4 | 銀行コード |
| 8 | 仕向金融機関名 | C | 15 | 銀行名(半角カナ) |
| 9 | 仕向店舗番号 | N | 3 | 支店コード |
| 10 | 仕向店舗名 | C | 15 | 支店名(半角カナ) |
| 11 | 預金種目(依頼人) | N | 1 | 1:普通, 2:当座 |
| 12 | 口座番号(依頼人) | N | 7 | 口座番号 |
| 13 | ダミー | C | 17 | スペース |
データレコード(2レコード)の構造
| 項番 | 項目名 | 属性 | 桁数 | 内容 |
|---|---|---|---|---|
| 1 | データ区分 | N | 1 | 2固定 |
| 2 | 被仕向金融機関番号 | N | 4 | 受取人の銀行コード |
| 3 | 被仕向金融機関名 | C | 15 | 受取人の銀行名 |
| 4 | 被仕向店舗番号 | N | 3 | 受取人の支店コード |
| 5 | 被仕向店舗名 | C | 15 | 受取人の支店名 |
| 6 | 手形交換所番号 | N | 4 | (通常0000) |
| 7 | 預金種目 | N | 1 | 1:普通, 2:当座, 4:貯蓄 |
| 8 | 口座番号 | N | 7 | 受取人口座番号 |
| 9 | 受取人名 | C | 30 | 受取人名(半角カナ) |
| 10 | 振込金額 | N | 10 | 金額(円) |
| 11 | 新規コード | N | 1 | 1:新規, 0:変更なし |
| 12 | 顧客コード1 | C | 10 | 任意コード |
| 13 | 顧客コード2 | C | 10 | 任意コード |
| 14 | 振込指定区分 | N | 1 | 7:電信, 8:文書 |
| 15 | 識別表示 | C | 1 | スペース |
JavaScriptで実装する
ユーティリティ関数
* 全銀フォーマット生成ユーティリティ
* * 全レコード共通の固定長フォーマット処理
* *```
* /**
* * 数字項目(N): 右詰め、余白ゼロ埋め
* * @param {string|number} value - 値
* * @param {number} length - バイト数
* */
* function padN(value, length) {
* const str = String(value).replace(/[^0-9]/g, '');
* if (str.length > length) {
* throw new Error(`数字項目が桁数超過: "${str}" > ${length}桁`);
* }
* return str.padStart(length, '0');
* }
/**
* カナ/英数字項目(C): 左詰め、余白スペース埋め
* * @param {string} value - 値(半角カナ・英数字)
* * @param {number} length - バイト数
* */
* function padC(value, length) {
* // 全角文字の検出(全角は2バイトなので注意)
* const byteLength = getByteLength(value);
* if (byteLength > length) {
* throw new Error(`カナ項目がバイト数超過: "${value}" = ${byteLength}バイト > ${length}バイト`);
* }
* // バイト数でパディング(全角混在の場合は要注意)
* return value.padEnd(length - byteLength + value.length, ' ');
* }
/**
* Shift_JIS換算のバイト長を返す
* * ブラウザ環境では TextEncoder が使えないため近似値
* */
* function getByteLength(str) {
* let len = 0;
* for (let i = 0; i < str.length; i++) {
* const code = str.charCodeAt(i);
* // ASCII (0x00-0x7F): 1バイト
* // 半角カナ (0xFF65-0xFF9F): 1バイト
* // 全角 (その他): 2バイト
* if (code <= 0x7F || (code >= 0xFF65 && code <= 0xFF9F)) {
* len += 1;
* } else {
* len += 2;
* }
* }
* return len;
* }
/**
* レコードが120バイトになっているか検証
* */
* function validateRecord(record, recordType) {
* const byteLen = getByteLength(record);
* if (byteLen !== 120) {
* throw new Error(
* `${recordType}レコードのバイト数が不正: ${byteLen}バイト(120バイト必須)`
* );
* }
* return record;
* }
* ```
### ヘッダレコード生成
```javascript/**
* ヘッダレコード生成(1レコード)
* * @param {Object} params
* * @param {string} params.typeCode - 種別コード ('21':総合, '11':給与, '12':賞与)
* * @param {string} params.clientCode - 委託者コード(10桁)
* * @param {string} params.clientName - 委託者名(半角カナ、最大40バイト)
* * @param {string} params.transferDate - 取組日(MMDD形式、例: '0630')
* * @param {string} params.bankCode - 仕向金融機関番号(4桁)
* * @param {string} params.bankName - 仕向金融機関名(半角カナ、最大15バイト)
* * @param {string} params.branchCode - 仕向店舗番号(3桁)
* * @param {string} params.branchName - 仕向店舗名(半角カナ、最大15バイト)
* * @param {string} params.accountType - 預金種目('1':普通, '2':当座)
* * @param {string} params.accountNumber - 口座番号(7桁)
* */
* function buildHeaderRecord(params) {
* const record = [
* padN('1', 1), // データ区分
* padN(params.typeCode, 2), // 種別コード
* padN('0', 1), // コード区分(JIS)
* padN(params.clientCode, 10), // 委託者コード
* padC(params.clientName, 40), // 委託者名
* padN(params.transferDate, 4), // 取組日
* padN(params.bankCode, 4), // 仕向金融機関番号
* padC(params.bankName, 15), // 仕向金融機関名
* padN(params.branchCode, 3), // 仕向店舗番号
* padC(params.branchName, 15), // 仕向店舗名
* padN(params.accountType, 1), // 預金種目
* padN(params.accountNumber, 7), // 口座番号
* padC('', 17), // ダミー(スペース)
* ].join('');
### データレコード生成
```javascript/**
* データレコード生成(2レコード)各振込先1件につき1レコード
* * @param {Object} params * @param {string} params.bankCode - 被仕向金融機関番号(4桁)
* * @param {string} params.bankName - 被仕向金融機関名(半角カナ、最大15バイト)
* * @param {string} params.branchCode - 被仕向店舗番号(3桁)
* * @param {string} params.branchName - 被仕向店舗名(半角カナ、最大15バイト)
* * @param {string} params.accountType - 預金種目('1':普通, '2':当座, '4':貯蓄)
* * @param {string} params.accountNumber - 口座番号(7桁)
* * @param {string} params.recipientName - 受取人名(半角カナ、最大30バイト)
* * @param {number} params.amount - 振込金額(円)
* * @param {string} [params.customerCode1=''] - 顧客コード1(最大10バイト)
* * @param {string} [params.customerCode2=''] - 顧客コード2(最大10バイト)
* * @param {string} [params.transferType='7'] - 振込指定区分('7':電信, '8':文書)
* */
* function buildDataRecord(params) {
* const record = [
* padN('2', 1), // データ区分
* padN(params.bankCode, 4), // 被仕向金融機関番号
* padC(params.bankName, 15), // 被仕向金融機関名
* padN(params.branchCode, 3), // 被仕向店舗番号
* padC(params.branchName, 15), // 被仕向店舗名
* padN('0000', 4), // 手形交換所番号(通常0000)
* padN(params.accountType, 1), // 預金種目
* padN(params.accountNumber, 7), // 口座番号
* padC(params.recipientName, 30), // 受取人名
* padN(params.amount, 10), // 振込金額
* padN('1', 1), // 新規コード(1:新規)
* padC(params.customerCode1 || '', 10), // 顧客コード1
* padC(params.customerCode2 || '', 10), // 顧客コード2
* padN(params.transferType || '7', 1), // 振込指定区分
* padC('', 1), // 識別表示(スペース)
* ].join('');
return validateRecord(record, 'データ');
}
トレーラ・エンドレコード生成
/**
* トレーラレコード生成(8レコード)
* * @param {number} totalCount - データレコード件数 * @param {number} totalAmount - 振込金額合計
* */
* function buildTrailerRecord(totalCount, totalAmount) {
* const record = [
* padN('8', 1), // データ区分
* padN(totalCount, 6), // 合計件数
* padN(totalAmount, 12), // 合計金額
* padC('', 101), // ダミー(スペース)
* ].join('');
return validateRecord(record, 'トレーラ');
}
/**
* エンドレコード生成(9レコード)
* */
* function buildEndRecord() {
* const record = padN('9', 1) + padC('', 119);
* return validateRecord(record, 'エンド');
* }
* ```
### 全銀ファイル生成(メイン関数)
```javascript/**
* 全銀フォーマットファイルを生成する
* * @param {Object} header - ヘッダ情報 * @param {Array<Object>} dataList - 振込データリスト
* * @returns {string} 全銀フォーマットのテキスト
* */
* function generateZenginFile(header, dataList) {
* if (!dataList || dataList.length === 0) {
* throw new Error('振込データが1件もありません');
* }
const lines = [];
// 1. ヘッダレコード
lines.push(buildHeaderRecord(header));
// 2. データレコード(各振込先)
let totalAmount = 0;
for (const data of dataList) {
lines.push(buildDataRecord(data));
totalAmount += Number(data.amount);
}
// 3. トレーラレコード
lines.push(buildTrailerRecord(dataList.length, totalAmount));
// 4. エンドレコード
lines.push(buildEndRecord());
// 改行コードはCR+LF(銀行によって異なるが一般的)
return lines.join('\r\n');
}
```
## 実際に使ってみる
```javascript
// 使用例:給与振込ファイルを生成
const header = {
typeCode: '11', // 11:給与
clientCode: '1234567890', // 委託者コード
clientName: 'ヤマダ カブシキガイシャ', // 半角カナ
transferDate: '0630', // 6月30日
bankCode: '0001', // みずほ銀行
bankName: 'ミズホ', // 半角カナ
branchCode: '001',
branchName: 'マルノウチ',
accountType: '1', // 普通
accountNumber: '1234567',
};
const employees = [
{
bankCode: '0001',
bankName: 'ミズホ',
branchCode: '100',
branchName: 'シンジュク',
accountType: '1',
accountNumber: '1111111',
recipientName: 'ヤマダ タロウ',
amount: 350000,
},
{
bankCode: '0009',
bankName: 'ミツビシUFJ',
branchCode: '050',
branchName: 'シブヤ',
accountType: '1',
accountNumber: '2222222',
recipientName: 'サトウ ハナコ',
amount: 280000,
},
];
const zenginText = generateZenginFile(header, employees);
console.log(zenginText);
// ブラウザでダウンロード(Shift_JIS変換が必要な場合は別途対応)
function downloadZenginFile(content, filename = '振込依頼.txt') {
const blob = new Blob([content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
downloadZenginFile(zenginText);
```
## よくある実装バグ TOP5
実務でよく見かけるバグと解決法をまとめました。
### バグ1: バイト数のカウントミス(最頻出)
```javascript
// ❌ NG: JavaScriptのlengthは文字数(全角も1)
'ヤマダ'.length // → 4(正しい、半角カナは1文字=1バイト)
'山田'.length // → 2(文字数は2だが、Shift_JISでは4バイト!)
// ✅ OK: Shift_JISバイト数を正しく計算
function getShiftJISByteLength(str) {
let len = 0;
for (const char of str) {
const code = char.charCodeAt(0);
len += (code <= 0x7F || (code >= 0xFF65 && code <= 0xFF9F)) ? 1 : 2;
}
return len;
}
```
### バグ2: 金額のゼロ埋め漏れ
```javascript
// ❌ NG: 金額をそのまま文字列化
String(50000) // → "50000" (5桁)
// 10桁が必要なのに5桁しかない → 銀行に弾かれる
// ✅ OK: padN関数でゼロ埋め
padN(50000, 10) // → "0000050000" (10桁)
```
### バグ3: 全角文字を混入させてしまう
```javascript
// ❌ NG: 全角文字を使ってしまう(よくあるコピペミス)
const name = '山田太郎'; // 全角 → 銀行に弾かれる
// ✅ OK: 必ず半角カナに変換してから使う
function toHankakuKana(str) {
const kanaMap = {
'ア': 'ア', 'イ': 'イ', 'ウ': 'ウ', 'エ': 'エ', 'オ': 'オ',
'カ': 'カ', 'キ': 'キ', 'ク': 'ク', 'ケ': 'ケ', 'コ': 'コ',
// ... (全カナマップ)
' ': ' ', // 全角スペース→半角スペース
};
return str.split('').map(c => kanaMap[c] || c).join('');
}
```
### バグ4: BOM付きUTF-8で出力してしまう
```javascript
// ❌ NG: BOMがついたUTF-8 → 銀行のシステムが読めない
const blob = new Blob(['\uFEFF' + content], { type: 'text/plain' });
// ✅ OK: BOMなし、かつShift_JIS変換が必要な場合
// ブラウザ標準ではShift_JIS出力が困難なため、
// encoding.js などのライブラリを使用するか、
// サーバーサイドで変換する
```
### バグ5: トレーラの件数・金額が合わない
```javascript
// ❌ NG: ハードコーディング
buildTrailerRecord(10, 5000000);
// ✅ OK: データから自動集計
const totalCount = dataList.length;
const totalAmount = dataList.reduce((sum, d) => sum + Number(d.amount), 0);
buildTrailerRecord(totalCount, totalAmount);
```
## 文字コードの落とし穴:ブラウザでShift_JIS出力する方法
ブラウザのJavaScriptはデフォルトでUTF-8を使います。多くの銀行はJIS(Shift_JIS)を要求するため、変換が必要です。
```javascript
// encoding.js を使う場合(CDNから読み込み)
// <script src="https://cdn.jsdelivr.net/npm/encoding-japanese/encoding.min.js"></script>
function downloadAsShiftJIS(content, filename) {
// UTF-16 → Shift_JIS に変換
const sjisArray = Encoding.convert(
Encoding.stringToCode(content),
{ to: 'SJIS', from: 'UNICODE' }
);
const uint8Array = new Uint8Array(sjisArray);
const blob = new Blob([uint8Array], { type: 'text/plain; charset=shift_jis' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
```
:::note info
**実務Tips:** 最近は「JIS/Shift_JIS不要」という銀行も増えています。まず取引銀行の仕様書で文字コード要件を確認してください。UTF-8対応の銀行なら変換不要です。
:::
## 実装を自前でやる前に:無料ツールという選択肢
全銀フォーマットの実装は仕様が複雑で、バグが出ても銀行の振込が止まるリスクがあります。
個人プロジェクトや社内ツールで使う場合、自前実装の前に既存の無料ツールを検討するのも現実的な選択肢です。
筆者が開発した [yamada-tools.jp](https://yamada-tools.jp) の全銀フォーマット変換ツール は:
- ExcelやCSVをアップロードするだけで全銀ファイルを生成
- - 給与振込・総合振込・賞与振込に対応
- - 完全ブラウザ処理(ファイルはサーバーに送信されない)
- - 登録不要・完全無料
自前実装が必要な場合でも、このツールの出力ファイルを正解データとして使い、自分の実装と比較することができます。
## テストの書き方
全銀フォーマット実装のテストポイントです:
```javascript// Jest/Vitest での単体テスト例
describe('全銀フォーマット生成', () => {
test('ヘッダレコードが120バイトであること', () => {
const record = buildHeaderRecord({
typeCode: '11',
clientCode: '1234567890',
clientName: 'ヤマダカイシャ',
transferDate: '0630',
bankCode: '0001',
bankName: 'ミズホ',
branchCode: '001',
branchName: 'マルノウチ',
accountType: '1',
accountNumber: '1234567',
});
expect(getByteLength(record)).toBe(120);
});
test('データレコードが120バイトであること', () => {
const record = buildDataRecord({
bankCode: '0001',
bankName: 'ミズホ',
branchCode: '100',
branchName: 'シンジュク',
accountType: '1',
accountNumber: '1111111',
recipientName: 'ヤマダ タロウ',
amount: 350000,
});
expect(getByteLength(record)).toBe(120);
});
test('金額合計が正しく計算されること', () => {
const data = [
{ amount: 350000, ...otherFields },
{ amount: 280000, ...otherFields },
];
const total = data.reduce((sum, d) => sum + d.amount, 0);
expect(total).toBe(630000);
});
test('全角文字がある場合にエラーを投げること', () => {
expect(() => padC('山田太郎', 10)).toThrow();
});
});
```
## まとめ
全銀フォーマットのJS実装で押さえるポイント:
| ポイント | 内容 |
|---|---|
| 固定長 | 全レコード120バイト必須 |
| N項目 | 右詰め、ゼロ埋め |
| C項目 | 左詰め、スペース埋め |
| 文字コード | Shift_JIS(銀行による) |
| カナ | 必ず半角カナ |
| バイト計算 | 全角=2バイト、半角=1バイト |
| 金額 | トレーラで件数・合計を必ず合わせる |
## 参考リンク
- [全銀協 公式フォーマット仕様](https://www.zenginkyo.or.jp/)
- - [全銀フォーマット変換ツール(無料・ブラウザ完結)](https://yamada-tools.jp)
- - [全銀フォーマット完全ガイド【2026年版】](https://yamada-tools.jp/blog/zengin-format-guide)
この記事で紹介したコードは MIT ライセンスで公開しています。実務利用の際は取引銀行の最新仕様書を必ず確認してください。
return validateRecord(record, 'ヘッダ');
}