インターフェース設計書とは、システムとシステムの間でやり取りするデータについて、「誰が・いつ・どんな形式で渡し、うまく渡せなかったときにどうするか」を両者で取り決めた設計書です。 作る側と受け取る側が別のチームになるため、文字コードや改行のような細かい決まりまで文書にしておくことで、つなぐ段階での手戻りを減らせます。

この記事では、ファイルを受け渡す連携(ファイル連携)に絞り、インターフェース一覧と個別定義のテンプレートをMarkdownとCSVで用意して、架空の商品マスター連携で全項目を記入します。CSVと固定長の項目定義にサンプルデータを付け、ファイルが項目定義どおりかを検査するPythonコードを、実行結果と一緒に載せます。

確認環境:Ubuntu 24.04、Python 3.11.15(標準ライブラリのみ)。掲載したサンプルファイルと検査の出力は、すべてこの環境で作成・実行したものです。

インターフェース設計書の項目早見表

区分 項目 決めること 記入例(商品マスター連携)
一覧 IF-ID・名前 インターフェースを一意に指す番号と名前 IF-001 商品マスター連携
一覧 連携元・連携先 どのシステムが作り、どのシステムが受け取るか 商品管理システム → 販売サイト
個別 連携方式 受け渡しの手段と置き場所 SFTPで連携先の受信フォルダへ配置
個別 タイミング 頻度、時刻、全件か差分か 毎日 02:00、全件
個別 ファイル名 名前の付け方 products_YYYYMMDD.csv
個別 文字コード・改行 文字の符号化と行の区切り UTF-8(BOMなし)、LF
個別 区切り・囲み 項目の区切り方 カンマ区切り。カンマを含む値はダブルクォートで囲む
個別 ヘッダー・トレーラー 先頭と末尾に付ける管理用の行 H(IF-ID、作成日時)、T(データ件数)
個別 項目定義 項目名、型、桁数、必須、値の決まり 商品コード:英数8桁、必須、P+数字7桁
個別 エラー時の扱い 定義と違うファイルをどうするか 1件でもエラーがあれば全件取り込まない
個別 再送 送り直すときの決まり 同じファイル名で置き直す。何回取り込んでも同じ結果

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「インターフェース設計書の書き方と項目|ファイル連携の項目定義サンプル」

インターフェース設計書の構成

「一覧」で全体を見渡し、「個別定義」で1本ずつ取り決める2段の構成にします。

書類 役割 主な読み手
インターフェース一覧 システム間のつながりを1行1本で並べる 全体を管理する人、両システムの担当
個別定義(連携の取り決め) 方式、タイミング、形式、エラー時の扱い、再送 作る側と受け取る側の開発担当、運用担当
個別定義(項目定義) レコードの中身を1項目ずつ定義する 作る側と受け取る側の開発担当、テスト担当
エラーコード一覧 検査で見つかる不備と、そのときの動き 受け取る側の開発担当、運用担当

ファイル連携で出てくる用語です。

用語 意味
レコード ファイルの1行。1件のデータに当たる
ヘッダーレコード 先頭の1行。どのインターフェースの、いつ作ったファイルかを書く
トレーラーレコード 末尾の1行。データの件数を書き、途中で切れていないかを確かめる
固定長 項目ごとのバイト数を決め、区切り文字を使わずに並べる形式
全件連携・差分連携 毎回すべてのデータを渡すか、変わった分だけを渡すか

テンプレート1:インターフェース一覧

| IF-ID | インターフェース名 | 連携元 | 連携先 | 連携方式 | 形式 | タイミング | 全件/差分 | 件数の目安 |
|---|---|---|---|---|---|---|---|---|
| IF-000 | (名前) | (システム名) | (システム名) | (SFTP配置 など) | (CSV/固定長) | (毎日 hh:mm) | 全件 | (件) |
IF-ID,インターフェース名,連携元,連携先,連携方式,形式,タイミング,全件/差分,件数の目安
IF-000,(名前),(システム名),(システム名),(SFTP配置 など),(CSV/固定長),(毎日 hh:mm),全件,(件)

