字符串是否存在检查20k次

时间:2010-09-10 20:02:04

标签: c# string search

我有一个ITunes库XML文件备份文件 - 大约15 MB。

我的C驱动器上有20K音乐文件,E驱动器上有大约25K文件,文件夹结构完全相同。

我正在遍历第一个位置并逐个文件并检查文件是否在第二个位置。那部分适合我。

现在,对于所有这些重复文件,如果XML驱动器中存在来自E驱动器的文件路径,但XML中不存在C驱动器路径,那么我想从C驱动器中删除该文件。

检查XML文件中是否存在字符串的最佳方法是什么(我必须至少执行20K次)?

6 个答案:

答案 0 :(得分:3)

根据您是否要计算字符串出现的次数,或者您只是检查字符串是否存在,您的方法会略有不同。但是,这些是我考虑做的两种方式:

如果你想用最少的记忆来做这件事:

逐行加载文件(或者,如果您的XML没有像这样格式化,请使用XML解析器逐个节点...我相信有XML解析器可以执行此操作)。对每个字符串执行搜索操作。如果正确覆盖最后一行,一次只能有一个行/节点在内存中。这样做的缺点是它需要更长的时间,文件将更长时间打开。

如果您想快速执行此操作:

将整个文件加载到内存中,不要费心解析它,只搜索每个字符串。

<强> 修改

根据您的说明,我首先收集数组中的所有重复文件名,然后使用我的第一种方法(上面)继续扫描XML文件的每一行。如果您已经在内存中存储了20K文件名,我会毫不犹豫地同时加载整个15MB XML。

答案 1 :(得分:2)

建议:以文本形式加载,使用正则表达式提取所需的字符串(我想它们是用特定标记括起来的)并用它们构建一个哈希列表。您可以使用该列表来检查是否存在。

答案 2 :(得分:2)

这是使用Linq的简单解决方案。运行足够快,一次性使用:

using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;

class ITunesChecker
{
    static void Main(string[] args)
    {
        // retrieve file names
        string baseFolder = @"E:\My Music\";
        string[] filesM4a = Directory.GetFiles(baseFolder, "*.m4a", SearchOption.AllDirectories);
        string[] filesMp3 = Directory.GetFiles(baseFolder, "*.mp3", SearchOption.AllDirectories);
        string[] files = new string[filesM4a.Length + filesMp3.Length];
        Array.Copy(filesM4a, 0, files, 0, filesM4a.Length);
        Array.Copy(filesMp3, 0, files, filesM4a.Length, filesMp3.Length);

        // convert to the format used by iTunes
        for (int i = 0; i < files.Length; i++)
        {
            Uri uri = null;
            if (Uri.TryCreate(files[i], UriKind.Absolute, out uri))
            {
                files[i] = uri.AbsoluteUri.Replace("file:///", "file://localhost/");
            }
        }

        // read the files from iTunes library.xml
        XDocument library = XDocument.Load(@"E:\My Music\iTunes\iTunes Music Library.xml");
        var q = from node in library.Document.Descendants("string")
                where node.ElementsBeforeSelf("key").Where(n => n.Parent == node.Parent).Last().Value == "Location"
                select node.Value;

        // do the set operations you are interested in
        var missingInLibrary = files.Except(q, StringComparer.InvariantCultureIgnoreCase);
        var missingInFileSystem = q.Except(files, StringComparer.InvariantCultureIgnoreCase);
        var presentInBoth = files.Intersect(q, StringComparer.InvariantCultureIgnoreCase);
    }
}

答案 3 :(得分:1)

按字母顺序对您匹配的字符串列表进行排序,然后构建一个索引数组,该数组会告诉您列表的开头位于每个字符的位置,该字符是其中一个字符串的起始字符,可能索引到第二个字符串字符取决于变化的广度,如果您的匹配区分大小写。

使用流来逐字符号读取文件以最小化内存占用,检查索引数组以查看该字符在字符串列表中的开始和结束位置,以便您可以拉出该字符页面,如果有任何以这些开头的内容字符组合。然后继续在页面内部进行过滤,直到剩下一个匹配并且下一个字符匹配为止。

从要匹配的字符串列表中删除该字符串,如果需要,将其放在另一个列表中。然后开始检查下一个字符的索引,并在每次没有匹配时继续这样做。

索引为您提供了更高效的聚合,以最大限度地减少迭代的项目数。

这可以为您提供两个字符的深度索引:

Dictionary<string,int> stringIndex = new Dictionary<char,int>();
for(int i = 0; i < sortedSearchStrings.Length; i++;)
{
    if (!stringIndex.Keys.Contains(sortedSearchStrings[i][0])) stringIndex[sortedSearchStrings[i][0]] = i;
    if (!stringIndex.Keys.Contains(sortedSearchStrings[i][0] + sortedSearchStrings[i][1])) stringIndex[sortedSearchStrings[i][0] + sortedSearchStrings[i][1]] = i;
}

然后在列表中查找起始索引,您只需访问:

int startOfCurrentCharPage = stringIndex[string.Format("{0}{1}", lastChar, currentChar)];

答案 4 :(得分:1)

是否可以直接使用xml文档并跳过第一步?

如果是这样,您可以使用Xml.XmlDocument,并从那里使用xpath.XmlNode.SelectNodes(字符串),使用xpath导航文档。我不知道文档中有什么样的信息,但是第二阶段措辞的方式给出了这样的想法:有时C:\上的路径和E:\上的路径都存在?如果是这样,它就像两个IO.File.Exists检查一样简单,然后是IO.File.Delete()。

我的意思是,不是搜索XML文档N次以获取字符串,而是在搜索文档时删除重复文件,以便只运行一次文档。

我不会使用iTunes或手头有一个库备份来说明它是否可行。

答案 5 :(得分:0)

从XML中读取每个字符串并将其写入HashSet<string>。如果要查找字符串,请在HashSet中查找。成本将是O(n)读取XML,O(n)来执行HashSet的n次查找。不要尝试在XML中重复搜索(而是在HashSet中进行20,000次搜索),因为XML没有为搜索编制索引/优化。