S-JIS[2017-02-11/2018-10-30] 変更履歴

Asakusa Framework Direct I/O CSVファイル連携

Asakusa FrameworkDirect I/OのCSVファイル連携のメモ。


概要

Direct I/OでCSVファイルを読み書きすることが出来る。

列数が不定(可変)のCSVファイルには対応していないので、その場合は1行のテキストとして読み込み、自分でカンマ区切りで分離する。
もしくは、directio.text.csvを使う。[2017-04-30]


使用例

DMDLの記述

CSVファイルを読み書きする為のデータモデルを用意する。

データモデル名は何でもよいが、directio.csv属性を付ける。

src/main/dmdl/example.dmdl:

@directio.csv
example = {

    "カラム1"
    column1 : TEXT;

    "カラム2"
    column2 : INT;
};

DMDLのコンパイル

これでDMDLのコンパイルを行うと、「Abstractモデル名CsvInputDescription」「Abstractモデル名CsvOutputDescription」といったImporter/Exporterの抽象クラスが作られる。


Importer/Exporterクラスの記述

生成されたImporter/Exporterの抽象クラスを継承し、具象クラスを作成する。

Importerの例 Exporterの例
public class ExampleFromCsv extends AbstractExampleCsvInputDescription {

    @Override
    public String getBasePath() {
        return "example";
    }

    @Override
    public String getResourcePattern() {
        return "*.csv";
    }

    @Override
    public DataSize getDataSize() {
        return DataSize.LARGE;
    }
}
public class ExampleToCsv extends AbstractExampleCsvOutputDescription {

    @Override
    public String getBasePath() {
        return "example";
    }

    @Override
    public String getResourcePattern() {
        return "*.csv";
    }

    @Override
    public List<String> getDeletePatterns() {
        return Arrays.asList("*.csv");
    }
}

ベースパスとリソースパターンの使い分け


directio.csv属性

DMDLのデータモデルに記述するdirectio.csv属性には、色々な引数を指定することが出来る。
→Asakusa Framework documentationのCSV形式の設定

@directio.csv属性に指定できる主な要素
要素 説明
charset ファイルのエンコーディング。デフォルトはUTF-8。

なお、BOM付UTF-8には対応していない(AsakusaFWが使用しているHadoopライブラリーが未対応(というかJava自身も未対応))ので、読むときは注意。[2018-10-30]
BOM自身もデータの一部として読み込まれてしまう。

@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(
  has_header = TRUE,
  force_header = TRUE
)
compression ファイルの圧縮形式。デフォルトは非圧縮。 @directio.csv(compression = "gzip")

directio.csv.*属性

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
file_name : TEXT;
@directio.csv.line_number この属性が付けられたプロパティーには(CSVファイルのフィールドのデータではなく)テキスト行番号が入る。
これを指定すると、入力データの分割が行われなくなる。
line_numberとrecord_numberの違い
@directio.csv.line_number
line_number : LONG;
@directio.csv.record_number この属性が付けられたプロパティーには(CSVファイルのフィールドのデータではなく)レコード番号が入る。
これを指定すると、入力データの分割が行われなくなる。
line_numberとrecord_numberの違い
@directio.csv.record_number
record_number : LONG;

ヘッダーレコードの扱い

ヘッダーレコードのあるCSVファイルをdirectio.csv属性で扱う場合の注意点。


ヘッダーレコードのある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行をスキップするだけとなる。


ヘッダーレコードのあるCSVファイルの出力

@directio.csvのhas_headerがTRUEになっていると、CSVファイルの出力時にヘッダーレコードが出力される。


line_numberとrecord_numberの違い

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
b",bbb
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(テキスト行番号)は、どちらかと言えばデバッグ目的で使用するのが良さそう。


Direct I/Oへ戻る / AsakusaFW目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま