S-JIS[2014-12-13/2015-12-05] 変更履歴

Asakusa Framework テキスト入出力

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のテキストファイル読み込みの例

Direct I/Oでベタのテキストファイルを読み込むImporterを作ってみる。


データモデルの用意

まず、テキストファイルの1行を保持するデータモデルを用意する。

src/main/dmdl/models.dmdl:

"テキストファイルの一行"
line_model = {

    "テキスト"
    text : TEXT;
};

※ここでは特に「@directio.csv」等の属性は付けない。この属性は、あくまでFormatクラス等を自動生成する為のものであり、今回はそれを自作するので。


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メソッドで、データモデルオブジェクトのデータをファイルに出力する。


Importerクラスの例

上記の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ファイルと同様。
ベースパスとリソースパターンの使い分け


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