我正在处理一个处理大量数据的项目。 我有很多(数千)zip文件,每个文件包含一个简单的txt文件,包含数千行(大约80k行)。 我目前正在做的是:
for(File zipFile: dir.listFiles()){
ZipFile zf = new ZipFile(zipFile);
ZipEntry ze = (ZipEntry) zf.entries().nextElement();
BufferedReader in = new BufferedReader(new InputStreamReader(zf.getInputStream(ze)));
...
通过这种方式,我可以逐行读取文件,但它的定义太慢了。 鉴于需要读取大量文件和行,我需要以更有效的方式阅读它们。
我找了一种不同的方法,但我找不到任何东西。 我认为我应该使用的是用于密集I / O操作的java nio API,但我不知道如何将它们与zip文件一起使用。
真的很感激任何帮助。
谢谢,
马
答案 0 :(得分:3)
我有很多(数千个)zip文件。压缩文件各约30MB,而zip文件中的txt约为60/70 MB。使用此代码读取和处理文件需要大约15个小时,但这取决于。
让我们做一些背后的计算。
假设您有5000个文件。如果处理它们需要15个小时,这相当于每个文件约10秒。这些文件大约每个30MB,因此吞吐量约为3MB / s。
这比ZipFile
可以解压缩的速度慢一到两个数量级。
磁盘是否有问题(它们是本地磁盘还是网络共享?),或者是大部分时间都在进行的实际处理。
确定最佳方法是使用分析器。
答案 1 :(得分:1)
迭代zip文件的正确方法
final ZipFile file = new ZipFile( FILE_NAME );
try
{
final Enumeration<? extends ZipEntry> entries = file.entries();
while ( entries.hasMoreElements() )
{
final ZipEntry entry = entries.nextElement();
System.out.println( entry.getName() );
//use entry input stream:
readInputStream( file.getInputStream( entry ) )
}
}
finally
{
file.close();
}
private static int readInputStream( final InputStream is ) throws IOException {
final byte[] buf = new byte[ 8192 ];
int read = 0;
int cntRead;
while ( ( cntRead = is.read( buf, 0, buf.length ) ) >=0 )
{
read += cntRead;
}
return read;
}
Zip文件由几个条目组成,每个条目都有一个包含当前条目中字节数的字段。因此,很容易迭代所有zip文件条目而无需实际的数据解压缩。 java.util.zip.ZipFile接受文件/文件名,并使用随机访问在文件位置之间跳转。另一方面,java.util.zip.ZipInputStream正在使用流,因此它无法自由跳转。这就是为什么它必须读取和解压缩所有zip数据才能达到每个条目的EOF并读取下一个条目标题。
这是什么意思?如果您的文件系统中已有zip文件 - 无论您的任务如何,都使用ZipFile进行处理。作为奖励,您可以按顺序或随机访问zip条目(性能损失相当小)。另一方面,如果您正在处理流,则需要使用ZipInputStream按顺序处理所有条目。
这是一个例子。包含三个0.6Gb条目的zip存档(总文件大小= 1.6Gb)使用ZipFile在0.05秒内迭代,使用ZipInputStream在18秒内迭代。
答案 2 :(得分:0)
您可以像这样使用新文件API:
Path jarPath = Paths.get(...);
try (FileSystem jarFS = FileSystems.newFileSystem(jarPath, null)) {
Path someFileInJarPath = jarFS.getPath("/...");
try (ReadableByteChannel rbc = Files.newByteChannel(someFileInJarPath, EnumSet.of(StandardOpenOption.READ))) {
// read file
}
}
代码适用于jar文件,但我认为它也适用于拉链。
答案 3 :(得分:0)
您可以尝试此代码
try
{
final ZipFile zf = new ZipFile("C:/Documents and Settings/satheesh/Desktop/POTL.Zip");
final Enumeration<? extends ZipEntry> entries = zf.entries();
ZipInputStream zipInput = null;
while (entries.hasMoreElements())
{
final ZipEntry zipEntry=entries.nextElement();
final String fileName = zipEntry.getName();
// zipInput = new ZipInputStream(new FileInputStream(fileName));
InputStream inputs=zf.getInputStream(zipEntry);
// final RandomAccessFile br = new RandomAccessFile(fileName, "r");
BufferedReader br = new BufferedReader(new InputStreamReader(inputs, "UTF-8"));
FileWriter fr=new FileWriter(f2);
BufferedWriter wr=new BufferedWriter(new FileWriter(f2) );
while((line = br.readLine()) != null)
{
wr.write(line);
System.out.println(line);
wr.newLine();
wr.flush();
}
br.close();
zipInput.closeEntry();
}
}
catch(Exception e)
{
System.out.print(e);
}
finally
{
System.out.println("\n\n\nThe had been extracted successfully");
}
这段代码工作得很好。
答案 4 :(得分:0)
英特尔已经改进了zlib版本,Java使用内部peroform zip / unzip。它要求您使用Interl IPP paches修补zlib源。 我做了一个benchmark,吞吐量增加了1.4倍到3倍。
答案 5 :(得分:0)
使用Java Performance的建议,就像answer from Wasim Wani,that from Satheesh Kumar一样:对ZIP条目进行迭代以获取每个InputStream
的内容,并且关于他们的一些事情,我建立了自己的解决方案。
在我的情况下,处理是瓶颈,因此我从一开始就大规模启动并行提取,在entries.hasMoreElements(),
上进行迭代,并将每个结果放入我从处理中消耗的ConcurrentLinkedQueue
中线。我的ZIP包含一组表示序列化Java对象的XML文件,因此我的“提取”包括反序列化这些对象,而那些反序列化的对象就是放在队列中的那些对象。
对我来说,与我以前从ZIP顺序获取每个文件并对其进行处理的方法相比,它具有一些优势:
有一个缺点:包括并行性时,流控制要稍微复杂一些。