テンプレート2:個別定義(連携の取り決め)

## IF-000 (インターフェース名)

| 項目 | 内容 |
|---|---|
| 目的 | |
| 連携元/連携先 | |
| 連携方式 | |
| 配置先 | |
| タイミング | |
| 全件/差分 | |
| ファイル名 | |
| 配置完了の合図 | |
| 文字コード | |
| 改行コード | |
| 形式・区切り | |
| 囲み文字 | |
| ヘッダーレコード | |
| トレーラーレコード | |
| 0件のとき | |
| エラー時の扱い | |
| 再送 | |
| 保管期間 | |
| 連絡先 | |
項目,内容
目的,
連携元/連携先,
連携方式,
配置先,
タイミング,
全件/差分,
ファイル名,
配置完了の合図,
文字コード,
改行コード,
形式・区切り,
囲み文字,
ヘッダーレコード,
トレーラーレコード,
0件のとき,
エラー時の扱い,
再送,
保管期間,
連絡先,

テンプレート3:項目定義とエラーコード

CSV形式の項目定義です。

| No | 項目名 | 型 | 桁数(文字) | 必須 | 値の決まり | 備考 |
|---|---|---|---|---|---|---|
| 1 | | | | | | |
No,項目名,型,桁数(文字),必須,値の決まり,備考
1,,,,,,

固定長の項目定義です。開始位置とバイト数、詰め方の列が加わります。

| No | 項目名 | 型 | 開始位置 | バイト数 | 詰め方 | 必須 | 値の決まり |
|---|---|---|---|---|---|---|---|
| 1 | | | | | | | |
No,項目名,型,開始位置,バイト数,詰め方,必須,値の決まり
1,,,,,,,

エラーコード一覧です。

| コード | 検査の単位 | 内容 | 受け取る側の動き | 連携元への依頼 |
|---|---|---|---|---|
| E000 | | | | |
コード,検査の単位,内容,受け取る側の動き,連携元への依頼
E000,,,,

記入例:商品マスター連携

架空の商品管理システムから、販売サイトと倉庫システムへ商品マスターを渡す連携です。同じデータをCSVと固定長の2つの形式で定義します。

インターフェース一覧の記入例

IF-ID インターフェース名 連携元 連携先 連携方式 形式 タイミング 全件/差分 件数の目安
IF-001 商品マスター連携(販売サイト向け) 商品管理システム 販売サイト SFTP配置 CSV 毎日 02:00 全件 5,000件
IF-002 商品マスター連携(倉庫向け) 商品管理システム 倉庫システム SFTP配置 固定長 毎日 02:00 全件 5,000件
IF-ID,インターフェース名,連携元,連携先,連携方式,形式,タイミング,全件/差分,件数の目安
IF-001,商品マスター連携(販売サイト向け),商品管理システム,販売サイト,SFTP配置,CSV,毎日 02:00,全件,"5,000件"
IF-002,商品マスター連携(倉庫向け),商品管理システム,倉庫システム,SFTP配置,固定長,毎日 02:00,全件,"5,000件"

個別定義の記入例:IF-001(CSV)

項目 内容
目的 販売サイトに表示する商品の名前・単価・取扱区分を、商品管理システムの内容に合わせる
連携元/連携先 商品管理システム/販売サイト
連携方式 連携元がSFTPで連携先のサーバーへファイルを配置する
配置先 /data/if/in/IF-001/
タイミング 毎日 02:00 までに配置。連携先は 02:30 に取り込む
全件/差分 全件(連携先は取り込みのたびに全件を入れ替える)
ファイル名 products_YYYYMMDD.csv(YYYYMMDD は作成日)
配置完了の合図 products_YYYYMMDD.csv.tmp という名前で転送し、転送が終わってから .tmp を外す
文字コード UTF-8(BOMなし)
改行コード LF。最終行の末尾にも付ける
形式・区切り CSV、カンマ区切り
囲み文字 カンマ・ダブルクォート・改行を含む値だけダブルクォートで囲む
ヘッダーレコード 1行目。レコード区分 H、IF-ID、作成日時(YYYYMMDDhhmmss)
トレーラーレコード 最終行。レコード区分 T、データレコードの件数
0件のとき ヘッダーとトレーラー(件数0)だけのファイルを配置する
エラー時の扱い 検査で1件でもエラーがあれば全件を取り込まず、前日の内容のまま運用する。エラー一覧を連携元へ送る
再送 連携元が直したファイルを同じファイル名で置き直す。連携先は手動で取り込みを起動する
保管期間 取り込み済みのファイルを連携先で30日間保管
連絡先 連携元:商品管理システム担当/連携先:販売サイト担当

