将字节矩阵写入文本文件的最有效方法

时间:2012-06-20 04:24:43

标签: java byte text-files bufferedwriter

我在Java中将3D字节矩阵(作为无符号字节)写入文本文件时遇到问题。 矩阵是3D,看起来像wMatrix3D[k][j][i]。我想将它用于文本文件,因此值由空格分隔,并且每1,000个值都有一个换行符(每行有1,000个像素值,1,000行表示图像的1,000 x 1,000文本文件)。

目前,我这样做:

BufferedWriter out = new BufferedWriter(new FileWriter(imgout));

//Parse Headers
for(int countHeaderLines = 0; countHeaderLines < numHeaders; countHeaderLines+=1){
    out.write(headers[countHeaderLines] + "\n");
}
System.out.println("Wrote Headers");

//Parse 1,000,000 x 1,000 2D matrix into 3D (1,000 x 1,000) x 1,000 matrix 
System.out.println("Writing main matrix to text...");

//String slice = new String();
for(int k = 0; k < numLayers; k++){
    for(int j = 0;  j < numRows; j++){
        String rowStr = new String();
        for(int i = 0; i < numColumns; i++){
            rowStr += Integer.toString((Integer.valueOf(wMatrix3D[k][j][i]) & 0xFF)) + " ";
        }
    out.write(rowStr + "\n");
    }

/*if( (k+1) % 5 == 0){
slice = new String();
out.write(slice);
System.out.println("Writing Set of 10:" + k);
}*/

System.out.println("k: " + k);
}

但是,这种方式非常慢。有没有更有效的方法来做到这一点?在C中我使用“fprintf”没有问题,但在Java中,我无法让它运行良好。

4 个答案:

答案 0 :(得分:0)

你正在一个文件上写10亿个整数,所以假设每个数字至少有1个字节+每个空格1个字节,忽略换行符,你已经是必须写在磁盘上的2千兆字节数据。

现在这是一个很好的数量肯定需要时间,但您可以考虑在BufferedWriter周围使用PrintWriter,这样您就可以直接使用其他许多可以更优化的操作,例如

void print(int i) 

此外,您的矩阵似乎存储为字符串(因为您使用Integer.valueOf(...))将字符串转换为整数,然后您和它们再次转换回来。我想你可以节省一些时间已经把所有内容都作为int (或者无论如何这似乎更有意义)。

还要考虑使用StringBuilder而不是像你一样在长字符串上连接。但我认为你不应该连接任何东西(使用PrintWriter并优化数据结构)。

如果您的数据不是人类可读的,那么只需跳过字符串并保存二进制数据,您将获得至少10倍的速度。

答案 1 :(得分:0)

你必须记住,你有效地执行了10亿次中央循环。没有什么可以做的,以减少它,因为这是3d数组中的值的数量。你所希望做的就是让循环尽可能紧。

通过'添加'(使用+运算符)创建字符串效率非常低。减少您创建的字符串数量(每次使用+运算符连接它们时都会发生这种情况)会有所帮助。请考虑使用StringBuilder。

StringBuilder rowStr = new StringBuilder();
    ...
    rowStr.append(...);
    ...
rowStr.append("\n");
out.write(rowStr.toString());

另外,不要对Integer.valueOf的结果使用Integer.toString。尝试转换为int并处理结果为负的情况。

任何时候你都可以在内循环中删除新对象的构造,你将节省时间。

答案 2 :(得分:0)

Java中的{p> Arraybyte都是Serializable。只需通过ObjectOutputStream将数组写入文件。

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;

class MatrixWriter {
    public static void main(String... a) throws FileNotFoundException,
            IOException, ClassNotFoundException {

        byte[][][] data = new byte[][][] {
                { { 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 } },
                { { 9, 10 }, { 11, 12 } } };
        String filename = "data.ser";

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
                filename));
        out.writeObject(data);
        out.close();

        ObjectInputStream in = new ObjectInputStream(new FileInputStream(
                filename));
        byte[][][] array = (byte[][][]) in.readObject();
        in.close();

        for (byte[][] b : array) {
            System.out.print("[");
            for (byte[] c : b) {
                System.out.print(Arrays.toString(c));
            }
            System.out.println("]");
        }
    }
}

答案 3 :(得分:0)

您在其他答案中获得的大部分输入都是正确的。但找出导致性能问题的最佳方法是分析您的应用程序。 Netbeans IDE实际上内置了一个不错的分析器。如果您要分析您的应用程序,最好使用矩阵的子集(我尝试了200 ^ 3次迭代),您会注意到字符串操作是您的问题。

enter image description here

每次连接字符串时,您都在后台创建String对象。正如其他答案让你意识到的那样,你做了十亿次。因此,迈向良好解决方案的第一步是在每次迭代时停止创建对象。这可以通过使用StringBuilder使用append()来连接值和setLength(0)来重用字符数组来完成。这将产生轻微的改进,因为每次迭代只创建一个字符串。另一种方法是使用您想要编写的每个字符串调用out.write(),而不必将它们连接起来。您可以在下面看到结果:

使用StringBuilder:

enter image description here

直接发送到BufferedWriter:

enter image description here

仅供参考,我读过Memory Mapped Files提高文件写入速度。我见过的唯一缺点是你必须事先知道预期的文件大小。