C# C语言中的高速字符串匹配#

C# C语言中的高速字符串匹配#,c#,string,search,C#,String,Search,我在列表中有一个大约10000名员工的列表,我有一个列表框,其中包含这些员工的子集,具体取决于文本框中的搜索词 假设Staff对象具有以下公开属性: string FirstName string LastName string MiddleName int StaffID int CostCentre 我可以编写如下函数: bool staffMatchesSearch(Staff stf) { if (tbSrch.Text.Trim() == string.Empty)

我在
列表
中有一个大约10000名员工的列表,我有一个
列表框
,其中包含这些员工的子集,具体取决于文本框中的搜索词

假设
Staff
对象具有以下公开属性:

string FirstName
string LastName
string MiddleName
   int StaffID
   int CostCentre
我可以编写如下函数:

bool staffMatchesSearch(Staff stf)
{
  if (tbSrch.Text.Trim() == string.Empty)
    return true; // No search = match always.

  string s = tbSrch.Text.Trim().ToLower();

  // Do the checks in the order most likely to return soonest:
  if (stf.LastName.ToLower().Contains(s))
    return true;
  if (stf.FirstName.ToLower().Contains(s))
    return true;
  if (stf.MiddleName.ToLower().Contains(s))
    return true;

  if (stf.CostCentre.ToString().Contains(s))
    return true; // Yes, we want partial matches on CostCentre
  if (stf.StaffID.ToString().Contains(s))
    return true; // And also on StaffID

  return false;
}
然后做一些类似的事情:

tbSrch_TextChanged(object sender, EventArgs e)
{
  lbStaff.BeginUpdate();
  lbStaff.Items.Clear();

  foreach (Staff stf in staff)
    if (staffMatchesSearch(stf))
      lbStaff.Items.Add(stf);

  lbStaff.EndUpdate();
}
每次用户更改
tbSrch
框的内容时,都会重新评估筛选

这是可行的,速度也不算太慢,但我想知道我能不能让它更快一些

我曾尝试将整个过程重新编写为多线程,但由于只有10000名员工,开销似乎带走了大部分好处。此外,还有一系列其他的错误,比如如果搜索“John”,用户首先按下“J”来卷绕线程,但是当用户按下“o”时,在第一批有机会返回结果之前,另一组被卷绕。很多时候,结果会以杂乱无章的顺序返回,各种令人讨厌的事情都会发生

我可以想出一些调整,让最好的情况变得更好,但也会让最坏的情况变得更糟

你对如何改进这一点有什么想法吗


到目前为止,我已经实施了很多很好的建议,它们的效果如下:

  • ValueChanged
    事件上添加延迟,这样,如果用户在键盘上快速键入5个字符的名称,它只在末尾执行1次搜索,而不是连续执行5次搜索
  • 预先计算
    ToLower()
    并存储在
    Staff
    类中(作为
    [非序列化]
    属性,这样它就不会占用保存文件中的额外空间)
  • Staff
    中添加一个
    get
    属性,该属性将所有搜索条件作为单个、长、小写的串联字符串返回。然后对其运行单个
    Contains()
    。(此字符串存储在
    Staff
    对象中,因此只构造一次。)
到目前为止,这些已经将搜索时间从大约140毫秒降低到了大约60毫秒(尽管这些数字非常主观,取决于实际执行的搜索和返回的结果数量)。

1)正如评论中指出的,您可能不应该这样做。要字符串数字字段,只需匹配数字即可

2) ToLower调用是性能消耗。将这些属性的小写版本添加到Staff类中,这样ToLower只需执行一次


3) 当用户输入另一个字符时,不需要重新计算所有项目。输入字符只会减少匹配的数量,因此只会重新评估以前的匹配

4) 使用计时器在用户键入和启动搜索之间引入延迟。如果他们正在快速键入多个字符,您不妨等到他们暂停半秒钟


5) 检查按键是否按下。如果是NaN,则不检查int属性。

您可以向Staff对象添加一个“SearchTerm”私有属性,该属性为
(FirstName+LastName+MiddleName+StaffID+costcenter)。ToLower()
,然后执行
Contains()
检查。这将停止每次对每个字符串执行
ToLower()
,并将
Contains()
检查的数量从5减少到1。

