题 不区分大小写'包含(字符串)'


有没有办法让以下返回true?

string title = "ASTRINGTOTEST";
title.Contains("string");

似乎没有一个重载允许我设置区分大小写..目前我大写他们两个,但这只是愚蠢(我指的是 国际化 上下套管带来的问题)。

UPDATE
这个问题是古老的,从那时起我就意识到,如果你想完全研究它,我会要求一个简单的答案来解决一个非常庞大而困难的话题。
对于大多数情况,使用单语言,英语代码库 这个 答案就够了。我怀疑是因为大多数人来到这个类别这是最受欢迎的答案。
这个 然而,答案提出了一个固有的问题,即我们无法比较文本不区分大小写,直到我们知道两种文本是相同的文化并且我们知道文化是什么。这可能是一个不太受欢迎的答案,但我认为它更正确,这就是为什么我这样标记它。


2420
2018-01-14 21:39


起源


怎么这么傻?你是说你在弦上做了2次传球?我认为不区分大小写的比较只是将两个步骤结合起来。 - Calyth
由于我将在worldwebz上使用它,我必须考虑外国字符。正如下面的答案中所提到的,上行和下行给出了国际化问题。 - Boris Callens
上层两个字符串都是愚蠢的,因为您创建了两个新字符串,然后仍然执行区分大小写的搜索。创建新的字符串时会涉及不必要的额外处理和内存,特别是如果您正在搜索一组字符串并且冗余地对搜索或源语句进行大写。允许指定StringComparison值的IndexOf方法更好。 - Triynko
xkcd.com/979 - Francisco
@ColonelPanic:正确。如果你了解这种文化,那就不再是问题了。但通常,你要么不知道,要么不关心。 - Boris Callens


答案:


测试是否为字符串 paragraph 包含字符串 word (感谢@QuarterMeister)

culture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0

哪里 culture 是的例子 CultureInfo 描述文本所写的语言。

这个解决方案是透明的 不区分大小写的定义,它取决于语言。例如,英语使用字符 I 和 i 对于第九个字母的大写和小写版本,而土耳其语使用这些字符 第十一和第十二封信 它的29个字母长的字母表。土耳其大写版本的'i'是不熟悉的角色'İ'。

因此弦乐 tin 和 TIN 是同一个词 用英语,但不同的话 用土耳其语。据我所知,一个是“精神”,另一个是拟声词。 (土耳其人,请纠正我,如果我错了,或建议一个更好的例子)

总而言之,您只能回答“这两个字符串是否相同但在不同情况下”的问题 如果你知道文本是什么语言。如果你不知道,你将不得不采取行动。鉴于英语在软件方面的霸权,你应该诉诸于此 CultureInfo.InvariantCulture,因为熟悉的方式会出错。


1088
2018-03-17 18:22



为什么不 culture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0?它使用正确的文化并且不区分大小写,它不分配临时小写字符串,并且它避免了转换为小写和比较是否总是与不区分大小写的比较相同的问题。 - Quartermeister
该解决方案还通过为应该是搜索功能分配内存而不必要地污染堆 - JaredPar
当两个不同的字母具有相同的小写字母时,与ToLower()相比将从不区分大小写的IndexOf中给出不同的结果。例如,在U + 0398“希腊大写字母Theta”或U + 03F4“希腊大写字母Theta符号”上调用ToLower()会产生U + 03B8,“希腊小写字母Theta”,但大写字母被认为是不同的。两种解决方案都考虑使用相同大写字母的小写字母不同,例如U + 0073“Latin Small Letter S”和U + 017F“Latin Small Letter Long S”,因此IndexOf解决方案似乎更加一致。 - Quartermeister
完整性为+1 - 使用适当形式的解释的答案是用户实际从SO学习的唯一方式 - TheGeekZn
你为什么不写“ddddfg”.IndexOf(“Df”,StringComparison.OrdinalIgnoreCase)? - Chen


你可以使用 String.IndexOf方法 并通过 StringComparison.OrdinalIgnoreCase 作为要使用的搜索类型:

string title = "STRING";
bool contains = title.IndexOf("string", StringComparison.OrdinalIgnoreCase) >= 0;

更好的是为字符串定义一个新的扩展方法:

public static class StringExtensions
{
    public static bool Contains(this string source, string toCheck, StringComparison comp)
    {
        return source?.IndexOf(toCheck, comp) >= 0;
    }
}

注意 零传播  ?. 自C#6.0(VS 2015)起可用,适用于旧版本

if (source == null) return false;
return source.IndexOf(toCheck, comp) >= 0;

用法:

string title = "STRING";
bool contains = title.Contains("string", StringComparison.OrdinalIgnoreCase);

2361
2018-01-14 21:44



伟大的字符串扩展方法我编辑了我的,检查源字符串是否为null,以防止在执行.IndexOf()时发生任何对象引用错误。 - Richard Pursehouse
这给出了相同的答案 paragraph.ToLower(culture).Contains(word.ToLower(culture)) 同 CultureInfo.InvariantCulture 它并没有解决任何本地化问题。为什么复杂的事情? stackoverflow.com/a/15464440/284795 - Colonel Panic
@ColonelPanic ToLower version包括2个分配,这些分配在比较/搜索操作中是不必要的。为什么在不需要它的情况下不必要地分配? - JaredPar
@Seabiscuit因为不起作用 string 是一个 IEnumerable<char> 因此你不能用它来找到子串 - JaredPar
警告:默认为 string.IndexOf(string) 是使用当前的文化,而默认为 string.Contains(string) 是使用序数比较器。众所周知,前者可以改变选择较长的过载,而后者不能改变。这种不一致的后果是以下代码示例: Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; string self = "Waldstrasse"; string value = "straße"; Console.WriteLine(self.Contains(value));/* False */ Console.WriteLine(self.IndexOf(value) >= 0);/* True */ - Jeppe Stig Nielsen


您可以使用 IndexOf() 喜欢这个:

string title = "STRING";

if (title.IndexOf("string", 0, StringComparison.CurrentCultureIgnoreCase) != -1)
{
    // The string exists in the original
}

由于0(零)可以是索引,因此请检查-1。

MSDN

如果找到该字符串,则从零开始的索引位置值为-1   如果不是。如果value为String.Empty,则返回值为0。


203
2018-01-14 21:48





使用Regex的替代解决方案:

bool contains = Regex.IsMatch("StRiNG to search", "string", RegexOptions.IgnoreCase);

注意

正如@cHao在他的评论中指出的那样,有一些情况会导致这个解决方案返回错误的结果。 在随意实施此解决方案之前,请确保您知道自己在做什么。


116
2017-07-28 17:18



好主意,我们在RegexOptions中也有很多按位组合 RegexOptions.IgnoreCase & RegexOptions.IgnorePatternWhitespace & RegexOptions.CultureInvariant; 对任何人都有帮助。 - Saravanan
必须说我更喜欢这种方法,尽管使用IsMatch来保持整洁。 - wonea
更糟糕的是,由于搜索字符串被解释为正则表达式,因此许多标点字符将导致错误的结果(或由于表达式无效而触发异常)。尝试搜索 "." 在 "This is a sample string that doesn't contain the search string"。或者尝试搜索 "(invalid", 对于这个问题。 - cHao
@cHao:在这种情况下, Regex.Escape 有帮助。当正则表达式似乎仍然没必要 IndexOf / 延期 Contains 很简单(可以说更清楚)。 - Dan Mangiarelli
请注意,我并不是说这个Regex解决方案是最好的方法。我只是添加到原始发布的问题的答案列表中“是否有办法使以下返回成立?”。 - Jed


您可以先将字符串向上或向下翻转。

string title = "string":
title.ToUpper().Contains("STRING")  // returns true

哎呀,刚看到最后一点。不区分大小写的比较会 *大概* 无论如何都要做同样的事情,如果性能不是问题,我没有看到创建大写副本和比较它们的问题。我曾经发誓我曾经看过一次不区分大小写的比较...


63
2018-01-14 21:42