「配置完了の合図」は、転送の途中のファイルを取り込んでしまうことを防ぐ取り決めです。もう1つの防ぎ方がトレーラーの件数で、ファイルが途中で切れていれば件数が合わなくなります。

全件連携で「取り込みのたびに全件を入れ替える」と決めておくと、同じファイルを何回取り込んでも結果が同じになり、再送の手順が「置き直して取り込む」だけになります。

項目定義の記入例:IF-001(CSV)

データレコードの定義です。

No 項目名 型 桁数(文字) 必須 値の決まり 備考
1 レコード区分 半角英数 1 ○ D 固定
2 商品コード 半角英数 8 ○ P+数字7桁 キー。ファイル内で重複しない
3 商品名 文字 20以内 ○
4 カテゴリコード 半角数字 3 ○
5 単価 半角数字 7以内 ○ 0以上の整数。税抜、円 カンマ・小数点を付けない
6 販売開始日 日付 8 ○ YYYYMMDD
7 販売終了日 日付 8 YYYYMMDD 決まっていないときは空
8 取扱区分 半角数字 1 ○ 1:取扱中、2:休止、9:終了
No,項目名,型,桁数(文字),必須,値の決まり,備考
1,レコード区分,半角英数,1,○,D 固定,
2,商品コード,半角英数,8,○,P+数字7桁,キー。ファイル内で重複しない
3,商品名,文字,20以内,○,,
4,カテゴリコード,半角数字,3,○,,
5,単価,半角数字,7以内,○,0以上の整数。税抜、円,カンマ・小数点を付けない
6,販売開始日,日付,8,○,YYYYMMDD,
7,販売終了日,日付,8,,YYYYMMDD,決まっていないときは空
8,取扱区分,半角数字,1,○,1:取扱中、2:休止、9:終了,

ヘッダーとトレーラーの定義です。

レコード No 項目名 型 桁数 値の決まり
ヘッダー 1 レコード区分 半角英数 1 H 固定
ヘッダー 2 IF-ID 半角英数 6 IF-001
ヘッダー 3 作成日時 半角数字 14 YYYYMMDDhhmmss
トレーラー 1 レコード区分 半角英数 1 T 固定
トレーラー 2 データ件数 半角数字 7以内 データレコード(D)の行数

サンプルデータ(products_20261005.csv)です。3行目は商品名にカンマを含むので、ダブルクォートで囲んでいます。

H,IF-001,20261005020000
D,P0000101,ノート A5 40枚,110,180,20260401,,1
D,P0000102,"ボールペン 黒, 0.5mm",120,120,20260401,,1
D,P0000103,卓上カレンダー 2026年版,130,980,20250901,20260331,9
D,P0000104,付箋 75mm 5色セット,110,450,20261101,,2
T,4

項目定義の記入例:IF-002(固定長)

同じ内容を固定長で定義します。連携の取り決めのうち、IF-001 と違うところは次の4点です。

項目 内容
ファイル名 products_YYYYMMDD.dat
文字コード Shift_JIS(Windows の拡張文字を含む CP932)
改行コード CRLF
形式 固定長。1レコード 80 バイト(改行を除く)

データレコードの定義です。

