使用二进制搜索子字符串搜索数组字符串

时间:2014-12-15 20:22:36

标签: c# arrays binary-search

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

每条记录的格式为123456-99-Text。 123456是唯一的帐号,99是我需要的位置代码(从01更改为99),文本无关紧要。这些帐号按顺序排序,并在每个交流的文件中有换行符(111111,11111,1111113等)。

我制作了一个视觉工作室文本框和搜索按钮,让某人搜索该帐号。帐号实际上是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秒钟34;未找到号码"否则声明,或永远找到最后的记录。

我的问题:

阅读二进制搜索我试图尝试一次没有太大成功。我似乎无法使数组或文件像合法的二进制搜索一样。有没有办法从textbox1中取出6位数的actnum,将它与6位数帐号的数组子串进行比较,然后从该特定行中获取子串99代码?

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

对此的任何帮助将不胜感激。我目前实际使用的程序有时非常慢。

4 个答案:

答案 0 :(得分:5)

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

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

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

修改

如果您不需要来自特定行的所有数据,只需要一位(就像您提到的位置代码一样),您甚至不需要存储行号(因为您赢了&#39 ; t需要返回文件中的行) - 只需将位置数据存储为字典中的值。

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

这是一个简单的示例(假设您的记录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
}

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

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

你无法真正对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 :(得分:0)

虽然我没有找到更好的搜索方式,但我确实设法了解嵌入式资源,这大大加快了程序的速度。扫描整个文件现在需要几分之一秒,而不是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";
            }
        }
    }