您可以尝试实现
trie
或“前缀树”:

这将允许您搜索以值开头的文本

我相信关于的链接文章将允许您进行全文搜索,尽管它有更高的存储要求


确保在将数据插入到结构中时减少所有数据,这样在查找时就不必进行不区分大小写的比较。

看到您所做的事情(主要是从@mikel的答案上的评论),听起来您已经做到了。我还没有看到一个可以提高速度的建议,就是使用
StringComparison.OrdinalIgnoreCase
进行比较。在你的情况下,这将意味着更换

if (stf.LastName.ToLower().Contains(s))
  return true;

下面是一个示例,讨论快速字符串比较。

使用系统;
using System;
using System.Text.RegularExpressions;
namespace PatternMatching1
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("Please enter the first string.");
                string str = Console.ReadLine(); ;
                string replacestr = Regex.Replace(str, "[^a-zA-Z0-9_]+", " ");



                Console.WriteLine("Please enter the second string.");
                string str1 = Console.ReadLine(); ;
                string replacestr1 = Regex.Replace(str1, "[^a-zA-Z0-9_]+", " ");



                if (replacestr.Length == replacestr1.Length)
                {
                    char[] cFirst = replacestr.ToLower().ToCharArray();
                    char[] cSecond = replacestr1.ToLower().ToCharArray();

                    Array.Sort<char>(cFirst);
                    Array.Sort<char>(cSecond);

                    if ((new string(cFirst)).Equals((new string(cSecond))))
                        Console.WriteLine("Both String Same");
                    else
                        Console.WriteLine("Both String Not Same");
                }
                else
                    Console.WriteLine("oopsss, something going wrong !!!! try again");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.Read();
        }
    }
}
使用System.Text.RegularExpressions; 命名空间模式匹配1 { 班级计划 { 静态void Main(字符串[]参数) { 尝试 { WriteLine(“请输入第一个字符串”); string str=Console.ReadLine(); 字符串replacestr=Regex.Replace(str,“[^a-zA-Z0-9+”,”); WriteLine(“请输入第二个字符串”); 字符串str1=Console.ReadLine(); 字符串replacestr1=Regex.Replace(str1,“[^a-zA-Z0-9+”,”); if(replacestr.Length==replacestr1.Length) { char[]cFirst=replacestr.ToLower().ToCharArray(); char[]cssecond=replacestr1.ToLower().ToCharArray(); Array.Sort(cFirst); Array.Sort(秒); if((新字符串(cFirst)).Equals((新字符串(cSecond))) Console.WriteLine(“两个字符串相同”); 其他的 Console.WriteLine(“两个字符串不相同”); } 其他的 WriteLine(“哎呀,出问题了!!!!再试一次”); } 捕获(例外情况除外) { 控制台写入线(例如消息); } Console.Read(); } } }

`

你真的想
toString
那些
int
s吗?似乎您需要matches string方法和matches int方法。。。我的意思是,如果我在13号中心,我不应该出现,因为有人搜索1号中心或3号中心…试着实现一个Boyer Moore系列的字符串搜索算法?预处理
using System;
using System.Text.RegularExpressions;
namespace PatternMatching1
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("Please enter the first string.");
                string str = Console.ReadLine(); ;
                string replacestr = Regex.Replace(str, "[^a-zA-Z0-9_]+", " ");



                Console.WriteLine("Please enter the second string.");
                string str1 = Console.ReadLine(); ;
                string replacestr1 = Regex.Replace(str1, "[^a-zA-Z0-9_]+", " ");



                if (replacestr.Length == replacestr1.Length)
                {
                    char[] cFirst = replacestr.ToLower().ToCharArray();
                    char[] cSecond = replacestr1.ToLower().ToCharArray();

                    Array.Sort<char>(cFirst);
                    Array.Sort<char>(cSecond);

                    if ((new string(cFirst)).Equals((new string(cSecond))))
                        Console.WriteLine("Both String Same");
                    else
                        Console.WriteLine("Both String Not Same");
                }
                else
                    Console.WriteLine("oopsss, something going wrong !!!! try again");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            Console.Read();
        }
    }
}