No 項目名 型 開始位置 バイト数 詰め方 必須 値の決まり
1 レコード区分 半角英数 1 1 ○ D 固定
2 商品コード 半角英数 2 8 ○ P+数字7桁
3 商品名 文字 10 40 左詰め・半角スペース埋め ○ 全角は1文字2バイト
4 カテゴリコード 半角数字 50 3 ○
5 単価 半角数字 53 7 右詰め・ゼロ埋め ○ 0以上の整数
6 販売開始日 日付 60 8 ○ YYYYMMDD
7 販売終了日 日付 68 8 決まっていないときは半角スペース8個 YYYYMMDD
8 取扱区分 半角数字 76 1 ○ 1・2・9
9 予備 半角 77 4 半角スペース
No,項目名,型,開始位置,バイト数,詰め方,必須,値の決まり
1,レコード区分,半角英数,1,1,,○,D 固定
2,商品コード,半角英数,2,8,,○,P+数字7桁
3,商品名,文字,10,40,左詰め・半角スペース埋め,○,全角は1文字2バイト
4,カテゴリコード,半角数字,50,3,,○,
5,単価,半角数字,53,7,右詰め・ゼロ埋め,○,0以上の整数
6,販売開始日,日付,60,8,,○,YYYYMMDD
7,販売終了日,日付,68,8,決まっていないときは半角スペース8個,,YYYYMMDD
8,取扱区分,半角数字,76,1,,○,1・2・9
9,予備,半角,77,4,半角スペース,,

ヘッダーは「H(1)+IF-ID(6)+作成日時(14)+予備(59)」、トレーラーは「T(1)+データ件数(7、右詰め・ゼロ埋め)+予備(72)」で、どちらも80バイトです。

サンプルデータ(products_20261005.dat)の中身です。記事に載せるためにUTF-8へ変換して表示しています。行末の位置がわかるように、右端に縦棒を付けました。

HIF-00120261005020000                                                           |
DP0000101ノート A5 40枚                          110000018020260401        1    |
DP0000102ボールペン 黒, 0.5mm                    120000012020260401        1    |
DP0000103卓上カレンダー 2026年版                 130000098020250901202603319    |
DP0000104付箋 75mm 5色セット                     110000045020261101        2    |
T0000004                                                                        |

全角文字は画面上で2桁分の幅になるため、等幅フォントで見ると縦棒の位置がそろいます。ファイルの大きさは 492 バイトで、(80+改行2)×6行と一致します。

固定長では、桁数を「文字数」ではなく「バイト数」で数えることを設計書に書いておきます。「ボールペン 黒, 0.5mm」は14文字ですが、Shift_JIS では全角6文字が12バイト、半角8文字が8バイトで、合わせて20バイトです。

エラーコード一覧の記入例

コード 検査の単位 内容 受け取る側の動き 連携元への依頼
E101 ファイル 決めた文字コードで読めない 全件取り込まない 文字コードを直して再送
E102 ファイル 改行コードが決めたものにそろっていない 全件取り込まない 改行コードを直して再送
E201 ファイル ヘッダーレコードがない、またはIF-ID・作成日時が正しくない 全件取り込まない ファイルの取り違えを確認して再送
E202 ファイル トレーラーレコードがない 全件取り込まない 転送が途中で切れていないか確認して再送
E203 ファイル トレーラーの件数とデータ件数が一致しない 全件取り込まない 同上
E301 レコード 項目数(固定長ではレコード長)が定義と違う 全件取り込まない 該当行を直して再送
E302 項目 必須項目が空 全件取り込まない 該当行を直して再送
E303 項目 型が違う(数字・日付として読めない) 全件取り込まない 該当行を直して再送
E304 項目 桁数を超えている 全件取り込まない 該当行を直して再送
E305 項目 決められた値ではない 全件取り込まない 該当行を直して再送
E306 レコード キー(商品コード)がファイル内で重複している 全件取り込まない 重複を解消して再送

番号の百の位を検査の段階に合わせる(1xx:ファイルの読み取り、2xx:ヘッダーとトレーラー、3xx:レコードと項目)と、コードを見ただけでどの段階の不備かがわかります。

項目定義どおりかを検査するPythonコード

項目定義を表(リスト)としてコードの先頭に持ち、CSVと固定長の両方を検査します。check_if.py として保存します。

