(最終更新 2014/12/3)
【目次】
1.特殊な形式のファイル考察
2.プロセス情報の表示
3.テストの自動化
4.データベース(Oracle)との連携
5.JBA 全国銀行協会ファイルフォーマット
※この応用例は Solaris 10 for SPARC/tcsh を対象にしています。
実行例で説明していない応用的な使い方を説明します。
有編成ファイル内部の構造は仕様が公開されていない場合もあります。
この場合、通常は無編成ファイルとの相互変換ツールが付属されているはずです。
一時的に無編成ファイルに変換した後、dump_struct で扱うようにして下さい。
有編成ファイル以外でもデータを管理する領域(ヘッダやフッタ、ブロック単位でデータを管理する領域など)が確保されたファイルもあります。
ヘッダとフッタ、数レコード単位に確保された領域は dump_struct の機能で読み捨てることができます。
しかし、複数のデータ構造で、かつブロック単位にデータが格納されているような複雑なファイル形式の場合、dump_struct の機能だけでファイル全体の I/O を行うのは困難です。
この場合、データだけを抽出するツールを別に作る必要があります。
またデータを更新したい場合、書き戻しを行うツールも別に作る必要があります。
電文の内容を dump_struct で参照する場合、電文内容をファイルに保存できるようにして下さい。
データ送受信の内容を IP パケットモニタなどのツールで取得する場合、その出力は 16 進数の文字になるかと思います。
この場合、16 進数文字群をバイナリ変換できるツールを作るのが比較的簡単だと思います。
ソースコードを変更できる場合、電文送受信のラッパーAPI か電文送受信部分にファイル出力処理を追加するのが簡単そうです。
dump_struct を使えばファイルからデータベース、データベースから ファイルへの変換が比較的簡単に行えます。
データベースで使用できる SQL 言語はとても強力です。
ファイルの内容を複雑な条件で参照/更新したい場合、一時的にデータベースの表(テーブル)として取り込むことが最適なときもあります。
ただし、これらの作業は少しプログラミングが必要です。
データの内容が CSV エンコードを必要としない内容であればシェルスクリプト程度の簡単なプログラミングで十分です。
データ内容に記号「,」「"」、または改行コードがあると CSV エンコードが必要になります。この場合、C 言語などのプログラミング言語を使った方が良いでしょう。
Solaris のプロセス情報を表示してみます。
1)プロセスに関連したデータ構造は「struct psinfo」です。
「struct psinfo」は「/usr/include/sys/procfs.h」に含みます。
2)「struct psinfo」をデータ構造定義ファイルに貼り付けコメントとします。構造体「struct psinfo」をデータ構造定義ファイルに貼り付け、先頭に記号「#」を付けてコメントとします。
また関連する要素も同じようにコメントとして貼り付けます。
ここまでの内容は下記になります。
% cat psinfo.struct #/* # * process ps(1) information file. /proc//psinfo # */ ##define PRFNSZ 16 /* Maximum size of execed filename */ ##define PRARGSZ 80 /* number of chars of arguments */ #typedef struct psinfo { # int pr_flag; /* process flags (DEPRECATED; do not use) */ # int pr_nlwp; /* number of active lwps in the process */ # pid_t pr_pid; /* unique process id */ # pid_t pr_ppid; /* process id of parent */ # pid_t pr_pgid; /* pid of process group leader */ # pid_t pr_sid; /* session id */ # uid_t pr_uid; /* real user id */ # uid_t pr_euid; /* effective user id */ # gid_t pr_gid; /* real group id */ # gid_t pr_egid; /* effective group id */ # uintptr_t pr_addr; /* address of process */ # size_t pr_size; /* size of process image in Kbytes */ # size_t pr_rssize; /* resident set size in Kbytes */ # size_t pr_pad1; # dev_t pr_ttydev; /* controlling tty device (or PRNODEV) */ # /* The following percent numbers are 16-bit binary */ # /* fractions [0 .. 1] with the binary point to the */ # /* right of the high-order bit (1.0 == 0x8000) */ # ushort_t pr_pctcpu; /* % of recent cpu time used by all lwps */ # ushort_t pr_pctmem; /* % of system memory used by process */ # timestruc_t pr_start; /* process start time, from the epoch */ #typedef struct timespec { /* definition per POSIX.4 */ # time_t tv_sec; /* seconds */ # long tv_nsec; /* and nanoseconds */ #} timespec_t; # timestruc_t pr_time; /* usr+sys cpu time for this process */ # timestruc_t pr_ctime; /* usr+sys cpu time for reaped children */ # char pr_fname[PRFNSZ]; /* name of execed file */ # char pr_psargs[PRARGSZ]; /* initial characters of arg list */ # int pr_wstat; /* if zombie, the wait() status */ # int pr_argc; /* initial argument count */ # uintptr_t pr_argv; /* address of initial argument vector */ # uintptr_t pr_envp; /* address of initial environment vector */ # char pr_dmodel; /* data model of the process */ # char pr_pad2[3]; # taskid_t pr_taskid; /* task id */ # projid_t pr_projid; /* project id */ # int pr_nzomb; /* number of zombie lwps in the process */ # poolid_t pr_poolid; /* pool id */ # zoneid_t pr_zoneid; /* zone id */ # id_t pr_contract; /* process contract */ # int pr_filler[1]; /* reserved for future use */ # lwpsinfo_t pr_lwp; /* information for representative lwp */ #} psinfo_t;
3)dump_struct の型を 1 行づつ埋めていきます。完成したデータ構造定義ファイルの内容は下記になります。
% cat psinfo.struct #/* # * process ps(1) information file. /proc//psinfo # */ ##define PRFNSZ 16 /* Maximum size of execed filename */ ##define PRARGSZ 80 /* number of chars of arguments */ #typedef struct psinfo { # int pr_flag; /* process flags (DEPRECATED; do not use) */ process flags,hex,4 # int pr_nlwp; /* number of active lwps in the process */ lwps,int,4 # pid_t pr_pid; /* unique process id */ process id,int,4 # pid_t pr_ppid; /* process id of parent */ process id of parent,int,4 # pid_t pr_pgid; /* pid of process group leader */ pid of process group leader,int,4 # pid_t pr_sid; /* session id */ session id,int,4 # uid_t pr_uid; /* real user id */ real user id,int,4 # uid_t pr_euid; /* effective user id */ effective user id,int,4 # gid_t pr_gid; /* real group id */ real group id,int,4 # gid_t pr_egid; /* effective group id */ effective group id,int,4 # uintptr_t pr_addr; /* address of process */ address of process,hex,4 # size_t pr_size; /* size of process image in Kbytes */ size of process image in Kbytes,uint,4 # size_t pr_rssize; /* resident set size in Kbytes */ resident set size in Kbytes,uint,4 # size_t pr_pad1; # この pr_pad1 は予備だと思いますので読み捨てます。
,hidden,4 # dev_t pr_ttydev; /* controlling tty device (or PRNODEV) */ # /* The following percent numbers are 16-bit binary */ # /* fractions [0 .. 1] with the binary point to the */ # /* right of the high-order bit (1.0 == 0x8000) */ controlling tty device,uint,4 # ushort_t pr_pctcpu; /* % of recent cpu time used by all lwps */ percent of recent cpu time used by all lwps,ushort,2 # ushort_t pr_pctmem; /* % of system memory used by process */ percent of system memory used by process,ushort,2 # timestruc_t pr_start; /* process start time, from the epoch */ #typedef struct timespec { /* definition per POSIX.4 */ # time_t tv_sec; /* seconds */ # 記号「,」はセパレータのため「.」に変更します。 process start time(epoch). sec,int,4 # long tv_nsec; /* and nanoseconds */ process start time(epoch). nanosec,int,4 #} timespec_t; # timestruc_t pr_time; /* usr+sys cpu time for this process */ #typedef struct timespec { /* definition per POSIX.4 */ # time_t tv_sec; /* seconds */ usr+sys cpu time. sec,int,4 # long tv_nsec; /* and nanoseconds */ usr+sys cpu time. nanosec,int,4 #} timespec_t; # timestruc_t pr_ctime; /* usr+sys cpu time for reaped children */ #typedef struct timespec { /* definition per POSIX.4 */ # time_t tv_sec; /* seconds */ usr+sys cpu time children. sec,int,4 # long tv_nsec; /* and nanoseconds */ usr+sys cpu time children. nanosec,int,4 #} timespec_t; # char pr_fname[PRFNSZ]; /* name of execed file */ # struct psinfo ではプロセス(コマンド)名の先頭 16 バイトまで取得できます。 name of execed file,euc,16 # char pr_psargs[PRARGSZ]; /* initial characters of arg list */ # struct psinfo ではパラメータの先頭 80 バイトまで取得できます。 initial characters of arg list,euc,80 # int pr_wstat; /* if zombie, the wait() status */ wait() status,int,4 # int pr_argc; /* initial argument count */ initial argument count,int,4 # uintptr_t pr_argv; /* address of initial argument vector */ address of initial argument vector,hex,4 # uintptr_t pr_envp; /* address of initial environment vector */ address of initial environment vector,hex,4 # char pr_dmodel; /* data model of the process */ data model of the process,hex,1 # char pr_pad2[3]; ,hidden,3 # taskid_t pr_taskid; /* task id */ task id,int,4 # projid_t pr_projid; /* project id */ project id,int,4 # int pr_nzomb; /* number of zombie lwps in the process */ number of zombie lwps in the process,int,4 # poolid_t pr_poolid; /* pool id */ pool id,int,4 # zoneid_t pr_zoneid; /* zone id */ zone id,int,4 # id_t pr_contract; /* process contract */ process contract,int,4 # int pr_filler[1]; /* reserved for future use */ ,hidden,4 # lwpsinfo_t pr_lwp; /* information for representative lwp */ ,hidden,104 #} psinfo_t;
4)dump_struct でプロセス情報を表示してみます。プロセスの情報は /proc/{PID}/psinfo ファイルに保存されています。
ここではログインシェルの情報を表示してみます。
% dump_struct psinfo.struct < /proc/$$/psinfo 1,1,1,1,process flags,hex,4,02 00 00 00 1,2,5,5,lwps,int,4,1 1,3,9,9,process id,int,4,838 1,4,13,13,process id of parent,int,4,835 1,5,17,17,pid of process group leader,int,4,838 1,6,21,21,session id,int,4,838 1,7,25,25,real user id,int,4,1001 1,8,29,29,effective user id,int,4,1001 1,9,33,33,real group id,int,4,1 1,10,37,37,effective group id,int,4,1 1,11,41,41,address of process,hex,4,00 00 00 00 1,12,45,45,size of process image in Kbytes,uint,4,3936 1,13,49,49,resident set size in Kbytes,uint,4,2720 1,14,53,53,controlling tty device,uint,4,6291462 1,15,57,57,percent of recent cpu time used by all lwps,ushort,2,1 1,16,59,59,percent of system memory used by process,ushort,2,10 1,17,61,61,process start time(epoch). sec,int,4,1250257032 1,18,65,65,process start time(epoch). nanosec,int,4,64549324 1,19,69,69,usr+sys cpu time. sec,int,4,0 1,20,73,73,usr+sys cpu time. nanosec,int,4,166708082 1,21,77,77,usr+sys cpu time children. sec,int,4,4 1,22,81,81,usr+sys cpu time children. nanosec,int,4,540000000 1,23,85,85,name of execed file,euc,16,tcsh 1,24,101,101,initial characters of arg list,euc,80,-tcsh 1,25,181,181,wait() status,int,4,0 1,26,185,185,initial argument count,int,4,1 1,27,189,189,address of initial argument vector,hex,4,FF BF FE BC 1,28,193,193,address of initial environment vector,hex,4,FF BF FE C4 1,29,197,197,data model of the process,hex,1,01 1,30,198,198,task id,int,4,80 1,31,202,202,project id,int,4,3 1,32,206,206,number of zombie lwps in the process,int,4,0 1,33,210,210,pool id,int,4,0 1,34,214,214,zone id,int,4,0 1,35,218,218,process contract,int,4,99
Solaris 10 for x86 でも表示させてみます。
x86% dump_struct psinfo.struct < /proc/$$/psinfo 1,1,1,1,process flags,hex,4,00 00 00 02 1,2,5,5,lwps,int,4,1 1,3,9,9,process id,int,4,769 1,4,13,13,process id of parent,int,4,766 1,5,17,17,pid of process group leader,int,4,769 1,6,21,21,session id,int,4,769 1,7,25,25,real user id,int,4,1001 1,8,29,29,effective user id,int,4,1001 1,9,33,33,real group id,int,4,1 1,10,37,37,effective group id,int,4,1 1,11,41,41,address of process,hex,4,00 00 00 00 1,12,45,45,size of process image in Kbytes,uint,4,3520 1,13,49,49,resident set size in Kbytes,uint,4,2168 1,14,53,53,controlling tty device,uint,4,6291458 1,15,57,57,percent of recent cpu time used by all lwps,ushort,2,7 1,16,59,59,percent of system memory used by process,ushort,2,68 1,17,61,61,process start time(epoch). sec,int,4,1250300019 1,18,65,65,process start time(epoch). nanosec,int,4,369199308 1,19,69,69,usr+sys cpu time. sec,int,4,0 1,20,73,73,usr+sys cpu time. nanosec,int,4,191165529 1,21,77,77,usr+sys cpu time children. sec,int,4,0 1,22,81,81,usr+sys cpu time children. nanosec,int,4,30000000 1,23,85,85,name of execed file,euc,16,tcsh 1,24,101,101,initial characters of arg list,euc,80,-tcsh 1,25,181,181,wait() status,int,4,0 1,26,185,185,initial argument count,int,4,1 1,27,189,189,address of initial argument vector,hex,4,D0 7E 04 08 1,28,193,193,address of initial environment vector,hex,4,D8 7E 04 08 1,29,197,197,data model of the process,hex,1,01 1,30,198,198,task id,int,4,73 1,31,202,202,project id,int,4,3 1,32,206,206,number of zombie lwps in the process,int,4,0 1,33,210,210,pool id,int,4,0 1,34,214,214,zone id,int,4,0 1,35,218,218,process contract,int,4,95
問題なく表示でき、内容についても期待したものでした。
ソフトウェアの寿命は予想以上に長く 5 年 10 年は当たり前です。
改造の度、改造部分のテスト、および既存部分のデグレードテストが必要になります。
テスト環境の構築、テストデータの作成、ソフトウェアの実行と結果の検証、どれも簡単なことではありません。
長期に何度も行われるテスト作業をどれだけ効率良く実施できるか考えてみます。
それでは具体的な内容を決めていきます。
入力データ構造の仕様、下記のようにしてみました。
テキストエディタでは扱えないバイナリデータです。
% vi apply030000in.struct
ID,euc,8
値 1,int,4
値 2,int,4
出力データ構造の仕様、下記のようにしてみました。
% vi apply030000out.struct
ID,euc,8
加算結果,llong,8
減算結果,llong,8
ソフトウェアの仕様は、下記のように決めます。
出力.ID = 入力.ID
出力.加算結果 = 入力.値 1 + 入力.値 2
出力.減算結果 = 入力.値 1 - 入力.値 2
これを実現する手段は何でも良いのですが、ここでは C 言語で書いてみました。
---------- apply030000.c ---------- ※エラー制御無し。 #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_VALUES 2 typedef enum { RESULT_ADD = 0, RESULT_SUB, MAX_RESULTS } result_t; int main(int argc, char **argv) { struct { char id[8]; int values[MAX_VALUES]; } data_in; struct { char id[8]; long long results[MAX_RESULTS]; } data_out; int n, i; int recs; for (recs = 0;; ++recs) { n = fread(&data_in, sizeof(data_in), 1, stdin); if (n != 1) { break; } memset(&data_out, 0x00, sizeof(data_out)); memcpy( data_out.id, data_in.id, sizeof(data_out.id) < sizeof(data_in.id) ? sizeof(data_out.id): sizeof(data_in.id)); for (i = 0; i < MAX_VALUES; ++i) { if (i == 0) { data_out.results[RESULT_ADD] = (long long)data_in.values[i]; data_out.results[RESULT_SUB] = (long long)data_in.values[i]; } else { data_out.results[RESULT_ADD] += (long long)data_in.values[i]; data_out.results[RESULT_SUB] -= (long long)data_in.values[i]; } } (void)fwrite(&data_out, sizeof(data_out), 1, stdout); } (void)fprintf(stderr, "%d recs\n", recs); return 0; } ---------- ※この程度なのでコメントは付けません。
それではテストパターン(テスト項目)を考えます。
ただし、例外(エラー)系のテスト項目はここでは扱いません。
必要な要素は int 型の最小/0/最大、中間値があれば十分でしょう。
簡単なプログラムですので組み合わせも必要最小限にします。
最小,最小
最小,0
最小,最大
0,最小
0,0
0,最大
最大,最小
最大,0
最大,最大
中間,中間
中間,-中間
-中間,中間
-中間,-中間
ID は -mstt_value で埋めますが、値をここまで厳密に決めると -mstt_value では役不足です。仮値定義をデータ構造定義ファイルに定義します。
% vi apply030000in.struct ※途中改行しています。実際は3行です。
ID,euc,8
値1,int,4,,=:-2147483648:-2147483648:-2147483648:0:0:0:2147483647
:2147483647:2147483647:1073741823:1073741823:-1073741823:-1073741823
値2,int,4,,=:-2147483648:0:2147483647:-2147483648:0:2147483647:
-2147483648:0:2147483647:1073741823:-1073741823:1073741823:-1073741823
dump_struct がどのような文字データを生成するか確認します。
% dump_struct -mode struct_to_tmp -mstt_value meter2 -mstt_max 13 apply030000in.struct
1,1,,1,ID,euc,8,12345679
1,2,,9,値1,int,4,-2147483648
1,3,,13,値2,int,4,-2147483648
2,1,,1,ID,euc,8,22345679
2,2,,9,値1,int,4,-2147483648
2,3,,13,値2,int,4,0
(中略)
12,1,,1,ID,euc,8,22345679
12,2,,9,値1,int,4,-1073741823
12,3,,13,値2,int,4,1073741823
13,1,,1,ID,euc,8,32345679
13,2,,9,値1,int,4,-1073741823
13,3,,13,値2,int,4,-1073741823
中間ファイルを作らないで結果を得るためには下記のように行いますが、テストが目的なら残すべきでしょう。(障害解析に中間ファイルが必要な場合があるため)
% gcc apply030000.c
% dump_struct \
-mode struct_to_tmp -mstt_value meter2 -mstt_max 13 apply030000in.struct \
| dump_struct -mode char_to_data -mctd_auto_pos \
| ./a.out \
| dump_struct apply030000out.struct
1,1,1,1,ID,euc,8,12345679
1,2,9,9,加算結果,llong,8,-4294967296
1,3,17,17,減算結果,llong,8,0
2,1,25,1,ID,euc,8,22345679
2,2,33,9,加算結果,llong,8,-2147483648
2,3,41,17,減算結果,llong,8,-2147483648
(中略)
12,1,265,1,ID,euc,8,22345679
12,2,273,9,加算結果,llong,8,0
12,3,281,17,減算結果,llong,8,-2147483646
13,1,289,1,ID,euc,8,32345679
13,2,297,9,加算結果,llong,8,-2147483646
13,3,305,17,減算結果,llong,8,0
それではエビデンス(証拠)として残す情報とその取得手段を考えます。
なお多くのプログラムで同じですが、このプログラムの動作保障に必要な情報は入力データと出力データです。
・入力データファイル
・入力データの 16 進数ダンプ(OS 標準 od を使用)
・入力データの dump_struct 文字データ(目視による結果検証用)
・出力データファイル
・出力データの 16 進数ダンプ(OS 標準 od を使用)
・出力データの dump_struct 文字データ(目視による結果検証用)
これを実現するためのスクリプトを組み、これも残します。
なお当方は tcsh を使いますので、bash(sh)と比べ関数がエイリアスでしか作れません。規模が大きくなると少し読みにくくなりますがご了承下さい。
% vi apply030000 ※エラー制御無し。
#!/bin/tcsh -f
set BN = `/bin/basename "$0"`
set Exe = ./a.out
set In = $BN.in
set In16 = $BN.in16
set InChar = $BN.in_char
set Out = $BN.out
set Out16 = $BN.out16
set OutChar = $BN.out_char
# 入力データ
dump_struct \
-mode struct_to_tmp -mstt_value meter2 -mstt_max 13 apply030000in.struct \
| dump_struct -mode char_to_data -mctd_auto_pos \
> "$In"
/usr/bin/od -t x1 "$In" > "$In16"
dump_struct apply030000in.struct < "$In" > "$InChar"
echo "% cat $InChar"
/usr/bin/cat "$InChar"
# テスト対象プログラム実行
echo "% $Exe < $In > $Out"
$Exe < "$In" > "$Out"
set Status = $status
if ($Status != 0) then
echo "${BN}:エラー: $Exe でエラーが発生しました - $Status"
exit $Status
endif
# 出力データ
/usr/bin/od -t x1 "$Out" > "$Out16"
dump_struct apply030000out.struct < "$Out" > "$OutChar"
echo "% cat $OutChar"
/usr/bin/cat "$OutChar"
exit 0
実行権を付与し実行します。
% chmod +x apply030000
% ./apply030000
% cat apply030000.in_char
1,1,1,1,ID,euc,8,12345679
1,2,9,9,値 1,int,4,-2147483648
1,3,13,13,値 2,int,4,-2147483648
2,1,17,1,ID,euc,8,22345679
2,2,25,9,値 1,int,4,-2147483648
2,3,29,13,値 2,int,4,0
3,1,33,1,ID,euc,8,32345679
3,2,41,9,値 1,int,4,-2147483648
3,3,45,13,値 2,int,4,2147483647
4,1,49,1,ID,euc,8,42345679
4,2,57,9,値 1,int,4,0
4,3,61,13,値 2,int,4,-2147483648
5,1,65,1,ID,euc,8,52345679
5,2,73,9,値 1,int,4,0
5,3,77,13,値 2,int,4,0
6,1,81,1,ID,euc,8,62345679
6,2,89,9,値 1,int,4,0
6,3,93,13,値 2,int,4,2147483647
7,1,97,1,ID,euc,8,72345679
7,2,105,9,値 1,int,4,2147483647
7,3,109,13,値 2,int,4,-2147483648
8,1,113,1,ID,euc,8,82345679
8,2,121,9,値 1,int,4,2147483647
8,3,125,13,値 2,int,4,0
9,1,129,1,ID,euc,8,92345679
9,2,137,9,値 1,int,4,2147483647
9,3,141,13,値 2,int,4,2147483647
10,1,145,1,ID,euc,8,02345679
10,2,153,9,値 1,int,4,1073741823
10,3,157,13,値 2,int,4,1073741823
11,1,161,1,ID,euc,8,12345679
11,2,169,9,値 1,int,4,1073741823
11,3,173,13,値 2,int,4,-1073741823
12,1,177,1,ID,euc,8,22345679
12,2,185,9,値 1,int,4,-1073741823
12,3,189,13,値 2,int,4,1073741823
13,1,193,1,ID,euc,8,32345679
13,2,201,9,値 1,int,4,-1073741823
13,3,205,13,値 2,int,4,-1073741823
% ./a.out < apply030000.in > apply030000.out
13 recs
% cat apply030000.out_char
1,1,1,1,ID,euc,8,12345679
1,2,9,9,加算結果,llong,8,-4294967296
1,3,17,17,減算結果,llong,8,0
2,1,25,1,ID,euc,8,22345679
2,2,33,9,加算結果,llong,8,-2147483648
2,3,41,17,減算結果,llong,8,-2147483648
3,1,49,1,ID,euc,8,32345679
3,2,57,9,加算結果,llong,8,-1
3,3,65,17,減算結果,llong,8,-4294967295
4,1,73,1,ID,euc,8,42345679
4,2,81,9,加算結果,llong,8,-2147483648
4,3,89,17,減算結果,llong,8,2147483648
5,1,97,1,ID,euc,8,52345679
5,2,105,9,加算結果,llong,8,0
5,3,113,17,減算結果,llong,8,0
6,1,121,1,ID,euc,8,62345679
6,2,129,9,加算結果,llong,8,2147483647
6,3,137,17,減算結果,llong,8,-2147483647
7,1,145,1,ID,euc,8,72345679
7,2,153,9,加算結果,llong,8,-1
7,3,161,17,減算結果,llong,8,4294967295
8,1,169,1,ID,euc,8,82345679
8,2,177,9,加算結果,llong,8,2147483647
8,3,185,17,減算結果,llong,8,2147483647
9,1,193,1,ID,euc,8,92345679
9,2,201,9,加算結果,llong,8,4294967294
9,3,209,17,減算結果,llong,8,0
10,1,217,1,ID,euc,8,02345679
10,2,225,9,加算結果,llong,8,2147483646
10,3,233,17,減算結果,llong,8,0
11,1,241,1,ID,euc,8,12345679
11,2,249,9,加算結果,llong,8,0
11,3,257,17,減算結果,llong,8,2147483646
12,1,265,1,ID,euc,8,22345679
12,2,273,9,加算結果,llong,8,0
12,3,281,17,減算結果,llong,8,-2147483646
13,1,289,1,ID,euc,8,32345679
13,2,297,9,加算結果,llong,8,-2147483646
13,3,305,17,減算結果,llong,8,0
初回の結果検証は目視、量が多い場合は検証用プログラムを組み合わせるのが良いと思います。
入出力に使った下記のファイルはエビデンス(証拠)です。
・apply030000.in16
・apply030000.in_char
・apply030000.out16
・apply030000.out_char
od コマンドによる 16 進数ダンプの内容は下記になります。これだけで結果検証するのは非効率です。
% cat apply030000.in16 0000000 31 32 33 34 35 36 37 39 80 00 00 00 80 00 00 00 0000020 32 32 33 34 35 36 37 39 80 00 00 00 00 00 00 00 0000040 33 32 33 34 35 36 37 39 80 00 00 00 7f ff ff ff 0000060 34 32 33 34 35 36 37 39 00 00 00 00 80 00 00 00 0000100 35 32 33 34 35 36 37 39 00 00 00 00 00 00 00 00 0000120 36 32 33 34 35 36 37 39 00 00 00 00 7f ff ff ff 0000140 37 32 33 34 35 36 37 39 7f ff ff ff 80 00 00 00 0000160 38 32 33 34 35 36 37 39 7f ff ff ff 00 00 00 00 0000200 39 32 33 34 35 36 37 39 7f ff ff ff 7f ff ff ff 0000220 30 32 33 34 35 36 37 39 3f ff ff ff 3f ff ff ff 0000240 31 32 33 34 35 36 37 39 3f ff ff ff c0 00 00 01 0000260 32 32 33 34 35 36 37 39 c0 00 00 01 3f ff ff ff 0000300 33 32 33 34 35 36 37 39 c0 00 00 01 c0 00 00 01 0000320 % cat apply030000.out16 0000000 31 32 33 34 35 36 37 39 ff ff ff ff 00 00 00 00 0000020 00 00 00 00 00 00 00 00 32 32 33 34 35 36 37 39 0000040 ff ff ff ff 80 00 00 00 ff ff ff ff 80 00 00 00 0000060 33 32 33 34 35 36 37 39 ff ff ff ff ff ff ff ff 0000100 ff ff ff ff 00 00 00 01 34 32 33 34 35 36 37 39 0000120 ff ff ff ff 80 00 00 00 00 00 00 00 80 00 00 00 0000140 35 32 33 34 35 36 37 39 00 00 00 00 00 00 00 00 0000160 00 00 00 00 00 00 00 00 36 32 33 34 35 36 37 39 0000200 00 00 00 00 7f ff ff ff ff ff ff ff 80 00 00 01 0000220 37 32 33 34 35 36 37 39 ff ff ff ff ff ff ff ff 0000240 00 00 00 00 ff ff ff ff 38 32 33 34 35 36 37 39 0000260 00 00 00 00 7f ff ff ff 00 00 00 00 7f ff ff ff 0000300 39 32 33 34 35 36 37 39 00 00 00 00 ff ff ff fe 0000320 00 00 00 00 00 00 00 00 30 32 33 34 35 36 37 39 0000340 00 00 00 00 7f ff ff fe 00 00 00 00 00 00 00 00 0000360 31 32 33 34 35 36 37 39 00 00 00 00 00 00 00 00 0000400 00 00 00 00 7f ff ff fe 32 32 33 34 35 36 37 39 0000420 00 00 00 00 00 00 00 00 ff ff ff ff 80 00 00 02 0000440 33 32 33 34 35 36 37 39 ff ff ff ff 80 00 00 02 0000460 00 00 00 00 00 00 00 00 0000470それでは次に進みます。このままでは少しもラクができません。
テスト用スクリプト apply030000 は残っていますが、それだけでは次回また同じ目視による結果検証をデグレード確認として実施しなければいけなくなります。
入力データと出力データ、上記 6 種類のファイルを結果検証済みエビデンスとして保存します。
結果検証済みのファイルと実行する度に生成されるファイルの比較を自動化します。
% vi apply030000
(中略)
/usr/bin/cmp master/"$In" "$In"
/usr/bin/diff master/"$In16" "$In16"
/usr/bin/diff master/"$InChar" "$InChar"
/usr/bin/cmp master/"$Out" "$Out"
/usr/bin/diff master/"$Out16" "$Out16"
/usr/bin/diff master/"$OutChar" "$OutChar"
exit 0
結果検証済みのファイルをディレクトリ master にコピーします。
apply030000 を実行するたびに生成されるファイルを master 内のファイルと自動比較します。
これでデグレードの検出が自動化されます。
しかし、もっと便利にできます。
今後 apply030000 を実行したとき、端末に表示される内容を 2 度と見ないで済むような仕組みです。
この実現は外部にもう 1 つ別のテスト実施スクリプトを作ることで行います。
% vi auto_test
#!/bin/tcsh -f
set BN = `/bin/basename "$0"`
foreach T (apply??????)
if (! -x $T) continue
echo "${BN}: $T"
./$T >& $T.log
if ($status != 0) then
echo "${BN}:エラー: テスト $T でエラーが発生しました。"
/usr/bin/cat $T.log
exit 1
endif
/usr/bin/diff master/$T.log $T.log
if ($status != 0) then
echo "${BN}:エラー: $T の実施で差分が発生しています。"
echo "% cat master/$T.log"
/usr/bin/cat master/$T.log
echo "% cat $T.log"
/usr/bin/cat $T.log
exit 1
endif
end
exit 0
% chmod +x auto_test
1 回目の実行はログが保存されていませんのでエラーになります。
% ./auto_test
auto_test: apply030000
diff: master/apply030000.log: ファイルもディレクトリもありません。
auto_test:エラー: apply030000 の実施で差分が発生しています。
% cat master/apply030000.log
cat: master/apply030000.log をオープンできません。
% cat apply030000.log
% cat apply030000.in_char
1,1,1,1,ID,euc,8,12345679
1,2,9,9,値 1,int,4,-2147483648
1,3,13,13,値 2,int,4,-2147483648
(中略)
13,1,289,1,ID,euc,8,32345679
13,2,297,9,加算結果,llong,8,-2147483646
13,3,305,17,減算結果,llong,8,0
apply030000.log を master にコピーして下さい。
これで apply?????? のパターンに一致するファイル=テストがすべて実施されます。端末に表示される内容もすべて master と比較されます。
画面に差分が表示されていないことの目視で、一瞬にして結果検証が終わります。
しかし、これでもまだ不便です。
apply030000 内に master との差分取得を書いてしまっては、次のテスト項目に合わせたスクリプトを作るとき、また同じようなことを書かないといけないからです。
これを防ぐためには apply030000 内の cmp/diff をすべて auto_test に移動します。
ファイル名については一定のルールを決めれば済みます。
% vi auto_test
#!/bin/tcsh -f
set BN = `/bin/basename "$0"`
foreach T (apply??????)
if (! -x $T) continue
echo "${BN}: $T"
./$T >& $T.log
if ($status != 0) then
echo "${BN}:エラー: テスト $T でエラーが発生しました。"
/usr/bin/cat $T.log
exit 1
endif
/usr/bin/diff master/$T.log $T.log
if ($status != 0) then
echo "${BN}:エラー: $T の実施で差分が発生しました。"
echo "% cat master/$T.log"
/usr/bin/cat master/$T.log
echo "% cat $T.log"
/usr/bin/cat $T.log
exit 1
endif
set B = `/bin/basename "$T"`
set In = $B.in
set In16 = $B.in16
set InChar = $B.in_char
set Out = $B.out
set Out16 = $B.out16
set OutChar = $B.out_char
/usr/bin/cmp master/"$In" "$In"
set S1 = $status
/usr/bin/diff master/"$In16" "$In16"
set S2 = $status
/usr/bin/diff master/"$InChar" "$InChar"
set S3 = $status
/usr/bin/cmp master/"$Out" "$Out"
set S4 = $status
/usr/bin/diff master/"$Out16" "$Out16"
set S5 = $status
/usr/bin/diff master/"$OutChar" "$OutChar"
set S6 = $status
if ($S1 != 0 || $S2 != 0 || $S3 != 0 || \
$S4 != 0 || $S5 != 0 || $S6 != 0) \
then
echo "${BN}:エラー: $T の実施で差分が発生しました。"
echo "% cat master/$InChar"
/usr/bin/cat "master/$InChar"
echo "% cat $InChar"
/usr/bin/cat "$InChar"
endif
end
exit 0
実行結果は下記のようになります。
% ./auto_test
auto_test: apply030000
テスト項目を追加すると下記のようになるでしょう。
% ./auto_test
auto_test: apply030000
auto_test: apply040100
auto_test: apply040200
もし apply030000 で差分が検出されれば下記のようになります。
% ./auto_test
auto_test: apply030000
69,70c69
< 差分が発生するよう、下記の値を 4294967294 から 5294967294 に変更しました。
< 9,2,201,9,加算結果,llong,8,5294967294
---
> 9,2,201,9,加算結果,llong,8,4294967294
auto_test:エラー: apply030000 の実施で差分が発生しました。
% cat master/apply030000.log
(中略)
13,3,305,17,減算結果,llong,8,0
なお、差分を検出したときは即座にテスト継続を中止すべきです。
auto_test は下記のように修正した方が良いでしょう。
---------- auto_test ----------
if ($S1 != 0 || $S2 != 0 || $S3 != 0 || \
$S4 != 0 || $S5 != 0 || $S6 != 0) \
then
echo "${BN}:エラー: $T の実施で差分が発生しました。"
echo "% cat master/$InChar"
/usr/bin/cat "master/$InChar"
echo "% cat $InChar"
/usr/bin/cat "$InChar"
exit 1
endif
end
echo "${BN}: デグレードは発生していません"
exit 0
----------
差分が発生した場合、それが仕様変更による当然の結果なのか、それともデグレードを発生させてしまったのかは、表示されるログをじっくり見て判断する必要があります。
仕様変更による差分なら、表示されたログを良く見て期待する結果であるか検証して下さい。
期待の通りであれば、そのログ一式を master にコピーして下さい。
auto_test をもう一度実行し、差分がなくなっていることを確認して下さい。
デグレードを発生させてしまっているようなら、プログラムを修正して下さい。
差分が無ければ最後に「auto_test: デグレードは発生していません」が表示されます。これが表示されるまで auto_test を繰り返し実行することになります。
なおプログラムの改造を行ったのであれば、通常はテストスクリプトの追加が必要です。
※dump_struct は約 300 個の tcsh スクリプトでテストを実施しています。
もちろん過去の結果との差分検出機能を実装してありますので、何かの改造を行ったとしてもデグレードの発生を見落とすことはまずありません。
データベース(RDBMS)とファイルの連携を考えます。
試用するデータベースはオラクル(株)の Oracle 11g Enterprise Edition 64 ビット版です。
これを Solaris for SPARC 64 ビット版の上で使います。
ファイルをデータベースの表(テーブル)として取り込むことができれば強力な SQL 文が使えるため、データの操作がラクになります。(以降「データベース化」とします)
また、一度データベース化したデータをファイルに戻すことも行ってみます。(以降「ファイル化」とします)
※dump_struct はデータベースとの連携機能がありますが、今のところ Oracle 専用となっています。
他のデータベースをご利用のときは出力内容の修正が必要な場合があります。
ファイルをデータベースに取り込むためには、まず dump_struct を使ってデータを CSV 形式に変換します。
次に dump_struct で生成した SQL 文でデータベースに表を生成します。
最後に CSV 形式に変換したデータをデータベースに取り込みます。
ここでは dump_struct のテスト用スクリプトの一部を、ほぼそのまま公開します。
まずは dump_struct のテストで共通に使っているスクリプトです。
このスクリプトは単体で実行するのではなく、他のテスト用スクリプトから読み込まれます。
% cat ../common/sets ※一部省略。
alias ds "../../dump_struct"
set BN = `/bin/basename "$0"`
set DN = `/bin/basename "$PWD"`
set C_OS = `/bin/uname`
set C_Rev = `/bin/uname -r`
if ("$C_OS" != "SunOS") then
set C_CPU = "i386"
else
set C_CPU = `/bin/uname -p`
endif
set DBConnect = "scott/tiger"
alias sps "sqlplus -s $DBConnect"
alias sl "sqlldr $DBConnect"
alias s2c "sql2csv $DBConnect"
次はデータベース系テストの 1 つめ、t010101 と言うファイルです。
余談ですが 010101 はテストの項目番号 1-1-1 に相当します。
% cat t010101
#!/bin/tcsh -f
source ../common/sets
# データ構造定義ファイル内容表示
echo "% cat $BN.struct"
/bin/cat $BN.struct
# 仮文字データ生成
echo -n "% dump_struct -mode struct_to_tmp"
echo " -mstt_value meter3 -mstt_max 3 $BN.struct > $BN.char"
ds -mode struct_to_tmp -mstt_value meter3 -mstt_max 3 $BN.struct > $BN.char
# 仮文字データからデータ生成
echo "% dump_struct -mode char_to_data -mctd_auto_pos < $BN.char > $BN.data"
ds -mode char_to_data -mctd_auto_pos < $BN.char > $BN.data
# データ内容表示
echo "% dump_struct $BN.struct < $BN.data"
ds $BN.struct < $BN.data
# データの CSV(文字)化
echo "% dump_struct $BN.struct -mdtc_value_only -rec_line < $BN.data > $BN.csv"
ds $BN.struct -mdtc_value_only -rec_line < $BN.data > $BN.csv
# CSV 形式データ内容表示
echo "% cat $BN.csv"
/bin/cat $BN.csv
# RDB テーブル作成 SQL 生成
echo "% dump_struct -mode struct_to_sql $BN.struct > $BN.sql"
ds -mode struct_to_sql $BN.struct > $BN.sql
# RDB テーブル作成 SQL 内容表示
echo "% cat $BN.sql"
/bin/cat $BN.sql
# sqlplus による RDB テーブル作成 ※エラーは無視
echo "% sps < $BN.sql"
sps << EOF >& /dev/null
set feedback off
-- 【注意】テーブル内容の削除
--
-- データのみ削除
--truncate table $BN;
-- テーブルごと削除 ※カラム名などを変更した場合はこちらで。
drop table $BN;
@$BN.sql
EOF
# RDB テーブル構造表示
/bin/cat << EOF
% sps
desc $BN
EOF
sps << EOF
desc $BN
EOF
# sqlldr コントロールファイル生成
echo "% dump_struct -mode struct_to_sql $BN.struct -msts_mode loader > $BN.ctl"
ds -mode struct_to_sql $BN.struct -msts_mode loader > $BN.ctl
# sqlldr コントロールファイル内容表示
echo "% cat $BN.ctl"
/bin/cat $BN.ctl
# sqlldr によるデータ登録
echo "% sqlldr control=$BN.ctl log=$BN.log"
sl control=$BN.ctl log=$BN.log > /dev/null
# RDB データ内容表示
/bin/cat << EOFF
% sps << EOF
set colsep '|'
set recsepchar '-'
set pagesize 0
set feedback off
select * from $BN;
EOF
EOFF
sps << EOF
set colsep '|'
set recsepchar '-'
set pagesize 0
set feedback off
select * from $BN;
EOF
exit 0
内容を簡単に説明します。
この dump_struct テスト用スクリプトは順番に下記の処理を行っています。
・データ構造の表示
テストに使うデータの構造、データベースでは表の構造です。・仮文字データの生成
・仮文字データからデータの生成
この 2 つで内容を問わないテストデータファイルを生成しています。・データ内容の表示
・データの CSV(文字)化
・CSV 形式データ内容の表示
データベースにファイルを取り込む形式として、データを 1 行に 1 レコード CSV 形式に変換しています。・RDB テーブル作成 SQL の生成
なお -mdtc_value_only を付けるとデータの内容、値のみが文字として出力されます。
・RDB テーブル作成 SQL 内容の表示
データ構造定義ファイルに合わせたデータベース表作成 SQL を生成しています。・sqlplus による RDB テーブルの作成
プライマリキーなどの制約を追加する場合、このファイルを修正する必要があります。
このテスト用スクリプトは dump_struct の新しいバージョンをリリースする前に毎回実行しますので、古いテーブルは破棄(drop)しています。・RDB テーブル構造の表示
期待する表が作られているか確認するため、これを表示しています。・sqlldr コントロールファイルの生成
・sqlldr コントロールファイル内容の表示
Oracle に CSV 形式のファイルからデータを取り込むには sqlldr を使いますので、必要なコントロールファイルを生成しています。・sqlldr によるデータの登録
オプションを調整する必要があれば、このファイルを修正して下さい。
・RDB データ内容の表示
データベースに登録したデータ内容を表示しています。これを実行すると結果検証を行うための情報がすべて表示されます。
入力である「データ内容の表示」と一致していることが確認できます。
% ./t010101 % cat t010101.struct c01euc,euc,2 c02sjis,sjis,2 c03jis,jis,2 c04ebcdic,ebcdic,2 c05ebcdik,ebcdik,2 c06uni,uni,2 c07unijava,uni-java,2 c08char,char,1 c09uchar,uchar,1 c10short,short,2 c11ushort,ushort,2 c12int,int,4 c13uint,uint,4 c14long,long,4 c15ulong,ulong,4 c16llong,llong,8 c17ullong,ullong,8 c18float,float,4 c19double,double,8 c20ldouble,ldouble,16 c21binary,binary,1 c22octal,octal,1 c23hex,hex,1 c24pack,pack,3 c25unpack,unpack,3 % dump_struct \ -mode struct_to_tmp -mstt_value meter3 \ -mstt_max 3 t010101.struct \ > t010101.char % dump_struct -mode char_to_data -mctd_auto_pos \ < t010101.char > t010101.data % dump_struct t010101.struct < t010101.data 1,1,1,1,c01euc,euc,2,19 1,2,3,3,c02sjis,sjis,2,29 1,3,5,5,c03jis,jis,2,39 1,4,7,7,c04ebcdic,ebcdic,2,49 1,5,9,9,c05ebcdik,ebcdik,2,59 1,6,11,11,c06uni,uni,2,69 1,7,13,13,c07unijava,uni-java,2,79 1,8,15,15,c08char,char,1,8 1,9,16,16,c09uchar,uchar,1,9 1,10,17,17,c10short,short,2,0 1,11,19,19,c11ushort,ushort,2,1 1,12,21,21,c12int,int,4,2 1,13,25,25,c13uint,uint,4,3 1,14,29,29,c14long,long,4,4 1,15,33,33,c15ulong,ulong,4,5 1,16,37,37,c16llong,llong,8,6 1,17,45,45,c17ullong,ullong,8,7 1,18,53,53,c18float,float,4,8.0000000000000000000000E+00 1,19,57,57,c19double,double,8,9.0000000000000000000000E+00 1,20,65,65,c20ldouble,ldouble,16,0.0000000000000000000000E+00 1,21,81,81,c21binary,binary,1,00000001 1,22,82,82,c22octal,octal,1,002 1,23,83,83,c23hex,hex,1,03 1,24,84,84,c24pack,pack,3,42349 1,25,87,87,c25unpack,unpack,3,529 2,1,90,1,c01euc,euc,2,29 2,2,92,3,c02sjis,sjis,2,29 2,3,94,5,c03jis,jis,2,39 2,4,96,7,c04ebcdic,ebcdic,2,49 2,5,98,9,c05ebcdik,ebcdik,2,59 2,6,100,11,c06uni,uni,2,69 2,7,102,13,c07unijava,uni-java,2,79 2,8,104,15,c08char,char,1,8 2,9,105,16,c09uchar,uchar,1,9 2,10,106,17,c10short,short,2,0 2,11,108,19,c11ushort,ushort,2,1 2,12,110,21,c12int,int,4,2 2,13,114,25,c13uint,uint,4,3 2,14,118,29,c14long,long,4,4 2,15,122,33,c15ulong,ulong,4,5 2,16,126,37,c16llong,llong,8,6 2,17,134,45,c17ullong,ullong,8,7 2,18,142,53,c18float,float,4,8.0000000000000000000000E+00 2,19,146,57,c19double,double,8,9.0000000000000000000000E+00 2,20,154,65,c20ldouble,ldouble,16,0.0000000000000000000000E+00 2,21,170,81,c21binary,binary,1,00000001 2,22,171,82,c22octal,octal,1,002 2,23,172,83,c23hex,hex,1,03 2,24,173,84,c24pack,pack,3,42349 2,25,176,87,c25unpack,unpack,3,529 3,1,179,1,c01euc,euc,2,39 3,2,181,3,c02sjis,sjis,2,29 3,3,183,5,c03jis,jis,2,39 3,4,185,7,c04ebcdic,ebcdic,2,49 3,5,187,9,c05ebcdik,ebcdik,2,59 3,6,189,11,c06uni,uni,2,69 3,7,191,13,c07unijava,uni-java,2,79 3,8,193,15,c08char,char,1,8 3,9,194,16,c09uchar,uchar,1,9 3,10,195,17,c10short,short,2,0 3,11,197,19,c11ushort,ushort,2,1 3,12,199,21,c12int,int,4,2 3,13,203,25,c13uint,uint,4,3 3,14,207,29,c14long,long,4,4 3,15,211,33,c15ulong,ulong,4,5 3,16,215,37,c16llong,llong,8,6 3,17,223,45,c17ullong,ullong,8,7 3,18,231,53,c18float,float,4,8.0000000000000000000000E+00 3,19,235,57,c19double,double,8,9.0000000000000000000000E+00 3,20,243,65,c20ldouble,ldouble,16,0.0000000000000000000000E+00 3,21,259,81,c21binary,binary,1,00000001 3,22,260,82,c22octal,octal,1,002 3,23,261,83,c23hex,hex,1,03 3,24,262,84,c24pack,pack,3,42349 3,25,265,87,c25unpack,unpack,3,529 % dump_struct \ t010101.struct -mdtc_value_only -rec_line \ < t010101.data > t010101.csv % cat t010101.csv ※途中改行しています。実際は3行です。 19,29,39,49,59,69,79,8,9,0,1,2,3,4,5,6,7, 8.0000000000000000000000E+00,9.0000000000000000000000E+00, 0.0000000000000000000000E+00,00000001,002,03,42349,529 29,29,39,49,59,69,79,8,9,0,1,2,3,4,5,6,7, 8.0000000000000000000000E+00,9.0000000000000000000000E+00, 0.0000000000000000000000E+00,00000001,002,03,42349,529 39,29,39,49,59,69,79,8,9,0,1,2,3,4,5,6,7, 8.0000000000000000000000E+00,9.0000000000000000000000E+00, 0.0000000000000000000000E+00,00000001,002,03,42349,529 % dump_struct -mode struct_to_sql t010101.struct > t010101.sql % cat t010101.sql -- -- 【dump_struct 生成 Oracle DB テーブル作成 SQL ファイル】 -- -- このファイルは Oracle SQL*Plus 向けに生成した DB テーブル作成 -- ファイルです。 -- -- DB 制約など、追加で必要となる可能性がある SQL 文はコメントで -- 生成されています。 -- 必要であればコメント化を解除し部分的な修正を行って下さい。 -- -- sqlplus の実行は下記のように行って下さい。 -- -- % sqlplus ユーザー/パスワード[@SID] @a.sql -- -- ※a.sql はファイル名に合わせ変更して下さい。 -- -- テーブル破棄 --DROP TABLE t010101; -- レコード破棄 --TRUNCATE TABLE t010101; -- レコード削除 --DELETE t010101; CREATE TABLE t010101 ( c01euc VARCHAR2(4000), c02sjis VARCHAR2(4000), c03jis VARCHAR2(4000), c04ebcdic VARCHAR2(4000), c05ebcdik VARCHAR2(4000), c06uni VARCHAR2(4000), c07unijava VARCHAR2(4000), c08char NUMBER, c09uchar NUMBER, c10short NUMBER, c11ushort NUMBER, c12int NUMBER, c13uint NUMBER, c14long NUMBER, c15ulong NUMBER, c16llong NUMBER, c17ullong NUMBER, c18float NUMBER, c19double NUMBER, c20ldouble NUMBER, c21binary VARCHAR2(4000), c22octal VARCHAR2(4000), c23hex VARCHAR2(4000), c24pack NUMBER, c25unpack NUMBER ); -- プライマリキー制約の追加 --ALTER TABLE t010101 ADD CONSTRAINT PK_t010101 PRIMARY KEY (c01euc,c02sjis); % sps < t010101.sql % sps desc t010101 名前 NULL? 型 ----------------------------------------- -------- ---------------------------- C01EUC VARCHAR2(4000) C02SJIS VARCHAR2(4000) C03JIS VARCHAR2(4000) C04EBCDIC VARCHAR2(4000) C05EBCDIK VARCHAR2(4000) C06UNI VARCHAR2(4000) C07UNIJAVA VARCHAR2(4000) C08CHAR NUMBER C09UCHAR NUMBER C10SHORT NUMBER C11USHORT NUMBER C12INT NUMBER C13UINT NUMBER C14LONG NUMBER C15ULONG NUMBER C16LLONG NUMBER C17ULLONG NUMBER C18FLOAT NUMBER C19DOUBLE NUMBER C20LDOUBLE NUMBER C21BINARY VARCHAR2(4000) C22OCTAL VARCHAR2(4000) C23HEX VARCHAR2(4000) C24PACK NUMBER C25UNPACK NUMBER % dump_struct -mode struct_to_sql t010101.struct -msts_mode loader \ > t010101.ctl % cat t010101.ctl -- -- 【dump_struct 生成 Oracle SQL*Loader 向けコントロールファイル】 -- -- このファイルは Oracle SQL*Loader 向けに生成したコントロール -- ファイルです。 -- -- 安全第一なオプションを指定しています。必要であれば内容を修正 -- して下さい。 -- -- sqlldr の実行は下記のように行って下さい。 -- -- % sqlldr ユーザー/パスワード[@SID] control=a.ctl log=a.log -- -- ※a.ctl/a.log はファイル名に合わせ変更して下さい。 -- -- なお、事前にテーブル t010101 が作成されている必要があります。 -- OPTIONS(LOAD=-1,SKIP=0,ERRORS=1,ROWS=100) LOAD DATA CHARACTERSET ja16euc INFILE 't010101.csv' BADFILE 't010101.bad' DISCARDFILE 't010101.dis' APPEND INTO TABLE t010101 FIELDS TERMINATED BY "," TRAILING NULLCOLS ( c01euc, c02sjis, c03jis, c04ebcdic, c05ebcdik, c06uni, c07unijava, c08char, c09uchar, c10short, c11ushort, c12int, c13uint, c14long, c15ulong, c16llong, c17ullong, c18float, c19double, c20ldouble, c21binary, c22octal, c23hex, c24pack, c25unpack ) % sqlldr control=t010101.ctl log=t010101.log % sps << EOF set colsep '|' set recsepchar '-' set pagesize 0 set feedback off select * from t010101; EOF 19 29 39 49 59 69 79 8| 9| 0| 1| 2| 3| 4 5| 6| 7| 8| 9| 0 00000001 002 03 42349| 529 -------------------------------------------------------------------------------- 29 29 39 49 59 69 79 8| 9| 0| 1| 2| 3| 4 5| 6| 7| 8| 9| 0 00000001 002 03 42349| 529 -------------------------------------------------------------------------------- 39 29 39 49 59 69 79 8| 9| 0| 1| 2| 3| 4 5| 6| 7| 8| 9| 0 00000001 002 03 42349| 529 --------------------------------------------------------------------------------
以上 dump_struct と簡単なコマンドでファイルをデータベースの表として取り込むことができました。
カラムの型は「VARCHAR2」と「NUMBER」のみとなりますが、一度データベースに取り込んでしまえば SQL 文と PL/SQL 言語(Oracle のみ)でデータの操作は簡単に行えます。
カラム型の制約で思うようにデータ操作が行えない場合、別の表にデータを変換しながら移せば良いだけです。
このファイルだけの更新であればデータベースを使わず dump_struct と少しのコマンドで不足しません。
しかし、複数のファイルがキーによって関係を持っているのであれば、やはりデータベースの SQL 文は便利です。
複数のファイルを一時的にデータベース化し各種の更新を行った後、それぞれのファイルに書き戻せば目的をより早く=低コストに達成できると思います。
ここでは繰り返しを持つデータ構造定義ファイルを扱います。
データベース化、およびファイル化を行い入力と同じ出力が得られたか確認します。
前記同様 dump_struct のテストスクリプトをそのまま使います。
% cat t010103 #!/bin/tcsh -f source ../common/sets # データ構造定義ファイル内容表示 echo "% cat $BN.struct" /bin/cat $BN.struct # 仮文字データ生成 ※結果検証を行うため -mstt_test を追加 echo \ "% dump_struct -mode struct_to_tmp -mstt_value meter3 -mstt_max 3 -mstt_test $BN.struct \ > $BN.char" ds -mode struct_to_tmp -mstt_value meter3 -mstt_max 3 -mstt_test $BN.struct > $BN.char # 仮文字データからデータ生成 echo "% dump_struct -mode char_to_data -mctd_auto_pos < $BN.char > $BN.data" ds -mode char_to_data -mctd_auto_pos < $BN.char > $BN.data # データ内容表示 echo "% dump_struct $BN.struct < $BN.data" ds $BN.struct < $BN.data # データの CSV(文字)化 echo "% dump_struct $BN.struct -mdtc_value_only -rec_line < $BN.data > $BN.csv" ds $BN.struct -mdtc_value_only -rec_line < $BN.data > $BN.csv # CSV 形式データ内容表示 echo "% cat $BN.csv" /bin/cat $BN.csv # RDB テーブル作成 SQL 生成 echo "% dump_struct -mode struct_to_sql $BN.struct > $BN.sql" ds -mode struct_to_sql $BN.struct > $BN.sql # RDB テーブル作成 SQL 内容表示 echo "% cat $BN.sql" /bin/cat $BN.sql # sqlplus による RDB テーブル作成 ※エラーは無視 echo "% sps < $BN.sql" sps << EOF >& /dev/null set feedback off -- 【注意】テーブル内容の削除 -- -- データのみ削除 --truncate table $BN; -- テーブルごと削除 ※カラム名などを変更した場合はこちらで。 drop table $BN; @$BN.sql EOF # RDB テーブル構造表示 /bin/cat << EOF % sps desc $BN EOF sps << EOF desc $BN EOF # sqlldr コントロールファイル生成 echo "% dump_struct -mode struct_to_sql $BN.struct -msts_mode loader > $BN.ctl" ds -mode struct_to_sql $BN.struct -msts_mode loader > $BN.ctl # sqlldr コントロールファイル内容表示 echo "% cat $BN.ctl" /bin/cat $BN.ctl # sqlldr によるデータ登録 echo "% sqlldr control=$BN.ctl log=$BN.log" sl control=$BN.ctl log=$BN.log > /dev/null # RDB データ内容表示 /bin/cat << EOFF % sps << EOF set colsep '|' set recsepchar '-' set pagesize 0 set feedback off select * from $BN; EOF EOFF sps << EOF set colsep '|' set recsepchar '-' set pagesize 0 set feedback off select * from $BN; EOF # RDB $BN テーブル CSV 化 /bin/cat << EOF echo "select * from $BN;" | s2c -trim true > $BN.csv_db EOF echo "select * from $BN;" | s2c -trim true > $BN.csv_db # RDB $BN テーブル CSV 内容表示 echo "% cat $BN.csv_db" /bin/cat $BN.csv_db # 疑似 dump_struct -rec_line 文字データの生成 ds $BN.struct -mode struct_show -rec_line > $BN.char_db /bin/cat $BN.csv_db | /bin/awk '{printf ",,"; print}' >> $BN.char_db /bin/cat $BN.char_db # 疑似文字データをデータ化 echo "% ds -mode char_to_data -rec_line -mctd_auto_pos < $BN.char_db > $BN.data_db" ds -mode char_to_data -rec_line -mctd_auto_pos < $BN.char_db > $BN.data_db # データ内容表示 echo "% ds $BN.struct < $BN.data_db > $BN.char_db2" ds $BN.struct < $BN.data_db > $BN.char_db2 echo "% cat $BN.char_db2" /bin/cat $BN.char_db2 # 両文字データ内容比較 echo "% diff $BN.char $BN.char_db2" /bin/diff $BN.char $BN.char_db2 # 両データ内容比較 echo "% cmp $BN.data $BN.data_db" /bin/cmp $BN.data $BN.data_db exit 0
前記の例とほぼ同じところの説明は省略します。 差分で説明します。
% diff -w t010101 t010103 9,12c9,11 < # 仮文字データ生成 < echo -n "% dump_struct -mode struct_to_tmp" < echo " -mstt_value meter3 -mstt_max 3 $BN.struct > $BN.char" < ds -mode struct_to_tmp -mstt_value meter3 -mstt_max 3 $BN.struct > $BN.char --- > # 仮文字データ生成 ※結果検証を行うため -mstt_test を追加 > echo "% dump_struct -mode struct_to_tmp -mstt_value meter3 -mstt_max 3 -mstt_test $BN.struct > $BN.char" > ds -mode struct_to_tmp -mstt_value meter3 -mstt_max 3 -mstt_test $BN.struct > $BN.char 97a97,131 > # RDB $BN テーブル CSV 化 > /bin/cat << EOF > echo "select * from $BN;" | s2c -trim true > $BN.csv_db > EOF > > echo "select * from $BN;" | s2c -trim true > $BN.csv_db > > # RDB $BN テーブル CSV 内容表示 > echo "% cat $BN.csv_db" > /bin/cat $BN.csv_db > > # 疑似 dump_struct -rec_line 文字データの生成 > ds $BN.struct -mode struct_show -rec_line > $BN.char_db > /bin/cat $BN.csv_db | /bin/awk '{printf ",,"; print}' >> $BN.char_db > /bin/cat $BN.char_db > > # 疑似文字データをデータ化 > echo "% ds -mode char_to_data -rec_line -mctd_auto_pos < $BN.char_db > $BN.data_db" > ds -mode char_to_data -rec_line -mctd_auto_pos < $BN.char_db > $BN.data_db > > # データ内容表示 > echo "% ds $BN.struct < $BN.data_db > $BN.char_db2" > ds $BN.struct < $BN.data_db > $BN.char_db2 > > echo "% cat $BN.char_db2" > /bin/cat $BN.char_db2 > > # 両文字データ内容比較 > echo "% diff $BN.char $BN.char_db2" > /bin/diff $BN.char $BN.char_db2 > > # 両データ内容比較 > echo "% cmp $BN.data $BN.data_db" > /bin/cmp $BN.data $BN.data_db
それでは順番に説明します。
最初の差分「9,12c9,11」はコメントにある通り、入出力文字データを比較検証するため dump_struct に -mstt_test を指示しています。
残りの差分はデータベースのテーブルをファイル化するときの手順です。
「# RDB $BN テーブル CSV 化」は公開しているツール sql2csv を使ってデータベース・テーブルの値を CSV 形式で出力しています。
どんな手段でも良いのですが、データベースと dump_struct を連携させるためには CSV 形式の文字データを作る必要があります。
「# 疑似 dump_struct -rec_line 文字データの生成」は -mode struct_show と -rec_line の組み合わせにより、データ化するときのヘッダを取得しています。
続いて CSV 形式で出力したデータベース・テーブルの内容に dump_struct -rec_line 形式に合わせるため先頭 2 つに空のフィールドを追加し、先ほどのヘッダとマージしています。
なおここで生成した char_db はファイル化(data_db)するための文字データであり、入力文字データと比較するには信頼性が足りません。
「# 疑似文字データをデータ化」はデータベース・テーブル内容のファイル化です。
ここまででデータベース・テーブル内容のファイル化は完了です。
以降は結果検証になります。
「# データ内容表示」でファイル化の成果を文字データに変換しています。
「# 両文字データ内容比較」はコメントの通り入力と出力の文字データに差分が無いことを検証するためのものです。
「# 両データ内容比較」は OS 標準コマンド「cmp」を使い入力と出力のデータに差分が無いことを検証するためのものです。
それでは実際にこの dump_struct テストスクリプトを動かしてみます。
% ./t010103 % cat t010103.struct c01euc,euc,2,{2}:{3} c12int,int,4,{4 c19double,double,8 c23hex,hex,1,} c24pack,pack,3 % dump_struct -mode struct_to_tmp -mstt_value meter3 \ -mstt_max 3 -mstt_test t010103.struct > t010103.char % dump_struct -mode char_to_data -mctd_auto_pos \ < t010103.char > t010103.data % dump_struct t010103.struct < t010103.data 1,1,1,1,c01euc[0][0],euc,2,19 1,2,3,3,c01euc[0][1],euc,2,29 1,3,5,5,c01euc[0][2],euc,2,39 1,4,7,7,c01euc[1][0],euc,2,49 1,5,9,9,c01euc[1][1],euc,2,59 1,6,11,11,c01euc[1][2],euc,2,69 1,7,13,13,c12int[0],int,4,7 1,8,17,17,c19double[0],double,8,8.0000000000000000000000E+00 1,9,25,25,c23hex[0],hex,1,09 1,10,26,26,c12int[1],int,4,0 1,11,30,30,c19double[1],double,8,1.0000000000000000000000E+00 1,12,38,38,c23hex[1],hex,1,02 1,13,39,39,c12int[2],int,4,3 1,14,43,43,c19double[2],double,8,4.0000000000000000000000E+00 1,15,51,51,c23hex[2],hex,1,05 1,16,52,52,c12int[3],int,4,6 1,17,56,56,c19double[3],double,8,7.0000000000000000000000E+00 1,18,64,64,c23hex[3],hex,1,08 1,19,65,65,c24pack,pack,3,92349 2,1,68,1,c01euc[0][0],euc,2,29 2,2,70,3,c01euc[0][1],euc,2,29 2,3,72,5,c01euc[0][2],euc,2,39 2,4,74,7,c01euc[1][0],euc,2,49 2,5,76,9,c01euc[1][1],euc,2,59 2,6,78,11,c01euc[1][2],euc,2,69 2,7,80,13,c12int[0],int,4,7 2,8,84,17,c19double[0],double,8,8.0000000000000000000000E+00 2,9,92,25,c23hex[0],hex,1,09 2,10,93,26,c12int[1],int,4,0 2,11,97,30,c19double[1],double,8,1.0000000000000000000000E+00 2,12,105,38,c23hex[1],hex,1,02 2,13,106,39,c12int[2],int,4,3 2,14,110,43,c19double[2],double,8,4.0000000000000000000000E+00 2,15,118,51,c23hex[2],hex,1,05 2,16,119,52,c12int[3],int,4,6 2,17,123,56,c19double[3],double,8,7.0000000000000000000000E+00 2,18,131,64,c23hex[3],hex,1,08 2,19,132,65,c24pack,pack,3,92349 3,1,135,1,c01euc[0][0],euc,2,39 3,2,137,3,c01euc[0][1],euc,2,29 3,3,139,5,c01euc[0][2],euc,2,39 3,4,141,7,c01euc[1][0],euc,2,49 3,5,143,9,c01euc[1][1],euc,2,59 3,6,145,11,c01euc[1][2],euc,2,69 3,7,147,13,c12int[0],int,4,7 3,8,151,17,c19double[0],double,8,8.0000000000000000000000E+00 3,9,159,25,c23hex[0],hex,1,09 3,10,160,26,c12int[1],int,4,0 3,11,164,30,c19double[1],double,8,1.0000000000000000000000E+00 3,12,172,38,c23hex[1],hex,1,02 3,13,173,39,c12int[2],int,4,3 3,14,177,43,c19double[2],double,8,4.0000000000000000000000E+00 3,15,185,51,c23hex[2],hex,1,05 3,16,186,52,c12int[3],int,4,6 3,17,190,56,c19double[3],double,8,7.0000000000000000000000E+00 3,18,198,64,c23hex[3],hex,1,08 3,19,199,65,c24pack,pack,3,92349 % dump_struct t010103.struct -mdtc_value_only -rec_line \ < t010103.data > t010103.csv % cat t010103.csv ※途中改行しています。実際は3行です。 19,29,39,49,59,69,7, 8.0000000000000000000000E+00,09,0,1.0000000000000000000000E+00,02,3, 4.0000000000000000000000E+00,05,6,7.0000000000000000000000E+00,08,92349 29,29,39,49,59,69,7, 8.0000000000000000000000E+00,09,0,1.0000000000000000000000E+00,02,3, 4.0000000000000000000000E+00,05,6,7.0000000000000000000000E+00,08,92349 39,29,39,49,59,69,7, 8.0000000000000000000000E+00,09,0,1.0000000000000000000000E+00,02,3, 4.0000000000000000000000E+00,05,6,7.0000000000000000000000E+00,08,92349 % dump_struct -mode struct_to_sql t010103.struct > t010103.sql % cat t010103.sql -- -- 【dump_struct 生成 Oracle DB テーブル作成 SQL ファイル】 -- -- このファイルは Oracle SQL*Plus 向けに生成した DB テーブル作成 -- ファイルです。 -- -- DB 制約など、追加で必要となる可能性がある SQL 文はコメントで -- 生成されています。 -- 必要であればコメント化を解除し部分的な修正を行って下さい。 -- -- sqlplus の実行は下記のように行って下さい。 -- -- % sqlplus ユーザー/パスワード[@SID] @a.sql -- -- ※a.sql はファイル名に合わせ変更して下さい。 -- -- テーブル破棄 --DROP TABLE t010103; -- レコード破棄 --TRUNCATE TABLE t010103; -- レコード削除 --DELETE t010103; CREATE TABLE t010103 ( c01euc_0_0 VARCHAR2(4000), c01euc_0_1 VARCHAR2(4000), c01euc_0_2 VARCHAR2(4000), c01euc_1_0 VARCHAR2(4000), c01euc_1_1 VARCHAR2(4000), c01euc_1_2 VARCHAR2(4000), c12int_0 NUMBER, c19double_0 NUMBER, c23hex_0 VARCHAR2(4000), c12int_1 NUMBER, c19double_1 NUMBER, c23hex_1 VARCHAR2(4000), c12int_2 NUMBER, c19double_2 NUMBER, c23hex_2 VARCHAR2(4000), c12int_3 NUMBER, c19double_3 NUMBER, c23hex_3 VARCHAR2(4000), c24pack NUMBER ); -- プライマリキー制約の追加 --ALTER TABLE t010103 ADD CONSTRAINT PK_t010103 PRIMARY KEY -- (c01euc_0_0,c01euc_0_1); % sps < t010103.sql % sps desc t010103 名前 NULL? 型 ----------------------------------------- -------- ---------------------------- C01EUC_0_0 VARCHAR2(4000) C01EUC_0_1 VARCHAR2(4000) C01EUC_0_2 VARCHAR2(4000) C01EUC_1_0 VARCHAR2(4000) C01EUC_1_1 VARCHAR2(4000) C01EUC_1_2 VARCHAR2(4000) C12INT_0 NUMBER C19DOUBLE_0 NUMBER C23HEX_0 VARCHAR2(4000) C12INT_1 NUMBER C19DOUBLE_1 NUMBER C23HEX_1 VARCHAR2(4000) C12INT_2 NUMBER C19DOUBLE_2 NUMBER C23HEX_2 VARCHAR2(4000) C12INT_3 NUMBER C19DOUBLE_3 NUMBER C23HEX_3 VARCHAR2(4000) C24PACK NUMBER % dump_struct -mode struct_to_sql t010103.struct -msts_mode loader \ > t010103.ctl % cat t010103.ctl -- -- 【dump_struct 生成 Oracle SQL*Loader 向けコントロールファイル】 -- -- このファイルは Oracle SQL*Loader 向けに生成したコントロール -- ファイルです。 -- -- 安全第一なオプションを指定しています。必要であれば内容を修正 -- して下さい。 -- -- sqlldr の実行は下記のように行って下さい。 -- -- % sqlldr ユーザー/パスワード[@SID] control=a.ctl log=a.log -- -- ※a.ctl/a.log はファイル名に合わせ変更して下さい。 -- -- なお、事前にテーブル t010103 が作成されている必要があります。 -- OPTIONS(LOAD=-1,SKIP=0,ERRORS=1,ROWS=100) LOAD DATA CHARACTERSET ja16euc INFILE 't010103.csv' BADFILE 't010103.bad' DISCARDFILE 't010103.dis' APPEND INTO TABLE t010103 FIELDS TERMINATED BY "," TRAILING NULLCOLS ( c01euc_0_0, c01euc_0_1, c01euc_0_2, c01euc_1_0, c01euc_1_1, c01euc_1_2, c12int_0, c19double_0, c23hex_0, c12int_1, c19double_1, c23hex_1, c12int_2, c19double_2, c23hex_2, c12int_3, c19double_3, c23hex_3, c24pack ) % sqlldr control=t010103.ctl log=t010103.log % sps << EOF set colsep '|' set recsepchar '-' set pagesize 0 set feedback off select * from t010103; EOF 19 29 39 49 59 69 7| 8 09 0| 1 02 3| 4 05 6| 7 08 92349 -------------------------------------------------------------------------------- 29 29 39 49 59 69 7| 8 09 0| 1 02 3| 4 05 6| 7 08 92349 -------------------------------------------------------------------------------- 39 29 39 49 59 69 7| 8 09 0| 1 02 3| 4 05 6| 7 08 92349 -------------------------------------------------------------------------------- echo "select * from t010103;" | s2c -trim true > t010103.csv_db % cat t010103.csv_db 19,29,39,49,59,69,7,8,09,0,1,02,3,4,05,6,7,08,92349 29,29,39,49,59,69,7,8,09,0,1,02,3,4,05,6,7,08,92349 39,29,39,49,59,69,7,8,09,0,1,02,3,4,05,6,7,08,92349 @h_col_num,#1,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 @h_col_name,#1,c01euc[0][0],c01euc[0][1],c01euc[0][2],c01euc[1][0],c01euc[1][1], c01euc[1][2],c12int[0],c19double[0],c23hex[0],c12int[1],c19double[1],c23hex[1], c12int[2],c19double[2],c23hex[2],c12int[3],c19double[3],c23hex[3],c24pack @h_col_types,#1,euc,euc,euc,euc,euc,euc,int,double,hex,int,double,hex,int, double,hex,int,double,hex,pack @h_col_bytes,#1,2,2,2,2,2,2,4,8,1,4,8,1,4,8,1,4,8,1,3 @h_rec_pos,#1,1,3,5,7,9,11,13,17,25,26,30,38,39,43,51,52,56,64,65 ,,19,29,39,49,59,69,7,8,09,0,1,02,3,4,05,6,7,08,92349 ,,29,29,39,49,59,69,7,8,09,0,1,02,3,4,05,6,7,08,92349 ,,39,29,39,49,59,69,7,8,09,0,1,02,3,4,05,6,7,08,92349 % ds -mode char_to_data -rec_line -mctd_auto_pos \ < t010103.char_db > t010103.dat a_db % ds t010103.struct < t010103.data_db > t010103.char_db2 % cat t010103.char_db2 1,1,1,1,c01euc[0][0],euc,2,19 1,2,3,3,c01euc[0][1],euc,2,29 1,3,5,5,c01euc[0][2],euc,2,39 1,4,7,7,c01euc[1][0],euc,2,49 1,5,9,9,c01euc[1][1],euc,2,59 1,6,11,11,c01euc[1][2],euc,2,69 1,7,13,13,c12int[0],int,4,7 1,8,17,17,c19double[0],double,8,8.0000000000000000000000E+00 1,9,25,25,c23hex[0],hex,1,09 1,10,26,26,c12int[1],int,4,0 1,11,30,30,c19double[1],double,8,1.0000000000000000000000E+00 1,12,38,38,c23hex[1],hex,1,02 1,13,39,39,c12int[2],int,4,3 1,14,43,43,c19double[2],double,8,4.0000000000000000000000E+00 1,15,51,51,c23hex[2],hex,1,05 1,16,52,52,c12int[3],int,4,6 1,17,56,56,c19double[3],double,8,7.0000000000000000000000E+00 1,18,64,64,c23hex[3],hex,1,08 1,19,65,65,c24pack,pack,3,92349 2,1,68,1,c01euc[0][0],euc,2,29 2,2,70,3,c01euc[0][1],euc,2,29 2,3,72,5,c01euc[0][2],euc,2,39 2,4,74,7,c01euc[1][0],euc,2,49 2,5,76,9,c01euc[1][1],euc,2,59 2,6,78,11,c01euc[1][2],euc,2,69 2,7,80,13,c12int[0],int,4,7 2,8,84,17,c19double[0],double,8,8.0000000000000000000000E+00 2,9,92,25,c23hex[0],hex,1,09 2,10,93,26,c12int[1],int,4,0 2,11,97,30,c19double[1],double,8,1.0000000000000000000000E+00 2,12,105,38,c23hex[1],hex,1,02 2,13,106,39,c12int[2],int,4,3 2,14,110,43,c19double[2],double,8,4.0000000000000000000000E+00 2,15,118,51,c23hex[2],hex,1,05 2,16,119,52,c12int[3],int,4,6 2,17,123,56,c19double[3],double,8,7.0000000000000000000000E+00 2,18,131,64,c23hex[3],hex,1,08 2,19,132,65,c24pack,pack,3,92349 3,1,135,1,c01euc[0][0],euc,2,39 3,2,137,3,c01euc[0][1],euc,2,29 3,3,139,5,c01euc[0][2],euc,2,39 3,4,141,7,c01euc[1][0],euc,2,49 3,5,143,9,c01euc[1][1],euc,2,59 3,6,145,11,c01euc[1][2],euc,2,69 3,7,147,13,c12int[0],int,4,7 3,8,151,17,c19double[0],double,8,8.0000000000000000000000E+00 3,9,159,25,c23hex[0],hex,1,09 3,10,160,26,c12int[1],int,4,0 3,11,164,30,c19double[1],double,8,1.0000000000000000000000E+00 3,12,172,38,c23hex[1],hex,1,02 3,13,173,39,c12int[2],int,4,3 3,14,177,43,c19double[2],double,8,4.0000000000000000000000E+00 3,15,185,51,c23hex[2],hex,1,05 3,16,186,52,c12int[3],int,4,6 3,17,190,56,c19double[3],double,8,7.0000000000000000000000E+00 3,18,198,64,c23hex[3],hex,1,08 3,19,199,65,c24pack,pack,3,92349 % diff t010103.char t010103.char_db2 % cmp t010103.data t010103.data_db
結果検証する前に繰り返しを持つデータ構造定義ファイルがどのようなデータベース・カラム名になるのか少し確認します。
通常は記号「[]」と添え字で繰り返し/配列を表現しますが、データベース・カラム名にこれは使えません。そのため記号「_」1 つに変換されています。
それでは結果検証です。
前記の例でファイルをデータベースに登録するところまでの検証は済んでいますので、ここで目視検証しなければいけないところは限られています。
上の方からざっと見ます。まずデータベース・テーブルにどのようなデータが入っているのか軽くみます。
次はもう最後にある 2 つの検証、diff と cmp の結果です。
途中経過は差分が発生し期待と異なる結果になったとき以外は見る必要の無いものです。
diff で入出力した文字データに差分が無いか検証しています。差分があれば何か表示されます。何も表示されていませんので問題が無いこと一瞬で結果検証を終えることができます。
cmp についても同様で、何も表示されていないことで結果検証は終了です。
なおこの t010103 テストスクリプトは Solaris for SPARC + Oracle 11g と Solaris for x86 + Oracle 10g で結果検証されています。
全銀ファイルフォーマットの1つを扱います。
全銀ファイルフォーマットとは、全銀協標準通信プロトコルによりデータ伝送を行うために全国銀行協会連合会が定めたファイルフォーマットです。(一般に公開されたフォーマットです)
まずは改行の無いテキストを 120 バイトごとに改行する方法を説明しておきます。
% fold -b -w 120 {全銀ファイル} > {1 レコード 1 行に変換した全銀ファイル}
※Solaris の fold は最終行に改行を追加しないことに注意して下さい。
EUC 環境で JIS 文字コードを参照する場合は変換します。
% iconv -f jis -t eucJP {1 レコード 1 行に変換した全銀ファイル}
EUC 半角カナは 2 バイトのためバイト数が変わる可能性があります。
後続のフィルタで固定位置の情報を取り出すときは、使用するフィルタが日本語 EUC を完全サポートしていないと期待と異なる結果になります。
なお商用である Solaris のコマンド(awk など)は問題ありません。
特定位置の情報を CSV 形式で取り出すときは下記のように行います。
下記の例は 44 バイト目から 7 バイト、51 バイト目から 30 バイトを抽出しています。(半角カナを見た目の通り 1 バイトとして期待通りカウントします)
% fold -b -w 120 {全銀ファイル} | \
iconv -f jis -t eucJP | \
nawk '{print substr($0,44,7)","substr($0,51,30)}'
ヘッダ、トレーラ、エンドのレコードを除外しデータレコードのみ抽出するためには nawk で先頭バイトにあるデータ区分を判断して下さい。
% fold -b -w 120 {全銀ファイル} | \
iconv -f jis -t eucJP | \
nawk 'substr($0,1,1) == '2' {print substr($0,44,7)","substr($0,51,30)}'
少々前置きが長くなりましたが、dump_struct で総合振込、給与振込、賞与振込のフォーマットを扱ってみます。
データ構造定義ファイルはヘッダ、データ、トレーラ、エンド、不明な場合を含め合計 5 つです。
1)ヘッダ (ファイル名: jba01_header.struct)
% cat jba01_header.struct # # 総合振込、給与振込、賞与振込 - ヘッダー・レコード # @pattern,1,1 # # 項番,項目名,桁数,文字形態,備考 # 1,データ区分,1,半角数字,ヘッダーレコード:1 データ区分,jis,1 # 2,種別コード,2,半角数字,総合:21、給与:11、賞与:12 種別コード,jis,2 # 3,コード区分,1,半角数字,省略可(スペース) コード区分,jis,1 # 4,依頼人コード,10,半角数字,当金庫の指定するコード 依頼人コード,jis,10 # 5,依頼人名,40,半角カナ,省略可(スペース) 依頼人名,jis,40 # 6,振込指定日,4,半角数字,月日の4桁(MMDD)で指定 振込指定日,jis,4 # 7,仕向金融機関番号,4,半角数字,省略可(スペース) 仕向金融機関番号,jis,4 # 8,仕向金融機関名,15,半角カナ,省略可(スペース)、左詰スペース埋め 仕向金融機関名,jis,15 # 9,仕向支店番号,3,半角数字,- 仕向支店番号,jis,3 # 10,仕向支店名,15,半角カナ,省略可(スペース)、左詰スペース埋め 仕向支店名,jis,15 # 11,預金種目(依頼人),1,半角数字,普通:1、当座:2 預金種目(依頼人),jis,1 # 12,口座番号(依頼人),7,半角数字,- 口座番号(依頼人),jis,7 # 13,ダミー,17,半角英数字,スペース ダミー,jis,17% cat jba01_data.struct # # 総合振込、給与振込、賞与振込 - データ・レコード # @pattern,1,2 # # 項番,項目名,桁数,文字形態,備考 # 1,データ区分,1,半角数字,データレコード:2 データ区分,jis,1 # 2,被仕向金融機関番号,4,半角数字,- 被仕向金融機関番号,jis,4 # 3,被仕向金融機関名,15,半角カナ,左詰、スペース埋め 被仕向金融機関名,jis,15 # 4,被仕向支店番号,3,半角数字,- 被仕向支店番号,jis,3 # 5,被仕向支店名,15,半角カナ,左詰、スペース埋め 被仕向支店名,jis,15 # 6,手形交換所番号,4,半角数字,省略可(スペース) 手形交換所番号,jis,4 # 7,預金種目,1,半角数字,普通:1、当座:2、貯蓄:4 預金種目,jis,1 # 8,口座番号,7,半角数字,右詰、ZERO埋め 口座番号,jis,7 # 9,受取人名,30,半角カナ,- 受取人名,jis,30 # 10,振込金額,10,半角数字,右詰、ZERO埋め 振込金額,jis,10 # 11,新規コード,1,半角数字,省略可(スペース) 新規コード,jis,1 # 12,顧客コード1,10,半角数字,省略可(スペース) 顧客コード1,jis,10 # 13,顧客コード2,10,半角数字,省略可(スペース) 顧客コード2,jis,10 # 14,ダミー,9,半角英数字,スペース ダミー,jis,9
2)データ% cat jba01_trailer.struct # # 総合振込、給与振込、賞与振込-トレーラ・レコード # @pattern,1,8 # # 項番,項 目 名,桁数,文字形態,備 考 # 1,データ区分,1,半角数字,トレーラ・レコード:8 データ区分,jis,1 # 2,合計件数,6,半角数字,データ・レコードの合計。右詰、ZERO埋め 合計件数,jis,6 # 3,合計金額,12,半角数字,データ・レコードの合計。右詰、ZERO埋め 合計金額,jis,12 # 4,ダミー,101,半角英数字,スペース ダミー,jis,101
3)トレーラ# # 総合振込、給与振込、賞与振込 - エンド・レコード # @pattern,1,9 # # 項番,項目名,桁数,文字形態,備考 # 1,データ区分,1,半角数字,エンド・レコード:9 データ区分,jis,1 # 2,ダミー,119,半角英数字,スペース ダミー,jis,119
4)エンド% cat jba01_unknown.struct # # 総合振込、給与振込、賞与振込 - 不明レコード # 不明,hex,120
5)不明な場合
不明な場合は 16 進数で表示しています。
これらの 5 つのファイルをカレントディレクトリに置きます。
% ls *.struct jba01_data.struct jba01_header.struct jba01_unknown.struct jba01_end.struct jba01_trailer.struct
データは各 1 レコード、値は文字 1 から 9、0 の繰り返しで先頭のデータ区分をそれぞれのレコードとして認識されるように書き換えました。
% wc -c data 600 data
データは下記の 120 バイト x 5 レコードです。
% fold -b -w 120 data ※1行を3行に改行。 1234567890123456789012345678901234567890 1234567890123456789012345678901234567890 1234567890123456789012345678901234567890 2234567890123456789012345678901234567890 1234567890123456789012345678901234567890 1234567890123456789012345678901234567890 8234567890123456789012345678901234567890 1234567890123456789012345678901234567890 1234567890123456789012345678901234567890 9234567890123456789012345678901234567890 1234567890123456789012345678901234567890 1234567890123456789012345678901234567890 Z234567890123456789012345678901234567890 1234567890123456789012345678901234567890 1234567890123456789012345678901234567890
dump_struct を使って内容を表示すると下記のようになります。
% dump_struct *.struct < data 1,1,1,1,データ区分,jis,1,1 1,2,2,2,種別コード,jis,2,23 1,3,4,4,コード区分,jis,1,4 1,4,5,5,依頼人コード,jis,10,5678901234 1,5,15,15,依頼人名,jis,40,5678901234567890123456789012345678901234 1,6,55,55,振込指定日,jis,4,5678 1,7,59,59,仕向金融機関番号,jis,4,9012 1,8,63,63,仕向金融機関名,jis,15,345678901234567 1,9,78,78,仕向支店番号,jis,3,890 1,10,81,81,仕向支店名,jis,15,123456789012345 1,11,96,96,預金種目(依頼人),jis,1,6 1,12,97,97,口座番号(依頼人),jis,7,7890123 1,13,104,104,ダミー,jis,17,45678901234567890 2,1,121,1,データ区分,jis,1,2 2,2,122,2,被仕向金融機関番号,jis,4,2345 2,3,126,6,被仕向金融機関名,jis,15,678901234567890 2,4,141,21,被仕向支店番号,jis,3,123 2,5,144,24,被仕向支店名,jis,15,456789012345678 2,6,159,39,手形交換所番号,jis,4,9012 2,7,163,43,預金種目,jis,1,3 2,8,164,44,口座番号,jis,7,4567890 2,9,171,51,受取人名,jis,30,123456789012345678901234567890 2,10,201,81,振込金額,jis,10,1234567890 2,11,211,91,新規コード,jis,1,1 2,12,212,92,顧客コード1,jis,10,2345678901 2,13,222,102,顧客コード2,jis,10,2345678901 2,14,232,112,ダミー,jis,9,234567890 3,1,241,1,データ区分,jis,1,8 3,2,242,2,合計件数,jis,6,234567 3,3,248,8,合計金額,jis,12,890123456789 3,4,260,20,ダミー,jis,101,01234567890123456789012345678901234567890 123456789012345678901234567890123456789012345678901234567890 4,1,361,1,データ区分,jis,1,9 4,2,362,2,ダミー,jis,119,234567890123456789012345678901234567890 123456789012345678901234567890123456789012345678901234567890 12345678901234567890 5,1,481,1,不明,hex,120,5A 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 30
本来は「受取人名」などに半角カナを含んでいますので、jis または ebcdik などの文字コードを正しく指定して下さい。