繁体   English   中英

用Java读写大型文件

[英]Reading and writing huge files in java

我的想法是制作一个小的软件来读取文件(无法“自然地”读取,但是其中包含一些图像),将其数据转换为十六进制,查找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);

}

我敢肯定还有另一种方法可以在打开大文件时使用较少的“机器资源”。 如果您有任何想法,请告诉我。 谢谢!

这是我第一次发布,因此如果有任何错误,请帮助我进行更正。

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

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

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

这有两个限制:

  • 它只能处理最大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块标记如何正确工作,我在上面假设它们开始您感兴趣的数据部分,而下一个标记开始数据的下一部分。

标准Java中缺少两件事:将十六进制字符串转换为字节数组的代码,以及在另一个字节数组中搜索字节数组的代码。 两者都可以在各种apache-commons库中找到,但是我将包括那些回答有关StackOverflow早期问题的人员的答案。 您可以将这些逐字记录复制到Png类中,以使上面的代码起作用。

使用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;
}

使用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;
    }
}

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

一次读取一个字节的文件将花费大量时间。 您可以将其提高几个数量级。 您应该在FileInputStream周围的BufferedInputStream周围使用DataInputStream ,并通过readFully.一次读取16个字节readFully.

然后处理它们, 而无需在十六进制之间进行转换,这在这里是完全没有必要的,然后通过FileOutputStream,周围的BufferedOutputStream将它们随即写入到输出中FileOutputStream,而不是将整个文件串联到内存中并且不必编写一劳永逸。 当然 ,这需要时间,但这是因为要这样做,而不是因为您必须那样做。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM