java 在java中读取对象直到文件结束
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14095713/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Reading Objects until End of File in java
提问by FudgeNouget
I'm trying to write a program where the user can: 1) Add a person to the contact (name, phone, email), 2) Remove a person from the contacts, 3) Read all from contact.
我正在尝试编写一个程序,用户可以:1)将一个人添加到联系人(姓名、电话、电子邮件),2)从联系人中删除一个人,3)从联系人中读取所有内容。
The Way I'm doing this is I'm asking for the user for their choice and respectively does whatever. For writing, I simply write an object to the file. For removing, I think I'll be asking the user for "last name" which will be used as the KEY (since I'm using a TreeMap)and will remove the value (object) at the key.
我这样做的方式是我要求用户做出选择并分别做任何事情。对于写入,我只是将一个对象写入文件。对于删除,我想我会要求用户提供“姓氏”,该名称将用作 KEY(因为我使用的是 TreeMap)并将删除该键处的值(对象)。
So I'm having a problem with reading here. I'm trying to read the object like so:
所以我在这里阅读有问题。我正在尝试像这样读取对象:
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");
}
}
Please do not suggest doing something like while(true)
until I get the EOFException
because:
while(true)
在我得到之前,请不要建议做类似的事情,EOFException
因为:
- that isn't what exception handling is for I believe
- I still have more things to do after this so I can't have the program terminating'
- 我相信这不是异常处理
- 在这之后我还有更多的事情要做,所以我不能终止程序'
回答by user207421
Please do not suggest doing something like while(true) until I get the EOFException
在我得到 EOFException 之前,请不要建议做类似 while(true) 的事情
That is exactly what I suggest. When you are searching for answers it is counter-productive to circumscribe the solution space according to arbitrary criteria like this.
这正是我的建议。当你在寻找答案时,根据这样的任意标准来限制解空间会适得其反。
because:
that isn't what exception handling is for I believe
因为:
我相信这不是异常处理
When an API that you are calling throws an exception, as this one does, you don't have any choice but to catch it. Whatever you may think about 'what exception handling is for', you are subject to what the designers of the API thoughtwhen theydesigned the API.
当您调用的 API 抛出异常时,就像这个一样,您别无选择,只能捕获它。不管你想“什么异常处理是”,你受什么API的设计者认为,当他们设计的API。
I still have more things to do after this so I can't have the program terminating'
在这之后我还有更多的事情要做,所以我不能终止程序'
So don't terminate it. Catch EOFException,
close the input, and break out of the loop.
所以不要终止它。CatchEOFException,
关闭输入,并跳出循环。
I have seen more costly programming time wasted over 'what exception handling is for' than I can really credit.
我已经看到比我真正相信的更多的浪费在“异常处理的用途”上的编程时间。
回答by Modus Tollens
I know that you are looking for an answer that is not using exception handling, but I believe in this case using EOFException
to determine when all input has been read is the right way.
我知道您正在寻找不使用异常处理的答案,但我相信在这种情况下使用EOFException
来确定何时读取所有输入是正确的方法。
The JavaDoc of EOFExceptionstates that
EOFException的 JavaDoc指出
This exception is mainly used by data input streams to signal end of stream. Note that many other input operations return a special value on end of stream rather than throwing an exception.
此异常主要由数据输入流用于表示流结束。请注意,许多其他输入操作在流结束时返回一个特殊值,而不是抛出异常。
So, there are input streams that use other means to signal an end of file, but ObjectInputStream#readObject
uses ObjectInputStream$BlockDataInputStream#peekByte
to determine if there is more data to read, and peekByte
throws an EOFException
when the end of the stream has been reached.
因此,有些输入流使用其他方式来表示文件结束,但ObjectInputStream#readObject
用于ObjectInputStream$BlockDataInputStream#peekByte
确定是否有更多数据要读取,并在到达流末尾时peekByte
抛出EOFException
。
So it is feasible to use this exception as an indicator that the end of the file has been reached.
所以用这个异常作为已经到达文件末尾的指示器是可行的。
To handle the exceptions without interrupting the program flow, some of the possible exceptions should be passed up in the hierarchy. They can be handled by a try - catch
block in the code that calls readContact()
.
为了在不中断程序流程的情况下处理异常,一些可能的异常应该在层次结构中向上传递。它们可以由try - catch
调用readContact()
.
The EOFException
can simply be used as an indicator that we are done reading the objects.
该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();
}
}
}
回答by Evgeniy Dorofeev
- 'ObjectInputStream.available returns 0' is a known problem, see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4954570, and since we cannot use it I think
EOFException
would be a reasonable approach in your situation. CatchingEOFExcepion
will not terminate your program. - You could write the number of objects to your file with
ObjectOutputStream.writeInt
, and then you would read this number withObjectInputStream.readInt
and know how many objects to read - You could use
null
as EOF marker. - You could save your objects as an array or List or even Map and then read them with one
readObject
.
- 'ObjectInputStream.available 返回 0' 是一个已知问题,请参阅http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4954570,由于我们无法使用它,我认为
EOFException
在您的情况下这是一个合理的方法。捕获EOFExcepion
不会终止您的程序。 - 您可以使用 将对象的数量写入您的文件
ObjectOutputStream.writeInt
,然后您将读取此数字ObjectInputStream.readInt
并知道要读取的对象数量 - 您可以
null
用作 EOF 标记。 - 您可以将对象保存为数组或 List 或什至 Map,然后使用一个
readObject
.
回答by Kumar Vivek Mitra
-Exceptions
are not onlyused in order to raise an alarm when something goes wrong while calling a method, but are also used in Threads
and IO
with various other uses.
-Exceptions
是不是只为了当同时调用一个方法,但也用不顺心的事发出警报使用Threads
和IO
各种其他用途。
-You can use Exception
to indicate end of the file.
-您可以使用Exception
来指示文件的结尾。
-Use the try-catch
combo to work along with the above to keep the flow of the program smooth.
-使用try-catch
组合与上述一起工作,以保持程序的流畅。
回答by vels4j
Why so much trouble while reading object from file, just save a hash map into a file and read the same once from file then perform any operation.
为什么在从文件中读取对象时如此麻烦,只需将哈希映射保存到文件中并从文件中读取一次然后执行任何操作。
Also I would suggest to use any one of object oriented database like Db4oto do this quickly then you never worry about end of file exception
此外,我建议使用任何一种面向对象的数据库(如Db4o)来快速执行此操作,这样您就不必担心文件结尾异常
回答by Mike Warren
What you have discovered
你发现了什么
You found out about FileInputStream.available()
returning 0 even though there are unread bytes in the file! Why is this? This could happen (rendering FileInputStream.available()
unreliable for a couple of reasons:
您发现即使文件中有未读字节也FileInputStream.available()
返回 0 !为什么是这样?这可能会发生(由于以下几个原因而变得不可靠:FileInputStream.available()
- According to this documentation,
FileInputStream.available()
approximatesthe number of bytes that can be read without blocking - The hard disk drive itself might change its operation mid-read (go from spinning to not spinning)
- The file you are trying to access is either a file on a remote system or a device file : May the FileInputStream.available foolish me?
- The
FileInputStream
might be blocked
- 根据此文档,
FileInputStream.available()
近似可以在不阻塞的情况下读取的字节数 - 硬盘驱动器本身可能会在读取过程中改变其操作(从旋转变为不旋转)
- 您尝试访问的文件是远程系统上的文件或设备文件:可能 FileInputStream.available 愚弄我吗?
- 将
FileInputStream
可能被阻止
Some alternative way
一些替代方式
As an alternative to relying on EOFException to close the file, you could use a (very-small!) binary file that keeps track of the number of Objects in your file. (Judging from your code, it looks like you are simply writing Objects to the file.) The way I have used this is just to
作为依赖 EOFException 关闭文件的替代方法,您可以使用(非常小!)二进制文件来跟踪文件中的对象数量。(从您的代码来看,您似乎只是将对象写入文件。)我使用它的方式只是为了
- store the number of bytes the number itself will consume
- using that number of bytes, store the number itself
- 存储数字本身将消耗的字节数
- 使用该字节数,存储数字本身
For example, the first time the serialization file is created, I could make the binary file store 1 1
(which specifies that the number of Objects in the serialization file takes up 1 byte, and that number is 1). This way, after 255 Objects (remember, an unsigned byte can only store up to 28-1 == 255), if I write another Object (Object number 256 up to 2562-1 == 65535), the binary file will have, as contents, 2 1 0
, which specifies that the number takes up 2 bytes and is 1*2561+0 == 256. Provided that the serialization is reliable (good luck on ensuring that: http://www.ibm.com/developerworks/library/j-serialtest/index.html), this method will let you store (and detect) up to 256255-1 bytes (which pretty much means that this method works indefinitely).
例如,第一次创建序列化文件时,我可以制作二进制文件存储1 1
(它指定序列化文件中的对象数占用1个字节,该数字为1)。这样,在 255 个 Objects 之后(记住,一个无符号字节最多只能存储 2 8-1 == 255),如果我再写一个 Object(Object number 256 up to 2562-1 == 65535),二进制文件就会有,作为内容,2 1 0
它指定数字占用 2 个字节并且是 1*256 1+0 == 256。前提是序列化是可靠的(祝你好运:http: //www.ibm.com/ developerworks/library/j-serialtest/index.html),这个方法可以让你存储(和检测)多达 256255-1 字节(这几乎意味着此方法可以无限期地工作)。
The code itself
代码本身
How something like that would be implemented would be something like:
如何实现这样的事情将是这样的:
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);
}
}
Now, we have the number of Objects in the file (I leave it to you to figure out how to update the bytes to indicate that one more Object has been written when yourOutputStream
calls writeObject(yourObject);
; I have to go clock in.)
现在,我们有了文件中对象的数量(我留给你去弄清楚如何更新字节以指示在yourOutputStream
调用时又写入了一个对象writeObject(yourObject);
;我必须打卡。)
Edit: yourOutputStream
is either going to write over all the data in the binaryFile
or append data to it. I just found out that RandomAccessFile
would be a way to insert data into anywhere in the file. Again, I leave details to you. However you want to do it.
编辑:yourOutputStream
要么将覆盖其中的所有数据,binaryFile
要么将数据附加到其中。我刚刚发现这RandomAccessFile
是一种将数据插入文件中任何位置的方法。再一次,我把细节留给你。然而你想这样做。
回答by Akshay
You can write the last object as null. And then iterate till you get a null at the reading side. e.g.
您可以将最后一个对象写为 null。然后迭代直到你在阅读端得到一个空值。例如
while ((object = inputStream.readObject()) != null) {
// ...
}