不区分大小写的列表搜索

Posted

技术标签:

【中文标题】不区分大小写的列表搜索【英文标题】:Case-Insensitive List Search 【发布时间】:2011-04-26 05:12:30 【问题描述】:

我有一个列表testList,其中包含一堆字符串。我想在testList 中添加一个新字符串,前提是它不存在于列表中。因此,我需要对列表进行不区分大小写的搜索并使其高效。我不能使用Contains,因为这没有考虑到大小写。出于性能原因,我也不想使用ToUpper/ToLower。我遇到了这种有效的方法:

    if(testList.FindAll(x => x.IndexOf(keyword, 
                       StringComparison.OrdinalIgnoreCase) >= 0).Count > 0)
       Console.WriteLine("Found in list");

这可行,但它也匹配部分单词。如果列表包含“山羊”,我不能添加“燕麦”,因为它声称“燕麦”已经在列表中。有没有办法以不区分大小写的方式有效地搜索列表,其中单词必须完全匹配?谢谢

【问题讨论】:

【参考方案1】:

以下是在整个列表中搜索关键字并删除该项目的示例:

public class Book

  public int BookId  get; set; 
  public DateTime CreatedDate  get; set; 
  public string Text  get; set; 
  public string Autor  get; set; 
  public string Source  get; set; 

如果您想删除在 Text 属性中包含某个关键字的书籍,您可以创建一个关键字列表并将其从书籍列表中删除:

List<Book> listToSearch = new List<Book>()
   
        new Book()
            BookId = 1,
            CreatedDate = new DateTime(2014, 5, 27),
            Text = " test voprivreda...",
            Autor = "abc",
            Source = "SSSS"

        ,
        new Book()
            BookId = 2,
            CreatedDate = new DateTime(2014, 5, 27),
            Text = "here you go...",
            Autor = "bcd",
            Source = "SSSS"


        
    ;

var blackList = new List<string>()
            
                "test", "b"
            ; 

foreach (var itemtoremove in blackList)
    
        listToSearch.RemoveAll(p => p.Source.ToLower().Contains(itemtoremove.ToLower()) || p.Source.ToLower().Contains(itemtoremove.ToLower()));
    


return listToSearch.ToList();

【讨论】:

【参考方案2】:

我知道这是一篇旧帖子,但如果其他人正在查看,您可以通过提供不区分大小写的字符串相等比较器来使用Contains,如下所示:

using System.Linq;

// ...

if (testList.Contains(keyword, StringComparer.OrdinalIgnoreCase))

    Console.WriteLine("Keyword Exists");

根据msdn,这从 .net 2.0 开始可用。

【讨论】:

绝对是最好的答案。 :) Enumerable.Contains(您引用的内容)自 .NET 2.0 以来就没有出现过。没有 List.Contains 具有您正在使用的重载。 @AdamSills 对。 List 中没有这样的 contains 方法。如果它是一个惰性集合,那么它可以像其他 Enumerable 方法那样迭代它几次。恕我直言,这种情况不应该使用这种方法,因为这种情况不合逻辑。 我一开始也没有看到这个重载,但是你需要使用 System.Linq 添加然后它才会出现。 StringComparer 类从 2.0 开始就存在了,但是在 3.5 中引入了 Contains 的重载。 msdn.microsoft.com/en-us/library/bb339118(v=vs.110).aspx【参考方案3】:

你可以使用StringComparer:

    var list = new List<string>();
    list.Add("cat");
    list.Add("dog");
    list.Add("moth");

    if (list.Contains("MOTH", StringComparer.OrdinalIgnoreCase))
    
        Console.WriteLine("found");
    

【讨论】:

只要添加“using System.Linq”,否则您不会看到 .Contains 的重载。【参考方案4】:

基于 Lance Larsen 的回答 - 这是推荐使用 string.Compare 而不是 string.Equals 的扩展方法

强烈建议您使用带有 StringComparison 参数的 String.Compare 重载。这些重载不仅允许您定义您想要的确切比较行为,使用它们还将使您的代码对其他开发人员更具可读性。 [Josh Free @ BCL Team Blog]

public static bool Contains(this List<string> source, string toCheck, StringComparison comp)

    return
       source != null &&
       !string.IsNullOrEmpty(toCheck) &&
       source.Any(x => string.Compare(x, toCheck, comp) == 0);

【讨论】:

【参考方案5】:

我有一个类似的问题,我需要项目的索引,但它必须不区分大小写,我在网上看了几分钟,什么也没找到,所以我只是写了一个小方法来完成它,here我就是这样做的:

private static int getCaseInvariantIndex(List<string> ItemsList, string searchItem)

    List<string> lowercaselist = new List<string>();

    foreach (string item in ItemsList)
    
        lowercaselist.Add(item.ToLower());
    

    return lowercaselist.IndexOf(searchItem.ToLower());

将此代码添加到同一个文件中,并像这样调用它:

int index = getCaseInvariantIndexFromList(ListOfItems, itemToFind);

希望这会有所帮助,祝你好运!

【讨论】:

为什么要生成第二个列表?这不是很有效。 for(var i = 0; i 我想我们永远不会知道。【参考方案6】:

基于 Adam Sills 上面的回答 - 这是一个不错的包含的干净扩展方法... :)

///----------------------------------------------------------------------
/// <summary>
/// Determines whether the specified list contains the matching string value
/// </summary>
/// <param name="list">The list.</param>
/// <param name="value">The value to match.</param>
/// <param name="ignoreCase">if set to <c>true</c> the case is ignored.</param>
/// <returns>
///   <c>true</c> if the specified list contais the matching string; otherwise, <c>false</c>.
/// </returns>
///----------------------------------------------------------------------
public static bool Contains(this List<string> list, string value, bool ignoreCase = false)

    return ignoreCase ?
        list.Any(s => s.Equals(value, StringComparison.OrdinalIgnoreCase)) :
        list.Contains(value);

【讨论】:

【参考方案7】:

使用 String.Equals 代替 String.IndexOf,以确保您没有部分匹配。也不要使用 FindAll,因为它会遍历每个元素,请使用 FindIndex(它会在遇到的第一个元素处停止)。

if(testList.FindIndex(x => x.Equals(keyword,  
    StringComparison.OrdinalIgnoreCase) ) != -1) 
    Console.WriteLine("Found in list"); 

交替使用一些 LINQ 方法(它也会在它遇到的第一个方法上停止)

if( testList.Any( s => s.Equals(keyword, StringComparison.OrdinalIgnoreCase) ) )
    Console.WriteLine("found in list");

【讨论】:

补充一点,在一些快速测试中,第一种方法似乎快了 50% 左右。也许其他人可以确认/否认这一点。 从 .NET 2.0 开始,这很容易完成 - 看看下面 shaxby 的回答。 Contains 方法 shaxby 的引用(它有一个采用 IEqualityComparer 的重载)是 LINQ 的一部分,因此自 .NET 2.0 以来它肯定不可用。只是 StringComparer 类已经存在了一段时间。 List 没有那个方法,ArrayList 或 StringCollection 也没有(他可以很容易地引用他的“列表”)。 好吧,因为我实际上需要索引,这对我来说绝对是最好的答案。 第一个解决方案应该使用List&lt;&gt;.Exists(Predicate&lt;&gt;)实例方法。另请注意,如果列表包含 null 条目,这可能会爆炸。在这种情况下,说keyword.Equals(x, StringComparison.OrdinalIgnoreCase)x.Equals(keyword, StringComparison.OrdinalIgnoreCase) 更安全(如果你能保证keyword 永远不会为空)。【参考方案8】:

您正在检查 IndexOf 的结果是否大于或等于 0,这意味着匹配是否从字符串中的 anywhere 开始。尝试检查它是否 等于 到 0:

if (testList.FindAll(x => x.IndexOf(keyword, 
                   StringComparison.OrdinalIgnoreCase) >= 0).Count > 0)
   Console.WriteLine("Found in list");

现在“goat”和“oat”不会匹配,但“goat”和“goa”会匹配。为避免这种情况,您可以比较两个字符串的长度。

为避免所有这些复杂情况,您可以使用字典而不是列表。它们的键是小写字符串,值是真正的字符串。这样,性能不会受到影响,因为您不必在每次比较时都使用ToLower,但您仍然可以使用Contains

【讨论】:

以上是关于不区分大小写的列表搜索的主要内容,如果未能解决你的问题,请参考以下文章

如何在不区分大小写的 MySQL 数据库上执行区分大小写的搜索?

如何将 Presto 搜索配置为不区分大小写?

grails中不区分大小写的搜索

使用“/”和“ - ”进行不区分大小写的搜索

不区分大小写的搜索

使用不区分大小写的搜索过滤字典