"""インターフェースファイルが項目定義どおりかを検査する。
使い方: python3 check_if.py csv   products_20261005.csv
        python3 check_if.py fixed products_20261005.dat
終了コード: 0 = 定義どおり / 1 = エラーあり
"""
import csv
import io
import re
import sys
from datetime import datetime

IF_ID = "IF-001"


def is_date(v):
    try:
        datetime.strptime(v, "%Y%m%d")
        return len(v) == 8
    except ValueError:
        return False


# 項目定義(データレコード)。csv_len は文字数、fix_len はバイト数
ITEMS = [
    # 項目名,        型,      csv_len, fix_len, 必須,  値の決まり
    ("レコード区分", "英数", 1, 1, True, lambda v: v == "D"),
    ("商品コード", "英数", 8, 8, True, lambda v: re.fullmatch(r"P[0-9]{7}", v)),
    ("商品名", "文字", 20, 40, True, None),
    ("カテゴリコード", "数字", 3, 3, True, None),
    ("単価", "数字", 7, 7, True, None),
    ("販売開始日", "日付", 8, 8, True, None),
    ("販売終了日", "日付", 8, 8, False, None),
    ("取扱区分", "数字", 1, 1, True, lambda v: v in ("1", "2", "9")),
]
TYPE_CHECK = {
    "英数": lambda v: re.fullmatch(r"[0-9A-Za-z\-]+", v),
    "数字": lambda v: re.fullmatch(r"[0-9]+", v),
    "日付": is_date,
    "文字": lambda v: True,
}
FIXED = {"encoding": "cp932", "newline": b"\r\n", "record_len": 80}
CSV = {"encoding": "utf-8", "newline": b"\n"}


def check_values(no, values, errors):
    """1レコード分の値(文字列のリスト)を項目定義と比べる"""
    for (name, typ, max_len, _, required, rule), v in zip(ITEMS, values):
        if v == "":
            if required:
                errors.append((no, "E302", f"{name}: 必須項目が空です"))
            continue
        if not TYPE_CHECK[typ](v):
            errors.append((no, "E303", f"{name}: {typ}として読めません「{v}」"))
        elif len(v) > max_len:
            errors.append((no, "E304", f"{name}: {max_len}文字を超えています({len(v)}文字)"))
        elif rule and not rule(v):
            errors.append((no, "E305", f"{name}: 決められた値ではありません「{v}」"))


def check_frame(records, errors):
    """ヘッダー・トレーラー・キー重複を調べ、データレコードを返す。
    records は (行番号, 値のリスト) のリスト"""
    if not records or records[0][1][0] != "H":
        errors.append((1, "E201", "先頭がヘッダーレコード(H)ではありません"))
        return []
    head = records[0][1]
    if len(head) < 3 or head[1] != IF_ID:
        errors.append((1, "E201", f"ヘッダーのIF-IDが {IF_ID} ではありません"))
    elif not re.fullmatch(r"[0-9]{14}", head[2]):
        errors.append((1, "E201", "ヘッダーの作成日時が14桁の数字ではありません"))
    last_no, last = records[-1]
    if last[0] != "T":
        errors.append((last_no, "E202", "最後がトレーラーレコード(T)ではありません"))
        return records[1:]
    data = records[1:-1]
    if len(last) < 2 or not last[1].isdigit() or int(last[1]) != len(data):
        got = last[1] if len(last) > 1 else ""
        errors.append((last_no, "E203", f"トレーラーの件数「{got}」とデータ件数 {len(data)} が一致しません"))
    seen = {}
    for no, values in data:
        if len(values) > 1 and values[1]:
            if values[1] in seen:
                errors.append((no, "E306", f"商品コード {values[1]} が {seen[values[1]]} 行目と重複しています"))
            seen.setdefault(values[1], no)
    return data


