【问题标题】:Sort search results from text file into listbox将文本文件中的搜索结果排序到列表框中
【发布时间】:2011-03-16 12:55:47
【问题描述】:

我有一个文本框,用户可以在其中输入字符串。该字符串将如下所示: G32:04:20:40

然后用户点击搜索按钮。然后程序必须打开一个文本文件并搜索与他们输入的字符串“最近”的五个字符串,并将它们显示在一个列表框中。

我将尽可能多地定义“最近的字符串”(很可能使用一个非常长的复杂示例)。

文本文件中包含的数据如下所示:

G32:63:58:11 JG01
G32:86:98:30 JG01
G33:50:05:11 JG06
G33:03:84:12 JG05
G34:45:58:11 JG07
G35:45:20:41 JG01
G35:58:20:21 JG03

所以如果用户输入字符串:

G33:89:03:20

五个最接近的结果应显示在列表框中,如下所示:

G33:50:05:11 JG06
G33:03:84:12 JG05
G32:86:98:30 JG01
G32:63:58:11 JG01
G34:45:58:11 JG07

此时我可能应该指出,字符串是坐标,“JG”之后的值表示该坐标处某物的值。

我得到这 5 个的方法是逐个遍历字符串。所以用户输入了“G33”,所以我找到所有开头有 G33 的人——如果没有,那么我找到最接近 G33 的人。然后是“89”,所以如果没有的话,我会找到下一部分是“89”的所有那些,然后最接近 89 更好,依此类推。

我需要知道的是我该怎么做?我已经构建了可视化组件,并且我也有处理类似事情的代码,但是当涉及到这一点时,我真的很难过。正如您现在可能知道的那样,我对 C# 相当陌生,但我正在学习 :)

编辑:搜索代码