有趣的是,我已经看到ToUpper()在这种情况下推荐使用ToLower(),因为显然ToLower()在某些文化中可能“失去保真度” - 也就是说,两个不同的大写字符转换为相同的小写字符。 - Matt Hamilton
搜索“土耳其测试”:) - Jon Skeet
在某些法语区域设置中,大写字母没有变音符号,因此ToUpper()可能不会比ToLower()更好。如果它们可用,我会说使用适当的工具 - 不区分大小写的比较。 - Blair Conrad
不要使用ToUpper或ToLower,并执行Jon Skeet所说的话 - Peter Gfader
两年后再次看到这个和一个新的downvote ...无论如何,我同意有更好的方法来比较字符串。但是,并非所有程序都将本地化(大多数不会),而且许多程序都是内部或一次性应用程序。因为对于一次性应用程序最好的建议,我几乎不能指望得到信用......我正在继续:D - Ed S.


答案的一个问题是,如果字符串为null,它将抛出异常。您可以将其添加为支票,这样就不会:

public static bool Contains(this string source, string toCheck, StringComparison comp)
{
    if (string.IsNullOrEmpty(toCheck) || string.IsNullOrEmpty(source))
        return true;

    return source.IndexOf(toCheck, comp) >= 0;
} 

48
2017-12-07 21:11



如果toCheck是空字符串,则需要根据Contains文档返回true:“如果value参数出现在此字符串中,则为true,或者如果value为空字符串(”“);否则为false。” - amurra
根据上述amurra的评论,建议的代码是否需要更正?并且不应该将其添加到已接受的答案中,以便首先获得最佳响应? - David White
现在,如果source是空字符串,则返回true,无论toCheck是什么,这都将返回null。这不可能是正确的。如果toCheck为空字符串且source不为null,则IndexOf也会返回true。这里需要检查null。我建议if(source == null || value == null)返回false; - Colin
源不能为空 - Lucas
if (string.IsNullOrEmpty(source)) return string.IsNullOrEmpty(toCheck); - Kyle Delaney


StringExtension类是前进的方法,我结合上面的几个帖子给出了一个完整的代码示例:

public static class StringExtensions
{
    /// <summary>
    /// Allows case insensitive checks
    /// </summary>
    public static bool Contains(this string source, string toCheck, StringComparison comp)
    {
        return source.IndexOf(toCheck, comp) >= 0;
    }
}

32
2017-11-18 16:48





这很简洁。

Regex.IsMatch(file, fileNamestr, RegexOptions.IgnoreCase)

31
2017-11-09 04:25



但这将与模式匹配。在您的示例中,如果 fileNamestr 有任何特殊的正则表达式字符(例如 *, +, .等等,那么你会非常惊讶。使这个解决方案正常工作的唯一方法 Contains 功能是逃避 fileNamestr 通过做 Regex.Escape(fileNamestr)。 - XåpplI'-I0llwlg'I -


OrdinalIgnoreCase,CurrentCultureIgnoreCase还是InvariantCultureIgnoreCase?

由于缺少这个,这里有一些关于何时使用哪一个的建议:

DOS

  • 使用 StringComparison.OrdinalIgnoreCase 比较 作为与文化无关的字符串匹配的安全默认值。
  • 使用 StringComparison.OrdinalIgnoreCase 对比 为了提高速度。
  • 使用 StringComparison.CurrentCulture-based 字符串操作 在向用户显示输出时。
  • 切换当前使用基于不变量的字符串操作 文化使用非语言 StringComparison.Ordinal 要么 StringComparison.OrdinalIgnoreCase 当比较时
    在语言上无关紧要(例如,象征性的)。
  • 使用 ToUpperInvariant 而不是 ToLowerInvariant 什么时候 规范化字符串以进行比较。

注意事项

  • 对未明确的字符串操作使用重载 或隐式指定字符串比较机制。
  • 使用 StringComparison.InvariantCulture 基于字符串
    大多数情况下的操作;少数例外之一
    坚持语言上有意义但与文化无关的数据。

根据这些规则,您应该使用:

string title = "STRING";
if (title.IndexOf("string", 0, StringComparison.[YourDecision]) != -1)
{
    // The string exists in the original
}

而[YourDecision]取决于上述建议。

来源链接: http://msdn.microsoft.com/en-us/library/ms973919.aspx


24
2018-06-17 10:31



如果你知道你总会得到一个英文字符串怎么办?哪一个使用? - BKSpurgeon
@BKSpurgeon我会使用OrdinalIgnoreCase,如果情况无关紧要的话 - Fabian Bigler