在java中读取对象直到文件结尾

时间:2012-12-30 23:19:22

标签: java generics treemap objectinputstream eofexception

我正在尝试编写一个程序,用户可以:1)向联系人添加人员(姓名,电话,电子邮件),2)从联系人中删除一个人,3)从联系人中读取所有人。

我这样做的方式是我要求用户选择并分别做任何事情。对于写入,我只是将一个对象写入该文件。为了删除,我想我会问用户“姓”将用作KEY(因为我正在使用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. 此后我还有更多事情要做,所以我不能让程序终止'

7 个答案:

答案 0 :(得分:6)

  

在我收到EOFException之前,请不要建议做像while(true)这样的事情

这正是我的建议。当您在寻找答案时,根据这样的任意标准限制解决方案空间会适得其反。

  

,因为:

     

这不是我相信的异常处理

当您正在调用的API抛出异常时,就像这样,您没有任何选择,只能抓住它。无论您如何看待'异常处理是什么',当他们设计API时,您将受到设计者认为的影响。

  

在此之后我还有更多的事要做,所以我不能让程序终止'

所以不要终止它。 Catch EOFException,关闭输入,然后突破循环。

我看到更多的编程时间浪费在'异常处理'上,而不是我真正可以信任的。

答案 1 :(得分: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();
        }
    }
}

答案 2 :(得分:2)

  1. 'ObjectInputStream.available返回0'是一个已知问题,请参阅http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4954570,由于我们无法使用它,我认为EOFException在您的情况下是合理的方法。捕获EOFExcepion不会终止您的计划。
  2. 您可以使用ObjectOutputStream.writeInt向文件中写入对象的数量,然后使用ObjectInputStream.readInt读取此数字并知道要读取的对象数
  3. 您可以使用null作为EOF标记。
  4. 您可以将对象保存为数组或列表,甚至是地图,然后使用一个readObject进行阅读。

答案 3 :(得分:2)

- Exceptions 不仅用于在调用方法时出现问题时发出警报,但也用于{{1和Threads以及其他各种用途。

- 您可以使用IO表示文件末尾

- 使用Exception组合与上述内容一起使用,以保持程序流畅。

答案 4 :(得分:2)

为什么在从文件中读取对象时遇到这么多麻烦,只需将哈希映射保存到文件中,然后从文件中读取一次,然后执行任何操作。

另外我建议使用像Db4o这样的面向对象数据库中的任何一个来快速执行此操作,然后再也不用担心文件结束异常

答案 5 :(得分:0)

您发现了什么

即使文件中有未读字节,您也发现FileInputStream.available()返回0 !为什么是这样?这可能发生(由于以下几个原因,呈现FileInputStream.available()不可靠:

  1. 根据此documentationFileInputStream.available() 近似可以无阻塞地读取的字节数
  2. 硬盘驱动器本身可能会在读取过程中更改其操作(从旋转到不旋转)
  3. 您尝试访问的文件是远程系统上的文件或设备文件:May the FileInputStream.available foolish me?
  4. 可能会阻止FileInputStream

  5. 某种替代方式

    作为依赖EOFException关闭文件的替代方法,您可以使用(非常小的!)二进制文件来跟踪文件中的对象数。 (从您的代码来看,看起来您只是将对象写入文件。)我使用它的方式只是为了

    1. 存储数字本身将消耗的字节数
    2. 使用该字节数,存储数字本身
    3. 例如,第一次创建序列化文件时,我可以使二进制文件存储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调用writeObject(yourObject);时已经写入了一个对象;我得进去。)

      编辑:yourOutputStream要么写入binaryFile中的所有数据,要么将数据附加到其中。我刚刚发现RandomAccessFile是一种将数据插入文件中任何位置的方法。我再次向您详细说明。但是你想要这样做。

答案 6 :(得分:-1)

您可以将最后一个对象写为null。 然后迭代直到你在阅读方面得到一个空值。 e.g。

while ((object = inputStream.readObject()) != null) {
  // ...
}