【问题标题】:estimating size of file on disk when using ObjectOutputStream使用 ObjectOutputStream 时估计磁盘上文件的大小
【发布时间】:2012-06-27 08:18:42
【问题描述】:

我正在尝试将我的空间数据从表写入文件。但在写入磁盘之前,我需要知道磁盘上数据的确切大小。例如,假设我正在使用以下代码写入磁盘:

    FileOutputStream fos = new FileOutputStream("t.tmp",false);
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeInt(gid);
    oos.writeUTF(fullname);
    oos.writeInt(d.shape.length);
    oos.write(d.shape);

    oos.close();
    fos.close();

我在想磁盘上的文件大小等于:

size= 4B {for gid, int} + fullname.getBytes.length() {string} + 4B {d.shape.length, int} + d.shape.length

但实际上,这与磁盘上的实际文件大小有很大不同。

我还注意到,即使使用 ObjectOutputstream 创建一个空文件也会导致磁盘上有 4B 空间。

对如何计算磁盘上的文件大小有任何帮助吗?

(我无法将数据写入磁盘然后读取实际大小。这会降低性能。相反,我需要根据存储在内存中的数据值来计算磁盘上的数据大小。)

【问题讨论】:

  • 你真的需要一个ObjectOutputStream吗?你用writeObject()吗?还是您的示例代码和您得到的一样复杂?看来您最好使用DataOutputStream。 (ObjectOutputStream 写入流标头信息并跟踪对已写入对象的引用,因此它可以简单地写入引用等 - 所有这些都会阻碍您预先计算大小的努力。)
  • 另外 - 您能否详细说明您在写入字节后使用文件系统所感受到的性能问题?
  • 就性能问题而言。我的表中有 40GB 的数据,其中许多是空间数据。我以存储在每个文件中的数据的总大小小于某个值 (max_file_size) 的方式对表进行分区。在第一轮中,我计算了我的表格每一行在磁盘上的大小,在接下来的几轮中,我将它们中的许多相加,以使文件总大小小于 max_file_size。因此,写入每个文件然后测量磁盘上的实际文件大小不是一种选择。
  • 就 ObjectOutputStream 的使用而言,我不确定这是否是写入磁盘的最佳方式。除了作为可变长度字节数组的几何之外,我还有几个字段可以键入 varchar(x)。我认为 ObjectOutputStream 将是读取/写入文件的最简单方法。事实上,到目前为止它运行良好。唯一的问题是我必须提前根据数据值计算磁盘上的大小。有什么建议吗?
  • ObjectOutputStream 的用处在于它能够自动序列化复杂的对象图。但是,这似乎不是您正在做的事情,因此我建议您放弃ObjectOutputStream,而只需使用DataOutputStream 自己编写字节。 DataOutputStream 不会自动执行任何操作,因此您将能够进行您期望的大小计算。注意 DataOutputStream.writeUTF() 的 Javadoc,因为它写入 2 个字节的长度数据,然后是字符串。

标签: java size fileoutputstream objectoutputstream


【解决方案1】:

我正在尝试将我的空间数据从表写入文件。但在写入磁盘之前,我需要知道磁盘上数据的确切大小。

您不应该使用ObjectOutputStreamObjectOutputStream 可以自动为您序列化复杂的对象图 - 但这似乎不是您的要求之一。作为此序列化的一部分,ObjectOutputStream 写入一些流标头信息(这是您在开始时发现的 4 个字节),并且还跟踪先前写入的对象,以便它可以写入特殊标记值而不是写出整个再次对象。

相反,只需使用DataOutputStream。它提供了您想要的相同功能:

数据输出流允许应用程序以可移植的方式将原始 Java 数据类型写入输出流。然后应用程序可以使用数据输入流来读回数据。

FileOutputStream fos = new FileOutputStream("t.tmp",false);
DataOutputStream dos = new DataOutputStream(fos);
dos.writeInt(gid);                 // write 4 bytes
dos.writeUTF(fullname);            // write 2 bytes of length, then variable length string (UTF encoded)
dos.writeInt(d.shape.length);      // write 4 bytes
dos.write(d.shape);                // write a variable length byte array

dos.close();
fos.close();

这里不会有任何意外(前提是您知道您的 UTF 编码的 String 将结束多少字节),并且您可以通过算术计算出确切的文件大小。

(如果您处理的字符串不仅仅等同于一个字符一个字节,您可以先使用字符集编码器将字符串呈现为字节数组)。

【讨论】:

  • 非常感谢。这解决了这个问题。似乎我需要刷新对 Java 流的记忆。你推荐任何关于 Java 流的教程吗?
【解决方案2】:

假设您不介意浪费一些内存,您可以先将其全部写入ByteArrayOutputStream,然后获取大小。

ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(boas);
oos.writeInt(gid);
oos.writeUTF(fullname);
oos.writeInt(d.shape.length);
oos.write(d.shape);

oos.close();
boas.close();
int size = boas.size();

【讨论】:

  • 太棒了。如果我想计算一行数据的大小,这很好用。我正在为每一行数据计算这个并将其存储在我的表中 block_size 列下。然后我选择了很多行,以至于它们的累积 block_size 小于某个值。问题是两行的block_size值之和大于写入磁盘的两行的文件大小。关于如何解决这部分的任何想法?
  • @reza 如果你需要它是准确的,你必须先序列化内存中的整个数据结构(或到一个临时文件)Java序列化会在一定程度上删除重复的对象和字符串,所以你不能假设双倍数据将占用两倍的空间。
  • 谢谢,我最终使用了 DataOutputStream。这样,我计算的大小与磁盘上的文件大小完全相同。感谢您的建议。
猜你喜欢
  • 2014-09-12
  • 2011-05-17
  • 2020-10-17
  • 2011-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-25
  • 2013-02-17
相关资源
最近更新 更多