IBM i で CPYTOIMPF を使ってCSVファイルを書き出す

# IBM i 上で CSVファイルの出力

IBM i で DB に保存されているデータをCSVとして出力する方法を試してみました。  
CSVファイルの書き出しは、Excelで開きたいといった場合があるので、使われている方も多いのではないでしょうか？  

今回は、CSVファイルをJSONファイルに変換するときどうすれば良いか？という話を聞いたので、JSONファイルに書き出すことを目的としてCSVファイルの書き出しについて見ていきたいと思います。  

本記事は、IBM i 7.5 環境を元に記載しております。  

# CPYTOIMPF

CSVへの書き出しは、`CPYTOIMPF` を利用します。  
CPYTOIMPF 自体は、物理ファイルをIFS(統合ファイル・システム)上のファイルに書き出したり、ファイルにレコードを追加したりといったことができます。  

https://www.ibm.com/docs/ja/i/7.5?topic=ssw_ibm_i_75/cl/cpytoimpf.html

ドキュメントを見てみると、オプションのパラメーター数がなかなか多く、少し大変そう。  
JSONファイルに書き出したり、CSVとして扱うときに便利であろう設定を紹介していきます。  

# サンプルデータ  

CPYTOIMPF の動きを見るに当たって、サンプルとなるデータを準備しました。  
下記のテーブルを用意し、適当なデータを入れています。  

- 商品マスター
    - CSHSHCD : 商品コード
    - CSHSHNK : 商品名称（カナ）
    - CSHSHNR : 商品名称（略称）
    - CSHSHNM : 商品名（伝票用）
    - CSHHB1C : 分類コード
    - CSHHB2C : 中分類コード
    - CSHHB3C : 小分類コード
    - CSHTMN1 : 単位名1（漢字）
    - CSHTMN2 : 単位名2（漢字）
    - CSHTMN3 : 単位名3（漢字）
    - CSHKUGP : 標準小売売価
    - CSHSPG1 : 標準仕入単価
    - CSHHPG1 : 販売価格

データの一部の抜き出すと、このような感じです。  

| CSHSHCD | CSHSHNK       | CSHSHNR          | CSHSHNM                | CSHHB1C | CSHHB2C | CSHHB3C | CSHTMN1 | CSHTMN2 | CSHTMN3 | CSHKUGP | CSHSPG1 | CSHHPG1 |
| :------ | :------------ | :--------------- | :--------------------- | :------ | :------ | :------ | :------ | :------ | :------ | :------ | :------ | :------ |
| 8010301 | ｾﾝｼｬｽﾎﾟﾝｼﾞ    | 洗車スポンジ     | 洗車スポンジ           | 2       | 1       | 1       | ﾊﾞﾗ     | ﾎﾞｰﾙ    | ｹｰｽ     | 300     | 207     | 270     |
| 8010302 | ｾﾝｼｬﾌﾞﾗｼ      | 洗車ブラシ       | 洗車ブラシ             | 2       | 1       | 1       | ﾊﾞﾗ     | ﾎﾞｰﾙ    | ｹｰｽ     | 260     | 189     | 238     |
| 8010303 | ﾂﾔﾀﾞｼﾗﾊﾞｰ     | つや出しラバー   | つや出しラバー         | 2       | 1       | 1       | ﾊﾞﾗ     | ﾎﾞｰﾙ    | ｹｰｽ     | 550     | 398     | 495     |
| 8010304 | ﾃﾝﾈﾝﾊﾁﾐﾂﾜｯｸｽ  | 天然蜂蜜ワックス | 天然蜂蜜ワックス       | 2       | 1       | 1       | ﾊﾞﾗ     | ﾎﾞｰﾙ    | ｹｰｽ     | 900     | 765     | 880     |
| 8010305 | ﾜｯｸｽｽﾌﾟﾚｰﾀｲﾌﾟ | ワックススプレー | ワックススプレータイプ | 2       | 1       | 1       | ﾊﾞﾗ     | ﾎﾞｰﾙ    | ｹｰｽ     | 400     | 288     | 360     |

データは、`DEMO/DEMOTABLE` として作成しています。  

# 実行例

まずは、実行例を見ていきたいと思います。  
その後、各パラメーターについて記述していきたいと思います。  

実行するコマンドは、下記のようになっています。  
```
CPYTOIMPF FROMFILE(ライブラリー/ファイル) TOSTMF(出力先のストリームファイル) MBROPT(*REPLACE) STMFCCSID(01208) RCDDLM(*LF) RMVBLANK(*BOTH) ADDCOLNAM(*SQL)
```

では、実際に動かしてみます。  
`DEMO/DEMOTABLE` のデータをすべて `cpytoimpf_output.csv` に書き出してみます。  
`CALL QCMD` を実行した上で、コマンド実行を行っています。  

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242652/bf23da77-688d-967a-e156-5bc4f198e5d4.png)

実行すると、「DEMOのファイルDEMOTABLEからすべてのレコードがコピーされた。」という表示が出ます。  

`WRKLNK` で確認してみると、 ファイルができているのがわかりますね。  

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242652/3569d20c-6aee-f418-2151-59af96173159.png)

