web-dev-qa-db-ja.com

markSupported()に関係なく、InputStreamを複数回読み取るようにする

_Java.io.InputStream_を複数回再利用できるようにする必要があり、次のコードが機能すると思いましたが、最初にしか機能しません。

コード


_public class Clazz
{
  private Java.io.InputStream dbInputStream, firstDBInputStream;
  private ArrayTable db;

  public Clazz(Java.io.InputStream defDB)
  {
    this.firstDBInputStream = defDB;
    this.dbInputStream = defDB;
    if (db == null)
      throw new Java.io.FileNotFoundException("Could not find the database at " + db);
    if (dbInputStream.markSupported())
      dbInputStream.mark(Integer.MAX_VALUE);
    loadDatabaseToArrayTable();
  }

  public final void loadDatabaseToArrayTable() throws Java.io.IOException
  {
    this.dbInputStream = firstDBInputStream;
    if (dbInputStream.markSupported())
      dbInputStream.reset();

    Java.util.Scanner fileScanner = new Java.util.Scanner(dbInputStream);
    String CSV = "";
    for (int i = 0; fileScanner.hasNextLine(); i++)
      CSV += fileScanner.nextLine() + "\n";
    db = ArrayTable.createArrayTableFromCSV(CSV);
  }

  public void reloadDatabase()//A method called by the UI
  {
    try
    {
      loadDatabaseToArrayTable();
    }
    catch (Throwable t)
    {
      //Alert the user that an error has occurred
    }
  }
}
_

ArrayTableは私のクラスであり、配列を使用してテーブルを操作するためのインターフェイスを提供することに注意してください。

質問


このプログラムでは、reloadDatabase()メソッドが呼び出された直後にデータベースがユーザーに直接表示されるため、メモリ内のオブジェクトへの初期読み取りを保存するソリューションは、データを更新しないため、役に立ちません。 (ブラウザのように考えてください。[更新]を押すと、最初に取得した情報を表示するだけでなく、情報を再度取得する必要があります)。 _Java.io.InputStream_を複数回読み取るにはどうすればよいですか?

10
Supuhstar

必ずしもInputStreamを複数回読み取ることはできません。一部の実装はそれをサポートし、一部はサポートしません。あなたがしているのは、markSupportedメソッドをチェックすることです。これは、同じストリームを2回読み取ることができるかどうかの指標ですが、結果を無視しています。そのメソッドを呼び出して、ストリームを2回読み取ることができるかどうかを確認し、読み取れない場合は、他の調整を行う必要があります。

編集(コメントへの応答):私が答えを書いたとき、私の「他の取り決め」は新しいInputStreamを取得することでした。しかし、あなたが何をしたいのかについてのあなたの質問に対するあなたのコメントを読んだとき、それが可能かどうかはわかりません。操作の基本については、おそらくRandomAccessFileが必要です(少なくともそれが私の最初の推測であり、それが機能した場合はそれが最も簡単です)-ただし、ファイルアクセスの問題が発生します。アプリケーションがファイルにアクティブに書き込み、別のアプリケーションがそのファイルを読み取ると、問題が発生します。正確には、どの問題がOSに依存するので、どのソリューションでもさらにテストが必要になります。 SOに当てはまる別の質問をお勧めします。それを試した人は、おそらくより多くの洞察を得ることができます。

8
Yishai

ストリームにリセットのマークを付けることはありません

public Clazz(Java.io.InputStream defDB)
  {
    firstDBInputStream = defDB.markSupported()?defDB:new BufferedInputStream(defDB);
      //BufferedInputStream supports marking
    firstDBInputStream.mark(500000);//avoid IOException on first reset
  }

public final void loadDatabaseToArrayTable() throws Java.io.IOException
  {
    this.dbInputStream = firstDBInputStream;

    dbInputStream.reset();
    dbInputStream.mark(500000);//or however long the data is

    Java.util.Scanner fileScanner = new Java.util.Scanner(dbInputStream);
    StringBuilder CSV = "";//StringBuilder is more efficient in a loop
    while(fileScanner.hasNextLine())
      CSV.append(fileScanner.nextLine()).append("\n");
    db = ArrayTable.createArrayTableFromCSV(CSV.toString());
  }

ただし、代わりに、元のArrayTableのコピーを保持し、必要に応じてコピーすることもできます(または、作成した文字列を再構築することもできます)

このコードは文字列を作成してキャッシュするため、入力ストリームを安全に破棄し、readCSVを使用してArrayTableを構築できます。

  private String readCSV=null;
  public final void loadDatabaseToArrayTable() throws Java.io.IOException
  {
    if(readCSV==null){

        this.dbInputStream = firstDBInputStream;


        Java.util.Scanner fileScanner = new Java.util.Scanner(dbInputStream);
        StringBuilder CSV = "";//StringBuilder is more efficient in a loop
        while(fileScanner.hasNextLine())
          CSV.append(fileScanner.nextLine()).append("\n");

        readCSV=CSV.toString();
        fileScanner.close();

    }
    db = ArrayTable.createArrayTableFromCSV(readCSV);
  }

ただし、新しい情報が必要な場合は、新しいストリームを作成から読み取る必要があります

2
ratchet freak