3

テキスト (ASCII) ファイル内の特定のレコードにランダムにアクセスし、特定の「停止シーケンス」(レコード区切り文字) が見つかるまでそこから読み取る必要があります。ファイルには複数行のレコードが含まれており、各レコードは区切り記号で区切られています。各レコードは、異なる量の行も必要とします! これは特定の専門分野で一般的に知られているファイル形式であり、変更することはできません。

要求されたレコードにすばやくジャンプできるように、ファイルのインデックスを作成したいと考えています。

のような同様の質問で

Javaの位置でファイル内の文字列にアクセスする方法

とその中のリンク、答えは常にのseek()ようなさまざまなクラスのメソッドを参照しRandomAccessFileます。私はそれについて知っています!

私が抱えている問題は、シークに必要なオフセットを取得する方法です! (ファイルの索引付け)

BufferedReadergetFilePointer()ファイルの先頭から現在のバイトオフセットを取得する方法やその他の方法はありません。RandomAccessFileメソッドはありますreadLine()が、そのパフォーマンスはひどいものです。私の場合はまったく使えません。

ファイルを 1 行ずつ読み取る必要があり、レコード区切り文字が見つかるたびにバイト オフセットを取得する必要があります。どうすればこれを達成できますか?

4

4 に答える 4

2

さらに多くのグーグル検索、試行錯誤などを行った後、RandomAccessFileすべてのメソッドを単純にラップして公開するソリューションにたどり着きました。ただし、この方法は、微調整を加えreadLine()て からのものを話すことで大幅に改善されました。BufferedReaderパフォーマンスはそれと同じになりました。

このいわゆるクラスOptimizedRandomAccessFileバッファは、ファイル内の位置を必要とする、またはファイル内の位置に影響を与える他のメソッドが呼び出されない限り、readLine() 呼び出しをバッファリングします。例:

OptimizedRandomAccessFile raf = new OptimizedRandomAccessFile(filePath, "r");
String line = raf.readLine();
int nextByte = raf.read();

nextByteファイルの次の行の最初のバイトが含まれます。

完全なコードはbitbucketにあります。

于 2013-09-22T18:36:52.597 に答える
2

クラスをサブクラス化BufferedReaderして、読み取り位置を記憶することができます。ただし、シーク機能はありません。

あなたが言及したように、レコードは複数行にすることができますが、すべてのレコードは停止シーケンスで区切られています。これを考えると、次RandomAccessFileのように使用できます。

  1. サイズが8kbyte b[]としましょう(これはパフォーマンス上の理由によるものです)

  2. このバッファ内のファイルから 8k を読み取り、区切り文字を見つけようとします。見つからない場合は、8k の別のブロックを読み取りますが、以前にデータを何らかのStringBuilder構造に追加します。

  3. 区切り文字が見つかった場合、区切り文字の位置は、最後の区切り文字が見つかってから処理されたバイト数によって決まります (簡単な計算を行う必要があります)。

トリッキーな部分は、レコード区切り文字が 1 文字よりも長い場合ですが、それは大きな問題になるはずです。

于 2013-09-20T07:21:03.743 に答える
1

次の一連の java.io デコレータを使用します。

   InputStreamReader    <-- reader, the top reader
   CountingInputStream  <-- cis, stores the position (from Google Guava)
   BufferedInputStream  <-- speeds up file reading
   FileInputStream

readLine()次に、行区切りまで文字を 1 つずつ読み取るメソッドを実装して、このトップ リーダーから読み取ります。BufferedReader は、固定サイズのバッファー全体を読み取ることで現在の位置が損なわれるため、使用しません。

したがって、問題を正しく解決できれば、アルゴリズムは次のように単純になります。

  1. long lineStartPosition = cis.getCount();
  2. String s = readLine(reader);
  3. if(s.equals(DELIMITER)) { storeToIndex(lineStartPosition, recordData); }
于 2013-09-20T21:59:31.203 に答える
0

すべてのデータ ファイルを読み取り、区切り文字が見つかった場所を記録し、このメタデータを別のファイルに保存できます。これで、メタデータを使用してファイル内を移動できます (ある区切り文字から別の区切り文字にジャンプします)。データ ファイルが変更されるたびに、再スキャンしてメタデータを再生成する必要があります。

于 2013-09-20T07:09:35.873 に答える