在Java中读/写大文件

8

i have a binary file with following format :

[N bytes identifier & record length] [n1 bytes data] 
[N bytes identifier & record length] [n2 bytes data] 
[N bytes identifier & record length] [n3 bytes data]

如您所见,我有不同长度的记录。每个记录中都有N字节固定内容,其中包含ID和“记录中数据的长度”。

这个文件非常大,可能包含300万条记录。

我想通过一个应用程序打开此文件,并让用户浏览和编辑记录。(插入/更新/删除记录)

我的初始计划是从原始文件创建索引文件,并为每个记录保留下一个和上一个记录地址,以便轻松向前和向后导航。(一种在文件而不是内存中的链表)

  • 是否有库(Java库)可以帮助我实现此要求?

  • 您认为有哪些推荐或有用的经验?

----------------- 编辑 ----------------------------------------------

感谢指导和建议,

更多信息:

原始文件及其格式超出了我的控制范围(它是第三方文件),我无法更改文件格式。但我必须读取它,让用户浏览记录并编辑其中一些记录(插入新记录/更新现有记录/删除记录),最后将其保存回原始文件格式。

您仍然建议使用数据库而不是普通索引文件吗?

----------------- 第二次编辑 ----------------------------------------------

更新模式下的记录大小是固定的。这意味着更新(编辑)后的记录与原始记录具有相同的长度,除非用户删除记录并创建具有不同格式的另一条记录。

非常感谢


5
使用数据库。即使是SQLite,也可以方便地处理如此大量的数据。 - Vladimir Ivanov
似乎前进导航已经很容易了,但是后退和随机访问却不是。您究竟想要实现什么?您希望用户能够编辑记录,但性能是个问题吗? - JackWilson
@JackWilson :是的,正如你提到的,向前导航很容易,但向后导航是个问题,这就是我考虑使用自定义格式的索引文件的原因。 - mhshams
6个回答

2
认真地说,你不应该使用二进制文件来处理这个问题,你应该使用数据库。
试图将此作为常规文件实现的问题在于操作系统不允许您向现有文件中插入额外的字节。因此,如果您需要插入记录(除了末尾之外的任何位置),更新记录(具有不同的大小)或删除记录,则需要:
1. 重写其他记录(插入/更新/删除点之后的记录)以创建或回收空间;或者 2. 在文件内实现某种自由空间管理。
所有这些都很复杂和/或昂贵。
幸运的是,有一类软件实现了这种事情。它被称为数据库软件。有各种各样的选择,从使用完整的关系型数据库管理系统到像BerkeleyDB文件这样的轻量级解决方案。
对于您的第一次和第二次编辑,数据库仍然更简单。
然而,以下是一个替代方法,可能比使用数据库在此用例中表现更好,而不需要进行复杂的自由空间管理。
1. 读取文件并构建将ID映射到文件位置的内存索引。 2. 创建一个第二个文件来保存新的和更新的记录。 3. 执行记录添加/更新/删除: - 添加通过将新记录写入第二个文件的末尾,并添加一个索引条目来处理。 - 更新通过将更新后的记录写入第二个文件的末尾,并更改现有的索引条目以指向它来处理。 - 删除通过删除记录键的索引条目来处理。 4. 压缩文件如下: - 创建一个新文件。 - 按顺序读取旧文件中的每个记录,并检查索引是否仍然指向该记录的位置。如果条目仍然指向该记录,则将该记录复制到新文件中。否则跳过它。 - 对于第二个文件,重复步骤4.2。 5. 如果我们成功地完成了上述所有操作,则删除旧文件和第二个文件。
请注意,这依赖于能够在内存中保留索引。如果不可行,则实现将更加复杂...并且更像数据库。

请看编辑后的版本,您还认为使用数据库是更好的解决方案吗? - mhshams

2
拥有一个数据文件和一个索引文件将是这种实现的一般基本想法,但在重复数据更新/删除等操作时,你很可能会发现自己处理数据碎片化。这种项目本身应该是一个单独的项目,不应该是你主要应用程序的一部分。然而,从本质上讲,你需要的是一个数据库,因为它是专门为这些操作和用例设计的,而且还可以让你搜索、排序和扩展(修改)你的数据结构,而无需重新设计内部(定制)解决方案。
我建议你下载Apache Derby并创建一个本地嵌入式数据库(当你在运行时创建一个新的嵌入式连接时,Derby会为你完成)。它不仅比你自己编写的任何东西都要快,而且会使你的应用程序更易于维护。
Apache Derby是一个单一的jar文件,你可以简单地将其包含在你的项目中并进行分发(如果在你的应用程序中可能涉及到任何法律问题,请检查许可证)。没有必要使用数据库服务器或第三方软件;它全部都是纯Java。
底线是一切都取决于您的应用程序有多大,是否需要在许多客户端之间共享数据,速度是否是您的应用程序的关键方面等等。
对于独立的单用户项目,我建议使用Apache Derby。 对于n-tier应用程序,您可能需要查看MySQLPostgreSQL或()甚至Oracle。 使用已经制作和测试过的解决方案不仅聪明,而且会减少开发时间(和维护工作)。
干杯。

1

通常情况下,让库或数据库为您完成工作会更好。

您可能不想要一个SQL数据库,而且有很多不使用SQL的简单数据库。http://nosql-database.org/列出了其中的122个。

至少,如果您要编写此类代码,建议您阅读其中一个数据库的源代码以了解其工作原理。


根据记录的大小而定,300万并不算太多,我建议您尽可能在内存中保存尽可能多的记录。
你可能会遇到的问题是确保数据一致性以及在发生损坏时恢复数据。第二个问题是高效地处理片段化(某些最聪明的GC工作人员正在处理的事情)。第三个问题很可能是以事务方式维护索引与源数据,以确保没有不一致之处。
虽然这一点起初可能看起来很简单,但要确保数据可靠、可维护且可以高效访问存在重大复杂性。这就是为什么大多数开发人员使用现有的数据库/数据存储库并专注于其应用程序独特的功能。

0

正如其他人所说,使用数据库似乎是更好的解决方案。以下是可用的Java SQL DB:H2, DerbyHSQLDB

如果您想使用索引文件,请查看Berkley DBNo Sql

如果有某些原因需要使用文件,请查看JRecord。它具有

  1. 有几个类用于读写具有可变长度二进制记录的文件(它们是为 Cobol VB 文件编写的)。任何主机/富士通/开放 Cobol VB 文件结构都可以胜任。
  2. 一个编辑器,用于编辑 JRecord 文件。最新版本的编辑器能够处理大型文件(它使用压缩/溢出文件)。编辑器的问题在于必须下载整个文件,并且一次只能有一个用户编辑该文件。

只有在以下情况下,JRecord 解决方案才能正常工作:

  • 有限数量(最好只有一个)的用户都位于同一位置。
  • 快速信息结构。

0
(注:我的回答是关于问题的一般性解决方案,不考虑任何Java库或者像其他答案所提出的使用数据库(库),这可能比重新发明轮子更好)
创建索引的想法很好,从性能上来说将非常有帮助(尽管你写了“索引文件”,我认为它应该保存在内存中)。如果您读取每个条目的ID和记录长度,然后只需通过文件查找跳过数据,生成索引应该相当快。
您还应该考虑编辑功能。特别是如果您做错了(例如删除然后移动所有后续条目以关闭间隙),在这样一个大文件上插入和删除可能会非常慢。
最好的选择是仅将已删除的条目标记为已删除。在插入时,您可以覆盖其中之一或附加到文件末尾。

0

插入/更新/删除记录

将记录插入(而不仅仅是追加)和从文件中删除记录是昂贵的,因为您必须移动文件后续内容以创建新记录的空间或删除其使用的空间。如果更新更改了记录的长度(您说它们是可变长度),则更新也同样昂贵。

您提出的文件格式基本上不适合您要执行的操作。其他人建议使用数据库。如果您不想走得那么远,添加索引文件(如您所建议的)是正确的方法。我建议使索引记录都具有相同的长度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接