[英]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()
一次读取一个字符非常慢。
一种非常简单的方法(可能非常快)是将整个文件读入内存(作为二进制数据,而不是十六进制转储),然后搜索标记。
这有两个限制:
做到这一点的基本代码是这样的:
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类中,以使上面的代码起作用。
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;
}
/**
* 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.