2

ユーザーができるプログラムを作成しようとしています: 1) 連絡先 (名前、電話、電子メール) に人を追加する、2) 連絡先から人を削除する、3) 連絡先からすべてを読み取る。

私がこれを行っている方法は、ユーザーに選択を求め、それぞれが何でもすることです。書き込みの場合は、オブジェクトをファイルに書き込むだけです。削除するには、キーとして使用される「姓」をユーザーに尋ねて(TreeMapを使用しているため)、キーの値(オブジェクト)を削除すると思います。

だから私はここで読むのに問題があります。私は次のようにオブジェクトを読み取ろうとしています:

public void readContact()
{
  TreeMap<String, Contact> contactMap = new TreeMap<String, Contact>();
  try 
  {
    ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(
                                                   new FileInputStream(file)));

    while( in.available() > 0 ) //This line does NOT read  
    {
      Contact c = (Contact)in.readObject();
      contactMap.put(c.getLastName(), c);           
    }

    for(Map.Entry contact : contactMap.entrySet() ) 
    {
      Contact con = contactMap.get( contact.getKey() );
      System.out.println( con.getLastName() + ", " + con.getFirstName() + ": " + con.getPhoneNumber() + "\t" + con.getEmail());
    }
  }
  catch(Exception e)
  {
    System.out.println("Exception caught");
  } 
}

次の理由while(true)が得られるまで、次のようなことを提案しないでください。EOFException

  1. それは例外処理の目的ではないと私は信じています
  2. この後もまだまだやることがあるので、プログラムを終了させるわけにはいきません」
4

7 に答える 7

7

EOFException が発生するまで while(true) のようなことを提案しないでください

それがまさに私が提案することです。答えを探しているとき、このような恣意的な基準に従って解空間を制限することは逆効果です。

なぜなら:

それは例外処理の目的ではないと私は信じています

この例のように、呼び出している API が例外をスローした場合、それをキャッチする以外に選択肢はありません。「例外処理の目的」についてどう考えても、API の設計者が API設計したときに考えたことに左右されます。

この後もまだまだやることがあるので、プログラムを終了させるわけにはいきません」

だから終わらせないで。EOFException,入力を閉じてキャッチし、ループから抜け出します。

私は、実際に信用できるよりも、「例外処理が何のためにあるのか」ということで、より多くのコストのかかるプログラミング時間を浪費しているのを見てきました。

于 2012-12-31T03:57:52.070 に答える
2

- Exceptionsメソッドの呼び出し中に問題が発生したときにアラームを発生させるために使用されるだけでなく、他のさまざまな用途でも使用さThreadsれます。IO

-ファイルの終わりExceptionを示すために使用できます。

-try-catchコンボを使用して上記と連携し、プログラムの流れをスムーズに保ちます。

于 2012-12-31T03:04:54.170 に答える
2
  1. 'ObjectInputStream.available returns 0' は既知の問題です。http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4954570EOFExceptionを参照してください。使用できないため、状況に応じて合理的なアプローチになると思います。キャッチEOFExcepionしてもプログラムは終了しません。
  2. を使用してファイルにオブジェクトの数を書き込むことができます。ObjectOutputStream.writeInt次に、この数値を で読み取り、ObjectInputStream.readInt読み取るオブジェクトの数を知ることができます。
  3. nullEOF マーカーとして使用できます。
  4. オブジェクトを配列、リスト、またはマップとして保存し、それらを 1 で読み取ることができますreadObject
于 2012-12-31T02:58:02.463 に答える
2

例外処理を使用していない答えを探していることは知っていますが、この場合EOFException、すべての入力がいつ読み取られたかを判断するために使用するのが正しい方法だと思います。

EOFExceptionの JavaDoc には、次のように記載されています。

この例外は、主にデータ入力ストリームでストリームの終わりを知らせるために使用されます。他の多くの入力操作は、例外をスローするのではなく、ストリームの最後に特別な値を返すことに注意してください。

そのため、他の手段を使用してファイルの終わりを知らせる入力ストリームがありますが、ObjectInputStream#readObjectを使用ObjectInputStream$BlockDataInputStream#peekByteして読み取るデータがまだあるかどうかを判断し、ストリームの終わりに達したときにをpeekByteスローします。EOFException

そのため、この例外をファイルの終わりに到達したことを示す指標として使用できます。

プログラム フローを中断せずに例外を処理するには、考えられる例外の一部を階層に渡す必要があります。これらはtry - catch、 を呼び出すコード内のブロックによって処理できますreadContact()

EOFException、オブジェクトの読み取りが完了したことを示す指標として単純に使用できます。

public TreeMap<String, Contact> readContact() throws FileNotFoundException,
            IOException, ClassNotFoundException {

    TreeMap<String, Contact> contactMap = new TreeMap<String, Contact>();

    // The following call can throw a FileNotFoundException or an IOException.
    // Since this is probably better dealt with in the calling function, 
    // readContact is made to throw these exceptions instead.
    ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(
                new FileInputStream(file)));

    while (true) {
        try {
            // Read the next object from the stream. If there is none, the
            // EOFException will be thrown.
            // This call might also throw a ClassNotFoundException, which can be passed
            // up or handled here.
            Contact c = (Contact) in.readObject();
            contactMap.put(c.getLastName(), c);

            for (Map.Entry<String, Contact> contact : contactMap.entrySet()) {
                Contact con = contact.getValue();
                System.out.println(con.getLastName() + ", "
                          + con.getFirstName() + ": " + con.getPhoneNumber()
                          + "\t" + con.getEmail());
            }
        } catch (EOFException e) {
            // If there are no more objects to read, return what we have.
            return contactMap;
        } finally {
            // Close the stream.
            in.close();
        }
    }
}
于 2012-12-31T02:55:41.997 に答える
2

ファイルからオブジェクトを読み取るときになぜそんなに問題が発生するのでしょうか。ハッシュマップをファイルに保存し、ファイルから同じものを一度読み取ってから、操作を実行してください。

また、 Db4oのようなオブジェクト指向データベースのいずれかを使用してこれをすばやく行うことをお勧めします。そうすれば、ファイルの終わりの例外について心配する必要はありません。

于 2012-12-31T03:19:48.160 に答える
0

あなたが発見したこと

ファイルに未読のバイトがあるのにFileInputStream.available()0 を返すことを発見しました! どうしてこれなの?これが発生する可能性があります (いくつかの理由で信頼性が低くなります:FileInputStream.available()

  1. this documentationによると、ブロックせずに読み取ることができるバイト数をFileInputStream.available() 概算します
  2. ハードディスク ドライブ自体が、読み取りの途中で動作を変更する場合があります (回転から非回転への移行)。
  3. アクセスしようとしているファイルは、リモート システム上のファイルまたはデバイス ファイルです。
  4. ブロックされているFileInputStream可能性があります

いくつかの代替方法

EOFException に依存してファイルを閉じる代わりに、ファイル内のオブジェクトの数を追跡する (非常に小さい!) バイナリ ファイルを使用できます。(あなたのコードから判断すると、単にオブジェクトをファイルに書き込んでいるように見えます。)私がこれを使用した方法は、

  1. 数値自体が消費するバイト数を保存します
  2. そのバイト数を使用して、数値自体を保存します

たとえば、シリアライゼーション ファイルを初めて作成するときに、バイナリ ファイル ストアを作成できます1 1(これにより、シリアライゼーション ファイル内のオブジェクトの数が 1 バイトを占め、その数が 1 になるように指定されます)。このように、255 個のオブジェクト (符号なしバイトは 2 8-1 == 255 までしか格納できないことに注意してください) の後、別のオブジェクト (オブジェクト番号 256 から 256 2-1 == 65535 まで) を書き込むと、バイナリ ファイルは内容として2 1 0、数値が 2 バイトを占め、1*256 1+0 == 256 であることを指定します。シリアライゼーションが信頼できる場合 ( http://www.ibm.com/ developerworks/library/j-serialtest/index.html )、このメソッドを使用すると、最大 256 個まで保存 (および検出) できます。255-1 バイト (これは、このメソッドが無期限に機能することを意味します)。


コード自体

そのようなものがどのように実装されるかは、次のようになります。

ObjectOutputStream yourOutputStream = new ObjectOutputStream(new FileOutputStream(workingDirectory + File.separatorChar + yourFileName);  //The outputStream
File binaryFile = new File(workingDirectory + File.separatorChar + nameOfFile); //the binary file
int numOfObjects = 0, numOfBytes; //The number of Objects in the file
//reading the number of Objects from the file (if the file exists)
try
{
    FileInputStream byteReader = new FileInputStream(binaryFile);
    numOfBytes = byteReader.read();
    //Read the rest of the bytes (the number itself)
    for (int exponent = numOfBytes; exponent >= 0; exponent--)
    {
        numOfObjects += byteReader.read() * Math.pow(256,exponent);
    }
}
catch (IOException exception)
{
    //if an exception was thrown due to the file not existing
    if (exception.getClass() == FileNotFoundException.class)
    {
        //we simply create the file (as mentioned earlier in this answer)
         try 
         {
             FileOutputStream fileCreator = new FileOutputStream(binaryFile);
             //we write the integers '1','1' to the file 
             for (int x = 0; x < 2; x++) fileCreator.write(1);
             //attempt to close the file
             fileCreator.close();
         }
         catch (IOException innerException)
         {
              //here, we need to handle this; something went wrong
              innerException.printStackTrace();
              System.exit(-1);
         }
    }
    else
    {
         exception.printStackTrace();
         System.exit(-2);
    }
}

yourOutputStreamこれで、ファイル内のオブジェクトの数がわかりました ( を呼び出したときに、もう 1 つのオブジェクトが書き込まれたことを示すためにバイトを更新する方法を理解するのはあなたに任せますwriteObject(yourObject);。出勤する必要があります)。

編集:yourOutputStream内のすべてのデータを上書きするか、データをbinaryFile追加します。RandomAccessFileファイル内の任意の場所にデータを挿入する方法であることがわかりました。繰り返しますが、詳細はお任せします。しかし、あなたはそれをしたいです。

于 2013-08-06T04:12:05.003 に答える
-1

最後のオブジェクトを null として書き込むことができます。そして、読み取り側でnullになるまで繰り返します。例えば

while ((object = inputStream.readObject()) != null) {
  // ...
}
于 2014-11-17T10:22:56.397 に答える