def read_bytes(path, spec, errors):
    """文字コードと改行を調べ、行ごとの bytes を返す"""
    raw = open(path, "rb").read()
    try:
        raw.decode(spec["encoding"])
    except UnicodeDecodeError as e:
        errors.append((0, "E101", f"{spec['encoding']} として読めません({e.start + 1} バイト目)"))
        return None
    nl = spec["newline"]
    body = raw.replace(nl, b"")
    if b"\r" in body or b"\n" in body or not raw.endswith(nl):
        name = "CRLF" if nl == b"\r\n" else "LF"
        errors.append((0, "E102", f"改行コードが {name} にそろっていません"))
        return None
    return raw[: -len(nl)].split(nl)


def check_csv(path):
    errors = []
    lines = read_bytes(path, CSV, errors)
    if lines is None:
        return errors
    text = "\n".join(b.decode(CSV["encoding"]) for b in lines)
    records = [(i, row) for i, row in enumerate(csv.reader(io.StringIO(text)), start=1)]
    for no, values in check_frame(records, errors):
        if values[0] != "D" or len(values) != len(ITEMS):
            errors.append((no, "E301", f"項目数が {len(ITEMS)} ではありません({len(values)})"))
            continue
        check_values(no, values, errors)
    return sorted(errors)


def check_fixed(path):
    errors = []
    lines = read_bytes(path, FIXED, errors)
    if lines is None:
        return errors
    enc = FIXED["encoding"]
    records = []
    for no, b in enumerate(lines, start=1):
        if len(b) != FIXED["record_len"]:
            errors.append((no, "E301", f"レコード長が {FIXED['record_len']} バイトではありません({len(b)} バイト)"))
            records.append((no, ["?"]))  # 件数には数え、項目の検査は行わない
            continue
        kind = b[:1].decode(enc)
        if kind == "H":
            records.append((no, ["H", b[1:7].decode(enc), b[7:21].decode(enc)]))
        elif kind == "T":
            records.append((no, ["T", b[1:8].decode(enc)]))
        else:
            values, pos = [], 0
            for name, typ, _, size, _, _ in ITEMS:
                part = b[pos : pos + size]
                pos += size
                try:
                    values.append(part.decode(enc).rstrip(" "))
                except UnicodeDecodeError:
                    errors.append((no, "E304", f"{name}: 全角文字の途中で区切られています"))
                    values = None
                    break
            if values:
                records.append((no, values))
    for no, values in check_frame(records, errors):
        if len(values) == len(ITEMS):
            check_values(no, values, errors)
    return sorted(errors)


if __name__ == "__main__":
    kind, path = sys.argv[1], sys.argv[2]
    result = check_csv(path) if kind == "csv" else check_fixed(path)
    for no, code, message in result:
        where = f"{no}行目" if no else "ファイル"
        print(f"{code} {where} {message}")
    print(f"結果: {'NG' if result else 'OK'}(エラー {len(result)} 件)")
    sys.exit(1 if result else 0)

CSVを検査した結果

python3 check_if.py csv products_20261005.csv; echo "exit=$?"
結果: OK(エラー 0 件)
exit=0

次に、不備を7つ入れたファイル(bad.csv)です。

H,IF-001,20261005020000
D,P0000101,ノート A5 40枚,110,180,20260401,,1
D,P0000102,"ボールペン 黒, 0.5mm",120,百二十,20260401,,1
D,P0000103,卓上カレンダー 2026年版,130,980,20261301,20260331,9
D,P0000104,付箋 75mm 5色セット,110,450,20261101,,3
D,P0000101,,110,200,20261101,,1
D,X0000105,消しゴム,110,90,20261101
T,5
python3 check_if.py csv bad.csv; echo "exit=$?"
E303 3行目 単価: 数字として読めません「百二十」
E303 4行目 販売開始日: 日付として読めません「20261301」
E305 5行目 取扱区分: 決められた値ではありません「3」
E302 6行目 商品名: 必須項目が空です
E306 6行目 商品コード P0000101 が 2 行目と重複しています
E301 7行目 項目数が 8 ではありません(6)
E203 8行目 トレーラーの件数「5」とデータ件数 6 が一致しません
結果: NG(エラー 7 件)
exit=1