private void btnSearch_Click(object sender, EventArgs e)
        {
            lstResult.Items.Clear();

            if (txtSearch.Text == String.Empty)
            {
                MessageBox.Show("The textbox is empty, there is nothing to search.",
                    "Textbox empty", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
            {
                this.CheckFormatting();
            }

        }

        private long GetIndexForCoord(string coord)
        {
            // gets out a numerical value for each coordinate to make it easier to compare
            Regex m_regex = new Regex("\\d\\d:\\d\\d:\\d\\d:\\d\\d");
            string cleaned = m_regex.Match(coord).Value;
            cleaned = cleaned.Replace(':', '0');
            return Convert.ToInt64(cleaned);
        }

        private List<string> GetResults(string coord)
        {
            // gets out the 5 closest coordinates
            long index = GetIndexForCoord(coord);

            // First find the 5 closest indexes to the one we're looking for
            List<long> found = new List<long>();
            while (found.Count < 5)
            {
                long closest = long.MaxValue;
                long closestAbs = long.MaxValue;
                foreach (long i in m_indexes)
                {
                    if (!found.Contains(i))
                    {
                        long absIndex = Math.Abs(index - i);
                        if (absIndex < closestAbs)
                        {
                            closest = i;
                            closestAbs = absIndex;
                        }
                    }
                }
                if (closest != long.MaxValue)
                {
                    found.Add(closest);
                }
            }

            // Then use those indexes to get the coordinates from the dictionary
            List<string> s = new List<string>();
            foreach (long i in found)
            {
                s.Add(m_dic[i]);
            }
            return s;
        }

        private void CheckFormatting()
        {
            StringReader objReader = new StringReader(txtSearch.Text);

            bool FlagCheck = true;

                if (!Regex.IsMatch(txtSearch.Text,
                    "G3[0-9]{1}:[0-9]{2}:[0-9]{2}:[0-9]{2}"))
                {
                    FlagCheck = false;
                }

            if (FlagCheck == true)
            {
                this.CheckAndPopulate();
            }
            else
            {
                MessageBox.Show("Your search coordinates are not formatted correctly.",
                       "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        private void CheckAndPopulate()
        {
            StreamReader objReader = new StreamReader("Jumpgate List.JG");
            List<String> v = new List<String>();
            do
            {
                v.Add(objReader.ReadLine());
            }
            while (objReader.Peek() != -1);

            objReader.Close();

            foreach (string c in v)
            {
                long index = GetIndexForCoord(c);
                m_dic.Add(index, c);
                m_indexes.Add(index);
            }

            List<string> results = GetResults(txtSearch.Text);
            foreach (string c in results)
            {
                lstResult.Items.Add(c);
            }
        }

【问题讨论】:

    标签: c# regex winforms search listbox


    【解决方案1】:

    编辑:添加了要在文件中读取的建议代码。最后还有一个解释。
    Edit2:添加了如果检查添加到字典/列表以处理重复项。

    请注意,这段代码几乎肯定是非常低效的(除了可能是偶然的),需要清理并需要添加错误处理等,但它至少可以为您提供编写更好代码的起点. 您可能想查看k-nearest neighbor algorithm 以找出正确的方法。

    我假设开头的字母始终是 G,并且所有 4 个坐标部分始终是 2 位数字。

    将以下 2 添加到您的课程/表单中:

        Dictionary<long, string> m_dic = new Dictionary<long, string>();
        List<long> m_indexes = new List<long>();
    

    然后使用以下代码初始化它们(我假设您已经将所有坐标读入一个名为 v 的字符串数组中,每个项目都有一个坐标):

    foreach (string c in v)
    {
        long index = GetIndexForCoord(c);
        if(!m_dic.ContainsKey(index))
        {
            m_dic.Add(index, c);
            m_indexes.Add(index);
        }
    }
    

    然后添加以下2个方法:

    // gets out a numerical value for each coordinate to make it easier to compare
    private long GetIndexForCoord(string coord)
    {
        Regex m_regex = new Regex("\\d\\d:\\d\\d:\\d\\d:\\d\\d");
        string cleaned = m_regex.Match(coord).Value;
        cleaned = cleaned.Replace(':', '0');
        return Convert.ToInt64(cleaned);
    }
    // gets out the 5 closest coordinates
    private List<string> GetResults(string coord)
    {
        long index = GetIndexForCoord(coord);
    
        // First find the 5 closest indexes to the one we're looking for
        List<long> found = new List<long>();
        while (found.Count < 5)
        {
                long closest = long.MaxValue;
                long closestAbs = long.MaxValue;
                foreach (long i in m_indexes)
                {
                    if (!found.Contains(i))
                    {
                        long absIndex = Math.Abs(index - i);
                        if (absIndex < closestAbs)
                        {
                            closest = i;
                            closestAbs = absIndex;
                        }
                    }
                }
                if (closest != long.MaxValue)
                {
                    found.Add(closest);
                }
        }
    
        // Then use those indexes to get the coordinates from the dictionary
        List<string> s = new List<string>();
        foreach (long i in found)
        {
            s.Add(m_dic[i]);
        }
        return s;
    }
    

    最后,当用户输入数据时,您将该数据发送到方法中:

    List<string> results  = GetResults(lookingFor);
    

    然后您可以使用结果来填充您的列表框。

    代码的工作原理是将每个坐标转换为称为索引的数值(因为它更易于使用),然后将所有坐标添加到以索引为键的字典中。
    当它查找最接近的坐标时,它会比较您要查找的索引与之前存储的每个索引之间的值差异,以找到 5 个最接近的索引(它使用 Math.Abs 方法,这样它就可以在没有不必担心负数)。这是非常低效的,因为它为您要查找的每个坐标循环遍历每个值一次(因此,如果您的列表包含 1000 个坐标并且您想找到最接近的 5 个坐标,它将通过内部循环 5000 次,我假设通过改进代码可能会减少到 1000 次,我建议查看靠近此答案顶部的 wiki 链接以获得更好的算法)。

    【讨论】:

    • 有用的点。我会看看我是否可以把它变成 C#(对我来说它看起来像 VB.NET)。我也会看看你发布的链接。谢谢
    • @Arcadian:很抱歉,不知道为什么我认为你想要在 VB 中使用它,不过现在应该会更好。
    • lol np,我以前用过VB,不过还是谢谢你的改变。
    • @Arcadian:您的文件读取代码看起来不错,但我建议对 StreamReader 使用 using 语句,这样您就不必担心关闭它(或者如果抛出异常)。我在答案的末尾添加了一些解释,可能还不是很清楚,但希望能有所帮助。
    • @Arcadian:好的,那位只是将结果放在一个列表中,然后您可以在该列表上执行foreach(string c in results) 循环并将它们添加到列表框listBox1.Items.Add(c) 中。记得打电话listBox1.Items.Clear()清理旧的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-16
    • 2016-05-16
    • 2022-01-16
    • 1970-01-01
    相关资源
    最近更新 更多