中身を見ると、カンマ区切りでデータが入っているのでちゃんとCSVとして開けそうです。  

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242652/c3cd379a-76eb-c948-438f-e59bfb73300e.png)


IFS上で確認してみると、このように表示されます。  
個人的には、こちらのほうが見やすいので好きです。  

```
demo # cat cpytoimpf_output.csv
"8010302","ｾﾝｼｬﾌﾞﾗｼ","洗車ブラシ","洗車ブラシ","2","1","1","ﾊﾞﾗ","ﾎﾞｰﾙ","ｹｰｽ",260,189.00,238.00
"8010303","ﾂﾔﾀﾞｼﾗﾊﾞｰ","つや出しラバー","つや出しラバー","2","1","1","ﾊﾞﾗ","ﾎﾞｰﾙ","ｹｰｽ",550,398.00,495.00
"8010304","ﾃﾝﾈﾝﾊﾁﾐﾂﾜｯｸｽ","天然蜂蜜ワックス","天然蜂蜜ワックス","2","1","1","ﾊﾞﾗ","ﾎﾞｰﾙ","ｹｰｽ",900,765.00,880.00
"8010305","ﾜｯｸｽｽﾌﾟﾚｰﾀｲﾌﾟ","ワックススプレー","ワックススプレータイプ","2","1","1","ﾊﾞﾗ","ﾎﾞｰﾙ","ｹｰｽ",400,288.00,360.00
"8020101","ﾍﾙﾒｯﾄ ﾌﾙ","ヘルメット  フル","ヘルメット  フル","2","2","1","ﾊﾞﾗ","ﾎﾞｰﾙ","ｹｰｽ",6200,3840.00,4800.00
"8020102","ﾍﾙﾒｯﾄ","ヘルメット","ヘルメット","2","2","1","ﾊﾞﾗ","ﾎﾞｰﾙ","ｹｰｽ",6000,3560.00,4450.00
"8020103","ｸﾞﾛｰﾌﾞ","グローブ","グローブ","2","2","1","ﾊﾞﾗ","ﾎﾞｰﾙ","ｹｰｽ",4800,3040.00,3800.00
"13010101","ﾌｰﾄﾞｵｵﾌﾞｸﾛ","フード  大袋","フード  大袋","1","4","3","ﾊﾞﾗ","ﾎﾞｰﾙ","ｹｰｽ",700,585.00,630.00
```

# パラメーターについて
それでは、こちらのコマンドをパラメーターに分けて見ていきます。  
```
CPYTOIMPF FROMFILE(ライブラリー/ファイル) TOSTMF(出力先のストリームファイル) MBROPT(*REPLACE) STMFCCSID(01208) RCDDLM(*LF) RMVBLANK(*BOTH) ADDCOLNAM(*SQL)
```

- FROMFILE(ライブラリー/ファイル)   
    CSVに書き出す対象となるファイルを指定します。  
    今回の場合だと、`DEMO/DEMOTABLE` になります。  
    <br>

- TOSTMF(出力先のストリームファイル) 
    IFS上に書き出すファイル名を指定します。  
    絶対パスで記述が必要なので、注意が必要です。
    <br>

- MBROPT(*REPLACE) 
    書き出す際に、置き換えをするか追記をするか書き出しの方式を選択できます。   
    指定できる値は、以下の2つとなっています。
    - ファイルの置き換え : *REPLACE
    - ファイルに追加  : *ADD
    <br>

- STMFCCSID(01208) 
    書き出したファイルのCCSIDを選択できます。  
    JSONへの変換を想定しているため、扱いやすい UTF-8 (01208) を指定しています。  
    ※ JSONへの書き出しについては、別の記事で書こうと思っています。  
    <br>

- RCDDLM(*LF) 
    レコードの区切り文字を指定します。  
    いわゆる改行コードを指します。  
    OSによって、使用される改行コードが異なるため、注意が必要です。  
    `LF` を指定していますが、これはUNIXで使われる改行コードです。

    `CR` を指定すると、WRKLNKなどで見た場合は改行されているように見えます。  
    ただ、vi (エディタ) などで開いてみると、下記の画像のように `^M` が表示され、改行もされていません。
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242652/2d074695-f5dc-430e-9c65-8ab77cac4831.png)


    これは、PASEが `CR` に対応していないためです。  
    Windowsは `CRLF`, Macは `CR` だったのですが、最近だとどれでも開けるようです。  

    基本的には、`LF` で書き出しておくのが無難でしょう。  
    <br>

- RMVBLANK(*BOTH) 
    ブランクの除去を行います。  
    IBM i のDBの場合、固定長のカラムでは一定の文字数に達していないデータには空白が含まれます。  
    CSVで書き出すデータには、空白部分は必要ないため削除します。  
    <br>

- ADDCOLNAM(*SQL)
    カラム名を追加します。  
    <br>

CPYTOIMPFは、ファイルに入っているデータすべてを書き出すため、一部のデータを書き出したい場合には、先に必要なデータを抽出したファイルを作成しておく必要があります。  

他にもいくつかパラメーターはありますが、用途に合わせて使い分けるのが良いかと思います。  
例えば、日付や時間、小数点の出力形式を指定することも可能です。