最快的方法是将一个字符串与一组通配符进行比较。

4

我有一个字典,我的键是带有通配符的字符串。我想知道一个字符串是否与字典中的任何键匹配。

例子:

String str = "Really Large String";
Dictionary dic = new Dictionary<String, MyClass>();
dic.Add("First+Match*", new MyClass());
dic.Add("*Large*", new MyClass());

编辑: 我想做类似于以下内容:

foreach(var s in dic.Keys){
  if(str.Match(s))
    //Do Something
}

2
这些通配符到底在哪里定义的,你希望它们如何运作? - Oded
将键符合标准的正则表达式格式,你就可以开始了。在那种情况下,你将能够利用LINQ +正则表达式。 - code4life
@Oded 我想要将我的字符串(str)与任意字典键匹配。 - Makah
3个回答

2
您可以使用正则表达式,将带通配符的字符串转换为正则表达式模式(我假设您想使用相当古老的“*”和“?”通配符):
public static string ToRegEx(string pattern)
{
    return Regex.Escape(pattern).Replace("\\*", ".*").Replace("\\?", ".");
}

2
为什么不呢,
var dic = Dictionary<Regex, MyClass>()
dic.Add(new Regex("..."), new MyClass)
....

foreach(var match in dic.Keys.Where(k => k.IsMatch(str)))
{
    var myClass = dic[match];
    ....
}

现在有一个问题,为什么要使用字典呢?为什么不直接扩展MyClass以匹配字符串本身,或许使用一个名为MatchPredicate
var matchers = new HashSet<MyClass>();
matchers.Add(new MyClass("some regex?");
....

foreach(var match in matchers.Where(Match(str)))
{
    ....
}

编辑

如果你只想要第一个匹配项,那么你可以使用FirstOrDefault代替Where

var firstMatch = matchers.FirstOrDefault(Match(str))
if (firstMatch != null)
{
    ....
}

然而,这将使列表的顺序变得重要。
编辑2:
实现MyClass的部分内容,包括一个Match谓词,可能会……
partial class MyClass
{
    private readonly RegEx matcher;

    public MyClass(string regEx)
    {
        matcher = new RegEx(regEx);
    }

    public bool Match(string value)
    {
        return matcher.IsMatch(value);
    }
}

谢谢你的回答。我忘记了Where和FirstOrDefault方法。为了完善这篇文章,如果您能添加一个Match()的实现就太好了。我可以添加我的Match(),但编辑您的回答并不礼貌。 :) - Makah
1
如果您经常与相同的通配符进行比较,使用RegEx构造函数的编译选项通常是值得的。 - Malcolm

-1

这个解释可能会比我想象中的要长一些,但在这种情况下,完全理解底层发生了什么是非常有用的。因为特定的方法在你的源代码中看起来很高效,并不意味着CPU也会以同样的方式看待它。当你关心每个字节的执行速度时,你必须理解操作实际上将在哪个级别上执行。任何在该级别以上的内容都只是语义和最终的宏定义,这些都不能给你一个准确的图片,告诉你实际上正在创建什么。

Intel / AMD CPU具有一组重复扫描指令,允许您设置指针,将一个字节放入寄存器,设置要扫描的字节数,内部CPU会作为单个内部指令运行扫描,逐字节扫描直到找到匹配或不匹配(或计数器耗尽)。当计数器耗尽时,调整指针并处理“未满足条件; 我已经用完计数器!”的情况可能会变得混乱;虽然这不会直接影响您的代码,但如果您在循环中进行了大量的单独搜索,则可能会影响执行时间。因此,尽量减少实际搜索次数从来都不是一个坏主意。这不是一个巨大的因素,但它可能会起到作用。

在我的代码中,对于大型搜索,我会向前扫描第一个字节。将其匹配作为进一步处理的起点可以节省大量时间,否则将浪费在比较每个字节上。让CPU来做这件事。CPU必须完成大量工作,仅加载指令并准备执行它,因此任何时候您都可以减少这项工作,程序都将运行得更快。
问题在于你对这些事情没有太多的直接控制权。无论你使用什么语言,最可能的方法都是使用最慢的、蛮力的方法:拿起一个字节,看一下,再拿起下一个字节,打哈欠,打哈欠,打哈欠。如果你的语言是这样做的(大多数都是这样),那么在编码方法上选择任何两个选项之间几乎没有什么区别:它们已经全部陷入了性能地下室。如果你处于可以插入一块汇编代码的情况下,你将获得巨大的好处。但是大多数人被禁止这样做,因为在1985年被认为是非法的。当然,还有32位和64位的问题需要考虑,但这些可以解决。总之,如果你能够确定你特定的语言到底做了什么,那么就要考虑到这一点。如果它正在使用逐个比较字符串的方法,那么战斗已经失败了,你所做的大部分调整代码的工作可能不会产生太大的影响。

3
你不觉得汇编语言太过于生硬了吗?为什么不写自己的微码或者更好的是开发一个新的处理单元来处理字符串比较呢?也许我们应该完全放弃冯·诺依曼体系结构的限制。(请原谅我的讽刺)这个问题标记了C#。 - Jodrell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接