【问题标题】:Find a string in a zipped file without unzipping the file在压缩文件中查找字符串而不解压缩文件
【发布时间】:2020-05-24 02:36:16
【问题描述】:

有没有办法在压缩文件夹内的文件中搜索字符串而不解压缩文件?

我的情况是一年中有超过 100 万个文件被压缩。 例如 2008_01、2008_02 等。 我只需要提取/解压缩文件中具有特定序列号的文件。

我唯一能找到的是将数据解压缩到临时位置以执行搜索,但手动解压缩数据需要 45-60 分钟。所以我假设代码执行该任务需要同样长的时间,而且我没有那么多可用空间。

请帮忙。

【问题讨论】:

  • 所以您用空间换取了搜索时间。不管压缩算法是什么,如果序列号会继续存在,我认为你应该制作(并保持维护)所有序列号的索引文件(或数据库),这样你就可以查找所需的文件。为什么要在可以进行索引搜索时搜索所有文件
  • 我相信您应该能够将文件解压缩为流并搜索流 - 根据文件大小,您可以将整个文件保存在内存中然后保存,或者只是重新提取匹配。
  • 您是否有 100 万个 zip 文件,每个文件包含 1 个文件?或者,一个包含一百万个文件的 Zip 文件?还是 N 个 zip 文件,每个文件包含 M 个文件(其中 NxM == 100 万)?根据您的情况,解决方案会有所不同。
  • 每个压缩文件夹包含超过 10 万个文件。所以说实话,有超过 100 万个文件可供查询。
  • 你知道你需要事先搜索哪些文件(即它们的完整路径)吗?因为您可以从 .zip 中提取特定文件的文件内容,而无需解压缩整个存档。

标签: c# string zip archive


【解决方案1】:

很遗憾,没有办法做到这一点。 zip 格式维护一个显示文件名和目录结构的未压缩清单,但文件本身的内容是压缩的,因此文件中的任何字符串在文件解压缩之前都不会匹配您的搜索。

几乎所有通用文件压缩格式(7zip、gzip、rar 等)都存在同样的限制。您实际上是在以 CPU 周期为代价来回收磁盘空间。

【讨论】:

  • 不是我想听到的答案,但至少我知道我的选择。我将尝试不同的方法将文件移动到临时位置以查询文件的序列号并删除不匹配的文件。
  • 正如@netmage 上面指出的那样,您不一定必须将所有内容解压缩到文件系统(取决于您的情况)
  • 是的,这是我将考虑的方法之一。我将为每个选项创建一个类,看看哪个更快。底线是我需要很长时间才能完成。
  • 我相信,通过对压缩格式的深入了解,可以扫描比特流以获得所需的压缩匹配,但我不确定它是否比解压缩更有效。
【解决方案2】:

使用一些扩展方法,您可以扫描Zip 文件。我认为您无法通过尝试并行扫描单个 zip 来获得任何好处,但您可能可以并行扫描多个 zip 文件。

public static class ZipArchiveEntryExt {
    public static IEnumerable<string> GetLines(this ZipArchiveEntry e) {
        using (var stream = e.Open()) {
            using (var sr = new StreamReader(stream)) {
                string line;
                while ((line = sr.ReadLine()) != null)
                    yield return line;
            }
        }
    }
}

public static class ZipArchiveExt {
    public static IEnumerable<string> FilesContain(this ZipArchive arch, string target) {
        foreach (var entry in arch.Entries.Where(e => !e.FullName.EndsWith("/")))
            if (entry.GetLines().Any(line => line.Contains(target)))
                yield return entry.FullName;
    }

    public static void ExtractFilesContaining(this ZipArchive arch, string target, string extractPath) {
        if (!extractPath.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal))
            extractPath += Path.DirectorySeparatorChar;

        foreach (var entry in arch.Entries.Where(e => !e.FullName.EndsWith("/")))
            if (entry.GetLines().Any(line => line.Contains(target)))
                entry.ExtractToFile(Path.Combine(extractPath, entry.Name));
    }
}

使用这些,您可以使用以下命令搜索 zip 文件:

var arch = ZipFile.OpenRead(zipPath);

var targetString = "Copyright";
var filesToExtract = arch.FilesContain(targetString);

您还可以将它们提取到特定路径(假设没有文件名冲突):

var arch = ZipFile.OpenRead(zipPath);

var targetString = "Copyright";
arch.ExtractFilesContaining(targetString, @"C:\Temp");

您可以将ExtractFilesContaining 修改为例如将年月添加到文件名中有助于避免冲突。

【讨论】:

    猜你喜欢
    • 2020-04-12
    • 1970-01-01
    • 2010-09-05
    • 1970-01-01
    • 1970-01-01
    • 2013-10-30
    • 1970-01-01
    • 2013-03-09
    • 1970-01-01
    相关资源
    最近更新 更多