【问题标题】:Searching an array string with a binary search sub string使用二分搜索子字符串搜索数组字符串
【发布时间】:2015-01-07 02:23:49
【问题描述】:

我有一个包含大约 200,000 条记录的 file.txt。

每条记录的格式为 123456-99-Text。 123456 是唯一的帐号,99 是我需要的位置代码(它从 01 变为 99),文本无关紧要。这些帐号按顺序排序,并在每个 ac(111111、111112、111113 等)的文件中使用换行符。

我制作了一个 Visual Studio 文本框和搜索按钮,让某人搜索帐号。帐号实际上是 11 位数字,但只有前 6 位重要。我把它写成字符串actnum = textbox1.text.substring(0,6)

我写了一个foreach (string x in file.readline('file.txt')) 和一个if (x.contains(actnum)) 然后string code = x.substring(8,2)) 声明。

该程序运行良好,但是由于如果有人搜索不存在的帐号或列表底部的数字,则记录太多,因此程序在进入“号码”之前锁定了10秒钟not found” else 语句,或者永远找不到最后一条记录。

我的问题:

阅读二进制搜索我曾尝试过一种但没有取得多大成功。我似乎无法让数组或文件像合法的二进制搜索一样工作。有没有办法从 textbox1 中获取 6 位数的 actnum,将其与 6 位数帐号的数组子字符串进行比较,然后从该特定行中获取子字符串 99 代码?

二分查找会有很大帮助!我可以取 555-555 并将其与记录文件的上半部分或下半部分进行比较,然后继续搜索,直到找到我需要的行,抓住整行,然后将 99 子串出来。我遇到的问题是我似乎无法对文件进行正确的整数转换,因为它同时包含数字和文本,因此我无法正确使用 、= 符号。

对此的任何帮助将不胜感激。我目前拥有的程序实际上可以运行,但有时速度非常慢。

【问题讨论】:

  • 继续粘贴您的代码。如果你做错了,有人会编辑帖子来修复它。总之做一个空行,然后将代码块缩进四个空格。
  • 您是否尝试将文件加载到内存中并可能从中生成对象表示?
  • @Brandon 这听起来像是一个相当大的列表,已经将所有内容都放入内存,如果它继续变大(正如拥有 11 位帐号所建议的那样),OP 可能会耗尽资源
  • 实际上刚刚发布的两个答案总结了我的建议。
  • 200,000 应该不是问题,除非你不关心的字符串很大。

标签: c# arrays binary-search


【解决方案1】:

作为一种可能的解决方案(不一定是最好的),您可以将记录 ID 添加到 Dictionary<string, int>(如果所有记录 ID 都是数字,甚至是 Dictionary<long, int>),其中每个键是一行的 ID,每个值是行索引。当您需要查找特定记录时,只需在字典中查找(它会为您进行有效查找)并为您提供行号。如果该项目不存在(不存在的 ID),您将不会在字典中找到它。

此时,如果文件中存在记录 ID,则您有一个行号 - 您可以将整个文件加载到内存中(如果它不是太大),或者只是寻找正确的行并在该行中读取与数据。

为此,您必须至少浏览文件一次并从所有行中收集所有记录 ID 并将它们添加到字典中。您不必实现二进制搜索 - 字典将在内部为您执行查找。

编辑

如果您不需要特定行的所有数据,只需一点(如您提到的位置代码),您甚至不需要存储行号(因为您不需要返回到文件中的行) - 只需将位置数据存储为字典中的值。

我个人仍然会存储行索引,因为根据我的经验,这样的项目开始时很小,但最终会收集功能,并且有时您必须从文件中获取所有内容。如果您希望随着时间的推移会出现这种情况,只需将每一行的数据解析为一个数据结构并将其存储在字典中 - 这将使您未来的生活更简单。如果您非常确定您永远不需要比一点信息更多的数据,您可以将数据本身存储在字典中。

这是一个简单的例子(假设你的记录ID可以解析成long):

public class LineData
{
    public int LineIndex { get; set; }

    public string LocationCode { get; set; }

    // other data from the line that you need
}

// ...

// declare your map
private Dictionary<long, LineData> _dataMap = new Dictionary<long, LineData> ();

// ...
// Read file, parse lines into LineData objects and put them in dictionary
// ...

要查看记录 ID 是否存在,只需调用TryGetValue()

LineData lineData;

if ( _dataMap.TryGetValue ( recordID, out lineData ) )
{
    // record ID was found
}

这种方法基本上将整个文件保存在内存中,但所有数据只解析一次(在开始时,在构建字典期间)。如果这种方法占用了太多内存,只需将行索引存储在字典中,然后在找到记录时返回文件并即时解析行。

【讨论】:

  • 第一次扫描文件时可以保存位置代码,为什么还要存储行索引?
  • @Kevin 我只建议这样可以在必要时从文件中访问整行,而不仅仅是一个特定的数据位。如果文件不是太大(并且预计将来会保持类似的大小),我只需加载整个文件并将其解析为对象字典,然后丢弃字符串数据。
  • 非常感谢!我还是新手,不完全理解字典命令。但是,您很好地解释了它可以做什么,我相信最好的选择是在程序启动时加载该字典(文件本身只有 10MB 左右,我认为内存不会太大)。
  • @ShoTo Dictionary 易于使用。这是一个查找表 - 您使用唯一键向它提供 Add 项目,然后您可以使用 TryGetValueContainsKey 等查找项目。声明中的两种类型用于键和值的类型.
【解决方案2】:

您不能真正对 file.ReadLine 进行二进制搜索,因为您必须能够以不同的顺序访问这些行。相反,您应该将整个文件读入内存(file.ReadAllLines 是一个选项)

假设您的文件按子字符串排序,您可以创建一个实现 IComparer 的新类

public class SubstringComparer : IComparer<string>
    {
        public int Compare(string x, string y)
        {
            return x.Substring(0, 6).CompareTo(y.Substring(0, 6));
        }
    }

然后您的二分搜索将如下所示:

int returnedValue = foundStrings.BinarySearch(searchValue, new SubstringComparer());

【讨论】:

    【解决方案3】:

    假设文件不经常更改,那么您可以使用一种可以更快地处理搜索的结构将整个文件加载到内存中。如果文件可以更改,那么您将需要决定重新加载文件的机制,无论是重新启动程序还是更复杂的过程。

    您似乎正在寻找完全匹配的内容(搜索 123456 只会产生一条标记为 123456 的记录)。如果是这种情况,那么您可以使用Dictionary。请注意,要使用字典,您需要定义键和值类型。在您的情况下,它们似乎都是string

    【讨论】:

      【解决方案4】:

      虽然我没有找到更好的搜索类型,但我确实设法了解了嵌入式资源,这大大加快了程序的运行速度。现在扫描整个文件只需要几分之一秒,而不是 5-10 秒。贴出以下代码:

         string searchfor = textBox1.Text
          Assembly assm = Assembly.GetExecutingAssembly();
          using (Stream datastream = assm.GetManifestResourceStream("WindowsFormsApplication2.Resources.file1.txt"))
          using (StreamReader reader = new StreamReader(datastream))
          {
              string lines;
              while ((lines = reader.ReadLine()) != null)
              {
                  if (lines.StartsWith(searchfor))
                  {
                      label1.Text = "Found";
                      break;
                  }
                  else
                  {
                      label1.Text = "Not found";
                  }
              }
          }
      

      【讨论】:

        猜你喜欢
        • 2011-07-04
        • 2023-03-24
        • 2020-09-27
        • 2021-06-28
        • 2015-06-15
        • 2014-10-06
        • 2012-01-13
        • 1970-01-01
        相关资源
        最近更新 更多