Asakusa FrameworkのDirect I/OのCSVファイル連携のメモ。
|
Direct I/OでCSVファイルを読み書きすることが出来る。
列数が不定(可変)のCSVファイルには対応していないので、その場合は1行のテキストとして読み込み、自分でカンマ区切りで分離する。
もしくは、directio.text.csvを使う。[2017-04-30]
CSVファイルを読み書きする為のデータモデルを用意する。
データモデル名は何でもよいが、directio.csv属性を付ける。
@directio.csv
example = {
"カラム1"
column1 : TEXT;
"カラム2"
column2 : INT;
};
これでDMDLのコンパイルを行うと、「Abstractモデル名CsvInputDescription」「Abstractモデル名CsvOutputDescription」といったImporter/Exporterの抽象クラスが作られる。
生成されたImporter/Exporterの抽象クラスを継承し、具象クラスを作成する。
Importerの例 | Exporterの例 |
---|---|
public class ExampleFromCsv extends
AbstractExampleCsvInputDescription { |
public class ExampleToCsv extends
AbstractExampleCsvOutputDescription { |
DMDLのデータモデルに記述するdirectio.csv属性には、色々な引数を指定することが出来る。
→Asakusa Framework documentationのCSV形式の設定
要素 | 説明 | 例 |
---|---|---|
charset |
ファイルのエンコーディング。デフォルトはUTF-8。
なお、BOM付UTF-8には対応していない(AsakusaFWが使用しているHadoopライブラリーが未対応(というかJava自身も未対応))ので、読むときは注意。[2018-10-30] |
@directio.csv(charset = "MS932") |
allow_linefeed |
csvファイルを読み込むとき、各フィールドのデータに改行(LF)が入っていても読めるようにするかどうか。 デフォルトはFALSE。データに改行が入っているとエラーになる。 TRUEにすると改行が入っていても読み込まれるようになる。ただし入力データの分割が行われなくなる。 (csvファイルの出力には関係ない。出力の場合は、データに改行が入っていると、自動的にダブルクォーテーションで囲まれて改行入りで出力される) |
@directio.csv(allow_linefeed = TRUE) |
has_header |
ヘッダーレコードがあるかどうか。デフォルトはFALSE(ヘッダー無し)。 TRUEにすると、 csvファイルを読み込む場合、先頭1行をヘッダーとして扱う。→force_header csvファイルを出力する場合、ヘッダーレコードを出力する。→@directio.csv.field →ヘッダーレコードの扱い |
@directio.csv(has_header = FALSE) |
force_header |
ヘッダーレコードのチェックを行わないかどうか。 デフォルトはFALSE(ヘッダーのチェックを行う)。→@directio.csv.field TRUEにすると、csvファイルの先頭1行をただスキップするだけになる。 →ヘッダーレコードの扱い |
@directio.csv( |
compression |
ファイルの圧縮形式。デフォルトは非圧縮。 | @directio.csv(compression = "gzip") |
DMDLのデータモデル内の各プロパティーにdirectio.csv関連の属性を指定することが出来る。
属性 | 説明 | 例 |
---|---|---|
@directio.csv.field |
name要素で、ヘッダー名を指定する。 省略すると、プロパティー名がそのままヘッダー名として使われる。 ヘッダーレコードを扱うCSV(has_header = TRUE)の場合だけ意味がある。 →ヘッダーレコードの扱い |
@directio.csv.field(name = "title") |
quote要素(AsakusaFW
0.9.0以降)で、出力時にダブルクォーテーションで囲むかどうかを指定する。 デフォルトはneeded(クォートが必要な場合だけ付ける)。 alwaysにすると、常にダブルクォーテーションで囲まれて出力される。 |
@directio.csv.field(quote = "always") |
|
@directio.csv.file_name |
この属性が付けられたプロパティーには(CSVファイルのフィールドのデータではなく)ファイル名 (スキーマ付きのフルパス)が入る。 | @directio.csv.file_name |
@directio.csv.line_number |
この属性が付けられたプロパティーには(CSVファイルのフィールドのデータではなく)テキスト行番号が入る。 これを指定すると、入力データの分割が行われなくなる。 →line_numberとrecord_numberの違い |
@directio.csv.line_number |
@directio.csv.record_number |
この属性が付けられたプロパティーには(CSVファイルのフィールドのデータではなく)レコード番号が入る。 これを指定すると、入力データの分割が行われなくなる。 →line_numberとrecord_numberの違い |
@directio.csv.record_number |
ヘッダーレコードのあるCSVファイルをdirectio.csv属性で扱う場合の注意点。
@directio.csvのhas_headerをTRUEにすると、
先頭1行をヘッダーレコードとして扱う。
Importerのリソースパターンで複数のファイルを読み込むように記述されている場合、各ファイルにヘッダーレコードがあるものとして扱われる。
そして、@directio.csvのforce_headerがFALSE(デフォルト)の場合は、CSVファイルを読み込んだ際にヘッダーレコードのチェックを行う。
すなわち、ヘッダーレコードの各フィールドのデータがヘッダー名と一致していることを確認する。
ヘッダー名は、DMDLのデータモデルの各プロパティーに付けられている@directio.csv.fieldのname要素で指定する。これが付いていない場合、プロパティー名が使われる。
例えば、以下のようなデータモデルの場合
@directio.csv( has_header = TRUE, force_header = FALSE ) example = { @directio.csv.field(name = "title") column1 : TEXT; @directio.csv.field(name = "data1") column2 : INT; column3 : INT; };
ヘッダーレコードが以下の状態でないと、エラーになる。
title,data1,column3
@directio.csvのforce_headerをTRUEにすると、ヘッダーレコードのチェックは行われない。
つまり、先頭1行をスキップするだけとなる。
@directio.csvのhas_headerがTRUEになっていると、CSVファイルの出力時にヘッダーレコードが出力される。
directio.csvでは、プロパティーに@directio.csv.line_numberを付けるとテキスト行番号、@directio.csv.record_numberを付けるとレコード番号が取れる。
テキスト行番号(line_number)は、そのデータのファイル内での開始行(位置)。
レコード番号(record_number)は、読み込んで作られたレコード(データモデルインスタンス)の番号。いわば論理的な番号。
例えば以下のようなCSVファイルを読み込むとき
aaa,aaa,aaa bbb,"b b",bbb ccc,ccc,ccc
line_numberとrecord_numberは以下のようになる。
データ | line_number | record_number |
---|---|---|
aaa,aaa,aaa |
1 | 1 |
bbb,"b |
2 | 2 |
ccc,ccc,ccc |
4 | 3 |
cccはファイル内では4行目から始まっているのでline_numberは4。しかしレコード(読み込んで作られたデータ)としては3番目に作られているので3。
また、ヘッダーレコードがある場合、record_nubmerは1から始まるが、line_numberは2から始まることになる。
(ヘッダーレコード(テキスト行番号は1)がスキップされる為)
それと、これらの番号は、入力ファイルが複数ある場合は各ファイル毎に振られる。
つまり同一番号のデータが複数存在することになる。
したがって、入力データ全体で一意になる番号としては使えない。
(@directio.csv.file_nameで取ってきたファイル名まで含めれば、一意に出来そう。ただしline_numberやrecord_numberを使うと入力データの分割は行われなくなるので、“効率の良い採番方法”とは言えない)
line_number(テキスト行番号)は、どちらかと言えばデバッグ目的で使用するのが良さそう。