Asakusa Framework0.7.1でテキストファイルを入出力する方法のメモ。
|
|
Direct I/O(やWindGate)でCSVファイル(カンマ区切り)やTSVファイル(タブ区切り)を扱うことが出来るが、ベタのテキストファイルを扱うことは出来ない。
(0.7.5で出来るようになった。→テキストファイル連携 [2015-12-05])
ベタのテキストファイルを読み込めると便利な場面があるのだが。
しかしこれは、AsakusaFWがそういったフォーマットを扱うImporter/Exporterの雛形を生成しない、というだけであって、自分でクラスを作成すれば、ベタのテキストファイルを扱うことが出来る。
Direct I/OではFormatクラスを自作することになる。
Formatクラスの中では、ファイルを読み込むModelInputと、ファイルへ書き出すModelOutputを用意する。
仮にファイル読み込みしか必要ない場合でも、フローのテスト実行時にテストデータをファイルに書き出す必要があるので、ModelOutputも必ず用意しておかなければならない。
データモデルに@directio.csvを付けてDMDLをコンパイルするとFormatクラスが生成されるので、自作する場合は参考になる。
(0.7.5で@directio.lineが用意された。→テキストファイル連携 [2015-12-05])
Direct I/Oでベタのテキストファイルを読み込むImporterを作ってみる。
まず、テキストファイルの1行を保持するデータモデルを用意する。
"テキストファイルの一行" line_model = { "テキスト" text : TEXT; };
※ここでは特に「@directio.csv」等の属性は付けない。この属性は、あくまでFormatクラス等を自動生成する為のものであり、今回はそれを自作するので。
次に、Formatクラスを作成する。
Direct I/Oの場合、BinaryStreamFormatを継承したクラスを作成する。
(→完成したDirectioLineFormatのソース)
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import com.asakusafw.runtime.directio.BinaryStreamFormat; import com.asakusafw.runtime.directio.util.DelimiterRangeInputStream; import com.asakusafw.runtime.io.ModelInput; import com.asakusafw.runtime.io.ModelOutput; import com.example.modelgen.dmdl.model.LineModel;
/** * format for {@link LineModel}. */ public final class DirectioLineFormat extends BinaryStreamFormat<LineModel> {
@Override public Class<LineModel> getSupportedType() { return LineModel.class; } @Override public long getPreferredFragmentSize() { return -1L; } @Override public long getMinimumFragmentSize() { return Long.MAX_VALUE; }
まずは、お決まりのようなメソッドから実装する。
今回はLineModelにファイルの一行分のデータを書き込むので、対象データモデルはLineModelを指定する。
getPreferredFragmentSizeメソッドはブロックの推奨サイズを返すらしい。
getMinimumFragmentSizeメソッドは、入力ファイルをブロックに分割することに関係しているらしい。分割しない(つまり並列で処理されない)ようにする場合は、-1を返すようにする。
@Override public ModelInput<LineModel> createInput(Class<? extends LineModel> dataType, String path, InputStream stream, long offset, long fragmentSize) throws IOException { InputStream fragmentInput = new DelimiterRangeInputStream(stream, '\n', fragmentSize, offset > 0L); return new Reader(fragmentInput); }
createInputメソッドで、ファイルを読み込んでデータモデルオブジェクトに書き込むModelInputを返す。
DelimiterRangeInputStreamは、入力のテキストファイルを分割して読み込む為のInputStreamらしい。
分割する必要が無い場合は、Readerに直接stream(createInputメソッドの引数)を渡せばいい。
Readerクラスは自分で用意する。後述。
@Override public ModelOutput<LineModel> createOutput(Class<? extends LineModel> dataType, String path, OutputStream stream) throws IOException { return new Writer(stream); }
createOutputメソッドは、データモデルオブジェクトの内容をファイルに書き出すModelOutputを返す。
Writerクラスは自分で用意する。後述。
Readerクラスは、ModelInputインターフェースの実装。
private static final class Reader implements ModelInput<LineModel> { private final BufferedReader reader; Reader(InputStream stream) { this.reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)); } @Override public boolean readTo(LineModel object) throws IOException { String line = reader.readLine(); if (line == null) { return false; } object.setTextAsString(line); return true; } @Override public void close() throws IOException { reader.close(); } }
readToメソッドで、データモデルオブジェクトに読み込んだデータを書き込む。
データが無かった場合はfalseを返す。
Writerクラスは、ModelOutputインターフェースの実装。
private static final class Writer implements ModelOutput<LineModel> { private final BufferedWriter writer; Writer(OutputStream stream) { this.writer = new BufferedWriter(new OutputStreamWriter(stream, StandardCharsets.UTF_8)); } @Override public void write(LineModel object) throws IOException { if (!object.getTextOption().isNull()) { writer.write(object.getTextAsString()); } writer.write('\n'); } @Override public void close() throws IOException { writer.close(); } } }
writeメソッドで、データモデルオブジェクトのデータをファイルに出力する。
上記のFormatを使ったImporterクラスは、以下のようになる。
import com.asakusafw.runtime.directio.DataFormat; import com.asakusafw.vocabulary.directio.DirectFileInputDescription; import com.example.modelgen.dmdl.model.LineModel;
public class TextFromFile extends DirectFileInputDescription { @Override public Class<?> getModelType() { return LineModel.class; } @Override public Class<? extends DataFormat<?>> getFormat() { return DirectioLineFormat.class; } @Override public String getBasePath() { return "input/file"; } @Override public String getResourcePattern() { return "*.txt"; } }
getModelTypeメソッドは、データモデルのクラスを返す。
getFormatメソッドで、自作したFormatクラスを返す。
getBasePathおよびgetResourcePatternメソッドは、Direct I/OのCSVファイルと同様。
→ベースパスとリソースパターンの使い分け