不区分大小写的列表搜索
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 开始可用。
【讨论】:
绝对是最好的答案。 :) EnumerableStringComparer
类从 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 类已经存在了一段时间。 ListList<>.Exists(Predicate<>)
实例方法。另请注意,如果列表包含 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
。
【讨论】:
以上是关于不区分大小写的列表搜索的主要内容,如果未能解决你的问题,请参考以下文章