0
Help us understand the problem. What are the problem?

posted at

4分チャレンジ:テキストファイルを分割してランダムアクセスする

はじめに

前記事

  1. 4分チャレンジ:タブ区切りファイルをhtmlファイルに変換する
  2. 4分チャレンジ:31時(翌7時)基準のファイル分割ツール

ツールというよりサンプルですが、近い将来ツールで使うためのstaticメソッドを作るためのPGMです。

要件

可変長テキストファイルをランダムアクセスしたい。
固定長テキストファイルや固定長バイナリファイル(構造体)は、指定したインデックスからファイルオフセットを計算でき、シークして読めます。
可変長の場合、各行のバイト数が分からないので、各行のインデックス番号を保存する必要がある。

staticメソッドの仕様

汎用的に使えるように以下の宣言とした。

	/**
	 * 指定したオフセットから指定した行を読み込む。
	 * 
	 * @param lr           読み込んだ後のファイルオフセットを返す。
	 * @param raf          ランダムアクセスファイル。
	 * @param startPointer ファイルオフセット。
	 * @param maxCnt       最大行数。
	 * @return 読み込んだ行のリスト。
	 * @throws IOException
	 */
	public List<String> readPartLines(LongReference lr, RandomAccessFile raf, long startPointer, int maxCnt) throws IOException {

LongReference lrに関して、C言語のlong *や、C++のlong &ができないので、long &をLongReferenceとした。
他にも戻り値のListを{ List と long }の2つをクラスを生成しても返してもよかったが、このメソッドが返したいのはListがメインなので、このようにした。

中の実装はUTF-8限定でListに追加する。

	public List<String> readPartLines(LongReference lr, RandomAccessFile raf, long startPointer, int maxCnt) throws IOException {
		long lastPointer = startPointer;
		List<String> lines = new ArrayList<>();
		raf.seek(startPointer);
//		System.out.println("startPointer=" + startPointer);
		for (int i = 0; i < maxCnt; i++) {
			String line = raf.readLine();
			if (line == null) {
				break;
			}
			line = new String(line.getBytes("ISO-8859-1"), "UTF-8");
			lines.add(line);
			lastPointer = raf.getFilePointer();
//			System.out.println("lastPointer[" + i + "]=" + lastPointer);
		}
		lr.val = lastPointer;
		return lines;
	}

テストメソッド

最大10行づつ読み込んで、読み込む前のオフセットと行数、1行目の文字列を表示する。
最終行は、ファイル末尾のオフセットと"EOF"の文字列を表示する。

	public void execute(String filepath) throws IOException {
		try (RandomAccessFile raf = new RandomAccessFile(new File(filepath), "r")) {
			long startPointer = 0;
			while (true) {
				LongReference lr = new LongReference();
				List<String> lines = readPartLines(lr, raf, startPointer, 10);
				if (lines.size() <= 0) {
					System.out.printf("%6d,%2d,%s\n", startPointer, 0, "EOF");
					break;
				}
				System.out.printf("%6d,%2d,%s\n", startPointer, lines.size(), lines.get(0));
				startPointer = lr.val;
			}
		} finally {
		}
	}

実行結果

手ごろな13,162バイト、91行のテキストファイルを指定して実行した。

     0,10,2022/05/20 08:45:08.085 sendCloseOrder(): CLOSE:{167060019 日経225mini 22/06  日中  price=26390L, qty=1, trigger=26440S(50), holdId=E202205200038G}
  1458,10,2022/05/20 09:17:08.835 sendCloseOrder(): CLOSE:{167060019 日経225mini 22/06  日中  price=26440L, qty=1, trigger=26485S(45), holdId=E2022052000EY4}
  2940,10,2022/05/20 11:25:08.207 sendCloseOrder(): CLOSE:{167060019 日経225mini 22/06  日中  price=26640L, qty=1, trigger=26685S(45), holdId=E2022052001SJC}
  4398,10,2022/05/20 21:55:09.371 sendCloseOrder(): CLOSE:{167060019 日経225mini 22/06  夜間  price=26900S, qty=1, trigger=26855L(45), holdId=E20220520036VS}
  5832,10,2022/05/20 23:13:08.882 sendCloseOrder(): CLOSE:{167060019 日経225mini 22/06  夜間  price=26850S, qty=1, trigger=26795L(55), holdId=E202205200351M}
  7290,10,2022/05/20 23:59:08.966 cancelOrder(): orderId=20220520A02N06629332, name=日経225mini 22/06, holdId=E2022052003434, holdQty=1
  8726,10,2022/05/21 00:09:08.870 cancelOrder(): orderId=20220520A02N06629502, name=日経225mini 22/06, holdId=E2022052002OKJ, holdQty=1
 10139,10,2022/05/21 00:25:08.294 sendCloseOrder(): CLOSE:{167060019 日経225mini 22/06  夜間  price=26650S, qty=1, trigger=26625L(25), holdId=E2022052001ILW}
 11645,10,2022/05/21 03:13:07.900 sendCloseOrder(): CLOSE:{167060019 日経225mini 22/06  夜間  price=26440L, qty=1, trigger=26470S(30), holdId=E20220523003EH}
 13009, 1,2022/05/21 04:51:08.444 sendCloseOrder(): CLOSE:{167060019 日経225mini 22/06  夜間  price=26640L, qty=1, trigger=26695S(55), holdId=E20220523004GT}
 13162, 0,EOF

使い道

上記ファイルでは、先頭23文字がタイムスタンプとなっているので、(オフセットとタイムスタンプ)のリストを作って、タイムスタンプを二分検索すれば、巨大なファイルから必要な日時の範囲を読み込むことができる。
特にログファイルなど時系列に追記されたファイルは、毎回先頭から読み込むと効率が悪い。

githubソース

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?