行番号・コード・項目名・実際の値がそろっているので、この出力をそのまま連携元へ送れば、直す場所が伝わります。

文字コードと改行は、項目の検査より先に調べます。サンプルのCSVを Shift_JIS に変換したファイル(sjis.csv)と、改行を CRLF に変えたファイル(crlf.csv)で確かめました。

$ python3 check_if.py csv sjis.csv; echo "exit=$?"
E101 ファイル utf-8 として読めません(36 バイト目)
結果: NG(エラー 1 件)
exit=1

$ python3 check_if.py csv crlf.csv; echo "exit=$?"
E102 ファイル 改行コードが LF にそろっていません
結果: NG(エラー 1 件)
exit=1

CSVから固定長を作って検査した結果

固定長のサンプルは、CSVから次のコードで作りました。左詰め・スペース埋めと右詰め・ゼロ埋めを、バイト数で行うところがポイントです。csv2fixed.py として保存します。

"""商品マスターのCSV(UTF-8・LF)を固定長(Shift_JIS・CRLF・80バイト)に変換する。
使い方: python3 csv2fixed.py products_20261005.csv products_20261005.dat
"""
import csv
import sys

ENC = "cp932"


def left(value, size):
    """左詰め・半角スペース埋め(バイト数で数える)"""
    b = value.encode(ENC)
    if len(b) > size:
        raise ValueError(f"{size}バイトを超えています: {value}")
    return b + b" " * (size - len(b))


def right_zero(value, size):
    """右詰め・ゼロ埋め"""
    return value.zfill(size).encode(ENC)


with open(sys.argv[1], encoding="utf-8", newline="") as f:
    rows = list(csv.reader(f))

out = []
for r in rows:
    if r[0] == "H":
        rec = b"H" + left(r[1], 6) + left(r[2], 14) + b" " * 59
    elif r[0] == "T":
        rec = b"T" + right_zero(r[1], 7) + b" " * 72
    else:
        rec = (b"D" + left(r[1], 8) + left(r[2], 40) + left(r[3], 3) + right_zero(r[4], 7)
               + left(r[5], 8) + left(r[6], 8) + left(r[7], 1) + b" " * 4)
    assert len(rec) == 80, len(rec)
    out.append(rec)

with open(sys.argv[2], "wb") as f:
    f.write(b"\r\n".join(out) + b"\r\n")
print(f"{len(out)} レコードを書き出しました(1レコード 80 バイト+CRLF)")
python3 csv2fixed.py products_20261005.csv products_20261005.dat
python3 check_if.py fixed products_20261005.dat; echo "exit=$?"
6 レコードを書き出しました(1レコード 80 バイト+CRLF)
結果: OK(エラー 0 件)
exit=0

固定長でよくある2つの不備を入れたファイル(bad.dat)も検査しました。3行目は商品名を「40文字」になるようにスペースで埋めたもの、4行目は単価をゼロではなくスペースで右詰めにしたものです。

$ python3 check_if.py fixed bad.dat; echo "exit=$?"
E301 3行目 レコード長が 80 バイトではありません(86 バイト)
E303 4行目 単価: 数字として読めません「    980」
結果: NG(エラー 2 件)
exit=1

3行目は、全角6文字の分だけ6バイト長くなり、86バイトになっています。「文字数で埋めるか、バイト数で埋めるか」の食い違いは、レコード長の検査で見つかります。

CSVと固定長の選び方

観点 CSV 固定長
項目の区切り 区切り文字(カンマなど) 開始位置とバイト数
値の長さが変わるとき そのまま書ける 桁数に合わせて埋める
項目を足すとき 末尾に列を足す。受け取る側は項目数の検査を直す 予備の領域を使う。足りなければレコード長が変わる
人が目で見るとき 表計算ソフトで開ける 等幅フォントで桁をそろえて見る
設計書で特に決めること 囲み文字、値にカンマや改行を含むときの扱い 文字コードごとのバイト数、詰め方、埋める文字

