在java中读写大文件

时间:2014-02-19 20:26:33

标签: java file hex

我的想法是创建一个读取文件的软件(不能“自然地”读取,但它包含一些图像),将其数据转换为十六进制,查找PNG块(一种标记,位于.png文件的开头和结尾),并将结果数据保存在不同的文件中(从十六进制中取回后)。我在Java中这样做,使用这样的代码:

// out is where to show the result and file is the source
public static void hexDump(PrintStream out, File file) throws IOException {
    InputStream is = new FileInputStream(file);
    StringBuffer Buffer = new StringBuffer();

    while (is.available() > 0) {
        StringBuilder sb1 = new StringBuilder();

        for (int j = 0; j < 16; j++) {
            if (is.available() > 0) {
                int value = (int) is.read();
                // transform the current data into hex
                sb1.append(String.format("%02X ", value));
            }
        }

        Buffer.append(sb1);

        // Should I look for the PNG here? I'm not sure
    }
    is.close();
    // Print the result in out (that may be the console or a file)
    out.print(Buffer);

}

我确信在打开大文件时,还有其他方法可以使用较少的“机器资源”。如果您有任何想法,请告诉我。谢谢!

这是我第一次发帖,所以如果有任何错误,请帮我纠正。

3 个答案:

答案 0 :(得分:0)

正如Erwin Bolwidt在评论中所说,首先是不要转换为十六进制。如果由于某种原因你必须转换为十六进制,退出将内容附加到两个缓冲区,并始终使用StringBuilder,而不是StringBuffer。 StringBuilder可以比StringBuffer快3倍。

另外,使用BufferedReader缓冲文件读取。使用FileInputStream.read()一次读取一个字符非常慢。

答案 1 :(得分:0)

执行此操作的一种非常简单的方法(可能非常快)是将整个文件读入内存(作为二进制数据,而不是十六进制转储),然后搜索标记。

这有两个限制:

  • 它只处理长度最大为2 GiB的文件(Java数组的最大大小)
  • 它需要大块内存 - 可以通过读取器更小的块来优化它,但这会使算法更复杂

这样做的基本代码是这样的:

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

public class Png {

    static final String PNG_MARKER_HEX = "abcdef0123456789"; // TODO: replace with real marker
    static final byte[] PNG_MARKER = hexStringToByteArray(PNG_MARKER_HEX);

    public void splitPngChunks(File file) throws IOException {
        byte[] bytes = Files.readAllBytes(file.toPath());
        int offset = KMPMatch.indexOf(bytes, 0, PNG_MARKER);
        while (offset >= 0) {
            int nextOffset = KMPMatch.indexOf(bytes, 0, PNG_MARKER);
            if (nextOffset < 0) {
                writePngChunk(bytes, offset, bytes.length - offset);
            } else {
                writePngChunk(bytes, offset, nextOffset - offset);
            }
            offset = nextOffset;
        }
    }

    public void writePngChunk(byte[] bytes, int offset, int length) {
        // TODO: implement - where do you want to write the chunks?
    }
}

我不确定这些PNG块标记是如何正常工作的,我假设它们开始了你感兴趣的数据部分,并且下一个标记开始下一部分数据。 / p>

标准Java中缺少两件事:将十六进制字符串转换为字节数组的代码和用于在另一个字节数组中搜索字节数组的代码。 两者都可以在各种apache-commons库中找到,但我会在PeopleOverflow上包含人们发布的早期问题的答案。您可以将这些逐字复制到Png类中,以使上述代码有效。

<强> Convert a string representation of a hex dump to a byte array using Java?

public static byte[] hexStringToByteArray(String s) {
    int len = s.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2) {
        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16));
    }
    return data;
}

<强> Searching for a sequence of Bytes in a Binary File with Java

/**
 * Knuth-Morris-Pratt Algorithm for Pattern Matching
 */
static class KMPMatch {
    /**
     * Finds the first occurrence of the pattern in the text.
     */
    public static int indexOf(byte[] data, int offset, byte[] pattern) {
        int[] failure = computeFailure(pattern);

        int j = 0;
        if (data.length - offset <= 0)
            return -1;

        for (int i = offset; i < data.length; i++) {
            while (j > 0 && pattern[j] != data[i]) {
                j = failure[j - 1];
            }
            if (pattern[j] == data[i]) {
                j++;
            }
            if (j == pattern.length) {
                return i - pattern.length + 1;
            }
        }
        return -1;
    }

    /**
     * Computes the failure function using a boot-strapping process, where the pattern is matched against itself.
     */
    private static int[] computeFailure(byte[] pattern) {
        int[] failure = new int[pattern.length];

        int j = 0;
        for (int i = 1; i < pattern.length; i++) {
            while (j > 0 && pattern[j] != pattern[i]) {
                j = failure[j - 1];
            }
            if (pattern[j] == pattern[i]) {
                j++;
            }
            failure[i] = j;
        }

        return failure;
    }
}

我修改了最后一段代码,以便能够以零以外的偏移量开始搜索。

答案 2 :(得分:0)

一次一个字节地读取文件将花费大量时间。你可以通过数量级来改善它。您应该在DataInputStream周围BufferedInputStream周围使用FileInputStream,并使用readFully.

一次读取16个字节

然后处理它们,没有转换为十六进制和从十六进制转换,这在这里是非常不必要的,并且通过BufferedOutputStream周围的FileOutputStream,将它们写入输出。 {{1}}而不是将整个文件连接到内存中,并且必须一次性写出来。 当然花费时间,但那是因为它确实存在,而不是因为你必须这样做。