どちらを選ぶかは、多くの場合、受け取る側のシステムが読める形式で決まります。どちらの形式でも、ヘッダー・トレーラー・文字コード・改行の4つは必ず書く、と覚えておくと抜けが出にくくなります。

つなぐ前に両者で確かめておくこと

確かめること 決めていないと起きること 設計書の記入欄
文字コードとBOMの有無 文字が読めない、先頭の項目だけ値が一致しない 文字コード
最終行の改行の有無 最後の1件が読まれない、または空の行が1件と数えられる 改行コード
値の前後のスペースの扱い 同じコードが別のものとして扱われる 項目定義の備考
数字のゼロ埋めと符号 数字として読めない 項目定義の詰め方
日付の形式と「未設定」の表し方 空・スペース・00000000 が混ざる 項目定義の値の決まり
0件のときにファイルを置くか 「0件」と「配置されていない」の区別がつかない 0件のとき
同じファイルを2回取り込んだときの結果 再送のたびにデータが重なる 全件/差分、再送
ファイルが時刻までに届かないとき 前日の内容で動かすか止めるかを、その場で相談することになる エラー時の扱い

レビュー用チェックリスト

  • [ ] インターフェース一覧に、連携元・連携先・方式・タイミングが全件分書かれている
  • [ ] ファイル名の付け方と配置先が書かれている
  • [ ] 転送の途中のファイルを読まないための取り決めがある
  • [ ] 文字コード(BOMの有無)と改行コード(最終行の扱い)が書かれている
  • [ ] ヘッダーとトレーラーの項目が定義されている
  • [ ] 項目定義に、型・桁数・必須・値の決まりがそろっている
  • [ ] 固定長では、桁数の単位(バイト)・詰め方・埋める文字が書かれている
  • [ ] キー項目と、重複したときの扱いが決まっている
  • [ ] 0件のときの動きが決まっている
  • [ ] エラーコードごとに、受け取る側の動きと連携元への依頼が決まっている
  • [ ] 再送の手順と、2回取り込んだときの結果が書かれている
  • [ ] サンプルデータが、検査のコードで「OK」になる

よくある質問

Q. インターフェース設計書と外部インターフェース仕様書は違うものですか? A. 呼び方は組織によって違いますが、書く内容は同じです。システムの外とやり取りするデータの形式と取り決めを書きます。この記事では、全体を並べた表を「インターフェース一覧」、1本ごとの取り決めを「個別定義」と呼びました。

Q. ヘッダーとトレーラーは必ず必要ですか? A. 必須の決まりではありませんが、付けておくと受け取る側が「正しい相手の、最後まで届いたファイルか」を機械的に確かめられます。上の実行例でも、件数の食い違いを E203 として見つけています。

Q. エラーが1件だけのとき、その行だけ飛ばして取り込んでもよいですか? A. どちらにするかを設計書で決めておきます。マスターのように全件がそろって意味を持つデータは「全件取り込まない」、1件ごとに独立したデータは「エラーの行だけ除いて取り込み、エラー一覧を返す」が選びやすい決め方です。

Q. 設計書の項目定義と検査のコードは、どう対応させますか? A. この記事のコードでは、項目定義を ITEMS という1つのリストにまとめました。設計書の表と1行ずつ見比べられる形にしておくと、定義を変えたときに直す場所が1か所で済みます。

まとめ

  • インターフェース設計書は「一覧」と「個別定義」の2段で書く
  • 個別定義には連携方式・タイミング・文字コード・改行・区切り・ヘッダーとトレーラー・項目定義・エラー時の扱い・再送を入れる
  • ヘッダーとトレーラー(件数)があると、ファイルの取り違えと途中で切れたファイルを機械的に見つけられる
  • 固定長は桁数をバイト数で数え、詰め方と埋める文字まで項目定義に書く
  • エラーコードは検査の段階ごとに番号帯を分け、受け取る側の動きと連携元への依頼をセットで決める
  • サンプルデータと検査のコードを設計書と一緒に渡すと、作る側が自分で定義どおりかを確かめられる

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「インターフェース設計書の書き方と項目|ファイル連携の項目定義サンプル」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?