Нечутливий до регістру "Містить (рядок)"


2907

Чи є спосіб здійснити таке повернення?

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

Здається, не існує перевантаження, яка дозволяє мені встановити чутливість регістру. В даний час я ПІДТРИМАТИ їх обох, але це просто нерозумно (під яким я маю на увазі проблеми i18n, які поставляються з корпусом вгору та вниз).

ОНОВЛЕННЯ
Це питання давнє, і з тих пір я зрозумів, що я попросив просту відповідь на дійсно обширну і складну тему, якщо ви хочете її повністю вивчити.
У більшості випадків в одномовної, англійській кодовій базі цієї відповіді буде достатньо. Я підозрюю, оскільки більшість людей, які приїжджають сюди, потрапляють до цієї категорії, це найпопулярніша відповідь. Однак
ця відповідь викликає притаманну проблему, що ми не можемо порівнювати текстові регістри, нечутливі до тих пір, поки не знаємо, що обидва тексти є однаковою культурою, і ми не знаємо, що це за культура. Це, можливо, менш популярна відповідь, але я вважаю, що вона більш правильна, і тому я позначив її як таку.

Відповіді:


1398

Щоб перевірити, чи paragraphмістить рядок рядок word(спасибі @QuarterMeister)

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

Де cultureє примірник CultureInfoопису мови, якою написаний текст.

Це рішення є прозорим щодо визначення чутливості до регістру, яке залежить від мови . Наприклад, англійська мова використовує символи Iта iдля верхніх та малих версій дев'ятої літери, тоді як турецька мова використовує ці символи для одинадцятої та дванадцятої літер свого 29-літерного алфавіту. Версія турецького верхнього регістру "i" - незнайомий символ "İ".

Таким чином, рядки tinі TINявляють собою те саме слово англійською мовою , але різні слова турецькою . Як я розумію, один означає «дух», а інший - слово ономатопеї. (Турки, будь ласка, виправте мене, якщо я помиляюся, або запропонуйте кращий приклад)

Підводячи підсумок, ви можете відповісти на запитання "чи ці два рядки однакові, але в різних випадках", якщо ви знаєте, на якій мові йде текст . Якщо ви цього не знаєте, вам доведеться взяти шайбу. Зважаючи на гегемонію англійської мови в програмному забезпеченні, ви, ймовірно, можете вдатися CultureInfo.InvariantCulture, оскільки це буде неправильно звичними способами.


67
Чому ні culture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0? Це використовує правильну культуру і нечутливе до регістру, вона не виділяє тимчасові рядки рядка, і це дозволяє уникнути питання про те, чи перетворення на малі та невеликі регістри завжди те саме, що порівняння не залежно від регістру.
Quartermeister

9
Це рішення також непотрібно забруднює купу, виділяючи пам'ять на те, що має бути функцією пошуку
JaredPar

15
Порівняння з ToLower () дасть різні результати від нечутливого до регістру IndexOf, коли дві різні літери мають однакову малу літеру. Наприклад, виклик ToLower () або U + 0398 "Грецька велика літера-тета", або U + 03F4 "Грецький символ великої літери-тети" призводить до U + 03B8, "Грецької малої букви тети", але великі літери вважаються різними. Обидва рішення розглядають малі літери з однаковою великою літерою, такі як U + 0073 "Малі латинські літери S" та U + 017F "Малі латинські літери довгі S", тому рішення IndexOf здається більш послідовним.
Quartermeister

3
@Quartermeister - і BTW, я вважаю. NET 2 і .NET4 поводяться з цим по-різному, оскільки .NET 4 завжди використовує NORM_LINGUISTIC_CASING, тоді як .NET 2 цього не зробив (ці прапори з'явилися в Windows Vista).
Саймон Мур’є

10
Чому ви не написали "ddddfg" .IndexOf ("Df", StringComppare.OrdinalIgnoreCase)?
Чень

2712

Ви можете використовувати метод 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);

3
Чудовий метод розширення рядків! Я відредагував шахту, щоб перевірити, що джерельна рядок не є нульовим, щоб запобігти появі помилок посилання на об'єкт під час виконання .IndexOf ().
Richard Pursehouse

8
Це дає ту саму відповідь , як paragraph.ToLower(culture).Contains(word.ToLower(culture))з CultureInfo.InvariantCultureі він не вирішує жодних проблем локалізації. Чому над складними речами? stackoverflow.com/a/15464440/284795
Полковник Паніка

60
@ColonelPanic ToLowerверсія включає 2 виділення, які не потрібні в операції порівняння / пошуку. Чому марно виділяти сценарій, який цього не вимагає?
JaredPar

4
@Seabiscuit , що не працюватиме , тому що stringце IEnumerable<char>означає , ви не можете використовувати його , щоб знайти підрядка
JaredPar

6
Слово попередження: за замовчуванням 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

231

Ви можете використовувати IndexOf()так:

string title = "STRING";

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

Оскільки 0 (нуль) може бути індексом, ви перевіряєте -1.

MSDN

Нульова позиція індексу значення, якщо знайдено цей рядок, або -1, якщо його немає. Якщо значення String.Empty, значення повернення дорівнює 0.


148

Альтернативне рішення з використанням Regex:

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

6
Хороша ідея. Також у RegexOptions ми маємо безліч розрядних комбінацій, як RegexOptions.IgnoreCase & RegexOptions.IgnorePatternWhitespace & RegexOptions.CultureInvariant;для всіх, якщо це допомагає.
Сараванан

7
Потрібно сказати, що я віддаю перевагу цьому методу, хоча для акуратності використовую IsMatch.
wonea

31
Що ще гірше, оскільки рядок пошуку інтерпретується як регулярний вираз, ряд знаків пунктуації призведе до неправильних результатів (або викликає виняток через невірний вираз). Спробуйте пошукати "."в "This is a sample string that doesn't contain the search string". Або спробуйте пошукати "(invalid"з цього питання.
cHao

17
@cHao: У цьому випадку, Regex.Escapeможе допомогти. Regex все ще здається непотрібним, коли IndexOf/ розширення Containsпросте (і, можливо, більш зрозуміле).
Dan Mangiarelli

6
Зауважте, що я не мав на увазі, що це рішення Regex - це найкращий шлях. Я просто додав до списку відповідей на оригінальне розміщене запитання "Чи є спосіб зробити таке повернення справжнім?".
Джед

79

Ви завжди зможете спершу підняти або зменшити рядки.

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

Ой, щойно побачив цей останній шматочок. Справа, нечутливе до випадку, порівняло б* ймовірно, *зробить те саме, і якщо продуктивність не є проблемою, я не бачу проблем із створенням великих копій та порівнянням їх. Я міг присягнути, що одного разу побачив порівняння, яке не враховує регістр,


122
Шукайте "Тест на Туреччину" :)
Джон Скіт

7
У деяких французьких мовах великі літери не мають діакритики, тому ToUpper () може бути не кращим, ніж ToLower (). Я б сказав, використовуйте належні інструменти, якщо вони доступні - порівняння з урахуванням регістру.
Блер Конрад

5
Не використовуйте ToUpper або ToLower, а робіть те, що сказав Джон Скіт
Пітер Гфадер

14
Щойно я побачив це знову через два роки та нове голосування ... все одно, я згоден, що є кращі способи порівняння рядків. Однак не всі програми будуть локалізовані (більшість не буде), і багато з них є внутрішніми або викидними програмами. Оскільки я навряд чи сподіваюсь на отримання кредитів за пораду, яку найкраще залишити для викидних програм ... Я рухаюся далі: D
Ед С.

8
Чи є пошук "тесту на Туреччину" таким самим, як пошук "TEST TURKEY TEST"?
JackAce

55

Тільки .NET Core 2.0+ (станом на даний момент)

.NET Core має пару методів для вирішення цього питання з версії 2.0:

  • String.Contains (Char, StringComppare )
  • String.Contains (String, StringComppare )

Приклад:

"Test".Contains("test", System.StringComparison.CurrentCultureIgnoreCase);

З часом вони, мабуть, пробиться до .NET Standard і, звідти, у всі інші реалізації Бібліотеки базових класів.


1
Тепер також доступний у .NET Standard 2.1
Paweł Bulwan

52

Одне питання з відповіддю полягає в тому, що він видасть виняток, якщо рядок є нульовим. Ви можете додати це як чек, щоб він не:

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;
} 

8
Якщо toCheck - порожній рядок, йому потрібно повернути true за документами, що містять зміст: "true, якщо параметр значення зустрічається в цій строці, або якщо значення є порожнім рядком (" "); в іншому випадку, false."
amurra

3
На підставі вищезазначеного коментаря amurra, чи не потрібно виправляти запропонований код? І чи не слід цього додавати до прийнятої відповіді, щоб спочатку була найкраща відповідь?
Девід Уайт

13
Тепер це повернеться істинним, якщо джерелом є порожній рядок або нуль незалежно від того, що це перевірити. Це не може бути правильним. Також IndexOf вже повертає значення true, якщо toCheck - порожній рядок, а джерело - недійсне. Тут потрібно чек на нуль. Я пропоную, якщо (source == null || value == null) повернути false;
Колін

2
Не може бути джерело нуля
Лукас

1
if (string.IsNullOrEmpty(source)) return string.IsNullOrEmpty(toCheck);
Кайл Делані

35

Клас 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;
    }
}

чому ви дозволяєте БІЛЬКИЙ шар абстракції StringComparison?
l - '' '''--------- '' '' '' '' ''

35

Це чисто і просто.

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

31
Однак це буде відповідати шаблону. У вашому прикладі, якщо fileNamestrє якісь - або спеціальні символи регулярних виразів (наприклад *, +, .і т.д.) , то ви будете в протягом досить несподівано. Єдиний спосіб змусити це рішення працювати як належна Containsфункція - це втеча fileNamestr, виконуючи це Regex.Escape(fileNamestr).
XåpplI'-I0llwlg'I -

окрім того, синтаксичний аналіз і відповідність регулярного виразів набагато більш ресурсомісткий, ніж просте порівняння з
невідчутним регістром

29

OrdinalIgnoreCase, CurrentCultureIgnoreCase або InvariantCultureIgnoreCase?

Оскільки цього немає, ось кілька рекомендацій щодо того, коли використовувати:

Дос

  • Використовуйте 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


що, якщо ти знаєш, що ти завжди будеш отримувати англійську струну. який використовувати?
BKSpurgeon

1
@BKSpurgeon Я б скористався OrdinalIgnoreCase, якщо справа не має значення
Fabian Bigler

20

Це найпростіші рішення.

  1. За індексом

    string title = "STRING";
    
    if (title.IndexOf("string", 0, StringComparison.CurrentCultureIgnoreCase) != -1)
    {
        // contains 
    }
  2. За зміною справи

    string title = "STRING";
    
    bool contains = title.ToLower().Contains("string")
  3. За Regex

    Regex.IsMatch(title, "string", RegexOptions.IgnoreCase);

11

Я знаю, що це не C #, але в рамках (VB.NET) вже є така функція

Dim str As String = "UPPERlower"
Dim b As Boolean = InStr(str, "UpperLower")

C # варіант:

string myString = "Hello World";
bool contains = Microsoft.VisualBasic.Strings.InStr(myString, "world");

11

InStrМетод з VisualBasic збірки краще всього, якщо у вас є занепокоєння щодо приводу інтернаціоналізації (або ви можете перевизначити його). Поглянувши на це, dotNeetPeek показує, що він не лише містить великі і малі літери, але й тип кана та символи повної ширини порівняно з половиною ширини (в основному стосується азіатських мов, хоча є й повні ширини версій римського алфавіту) ). Я пропускаю деякі деталі, але перевіряю приватний метод InternalInStrText:

private static int InternalInStrText(int lStartPos, string sSrc, string sFind)
{
  int num = sSrc == null ? 0 : sSrc.Length;
  if (lStartPos > num || num == 0)
    return -1;
  if (sFind == null || sFind.Length == 0)
    return lStartPos;
  else
    return Utils.GetCultureInfo().CompareInfo.IndexOf(sSrc, sFind, lStartPos, CompareOptions.IgnoreCase | CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth);
}

11

Просто так:

string s="AbcdEf";
if(s.ToLower().Contains("def"))
{
    Console.WriteLine("yes");
}

3
Це не є специфічним для культури, а може виникнути невдача в деяких випадках. культура.CompareInfo.IndexOf (абзац, слово, CompareOptions.IgnoreCase) слід використовувати.
hikalkan

3
Чому слід уникати string.ToLower () при порівнянні рядків з нечутливими до регістру? Tl; Dr Це дорого, оскільки "виготовляється" нова струна.
Ліам

8

Використовуй це:

string.Compare("string", "STRING", new System.Globalization.CultureInfo("en-US"), System.Globalization.CompareOptions.IgnoreCase);

26
Опитувач шукає Containsне Compare.
DuckMaestro

@DuckMaestro, прийняту відповідь реалізуємо за Containsдопомогою IndexOf. Тож такий підхід не менш корисний! У прикладі коду C # на цій сторінці використовується string.Compare (). Вибір команди SharePoint, який є!
вулкан ворон

6

Це досить схоже на інший приклад тут, але я вирішив спростити перерахунок до bool, головного, оскільки інші альтернативи зазвичай не потрібні. Ось мій приклад:

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

А використання - це щось на зразок:

if( "main String substring".Contains("SUBSTRING", true) )
....

6

Використання RegEx - це прямий спосіб зробити це:

Regex.IsMatch(title, "string", RegexOptions.IgnoreCase);

4
Ваша відповідь точно така ж, як і у guptat59, але, як було зазначено у його відповіді, це відповідатиме регулярному вираженню, тому якщо рядок, яку ви тестуєте, містить будь-які спеціальні символи регулярного виразів, вона не дасть бажаного результату.
Кейсі

2
Це пряма копія цієї відповіді і страждає від тих же питань, що й у цій відповіді,
Ліам

Домовились. Вивчайте регулярні вирази
Джаред

5

Просто будуючи відповідь тут, ви можете створити метод розширення рядків, щоб зробити це трохи більш зручним у користуванні:

    public static bool ContainsIgnoreCase(this string paragraph, string word)
    {
        return CultureInfo.CurrentCulture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0;
    }

1
Припустимо, що ваш абзац і слово завжди будуть в
англійській мові

3
Щоб уникнути проблем із примушенням культури до EN, використовуйте return CultureInfo.CurrentCulture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0;замість цього.
AndrewWhalan

3

якщо ви хочете перевірити, чи передана вами рядок є рядком, то для цього є простий метод.

string yourStringForCheck= "abc";
string stringInWhichWeCheck= "Test abc abc";

bool isContained = stringInWhichWeCheck.ToLower().IndexOf(yourStringForCheck.ToLower()) > -1;

Це булеве значення повернеться, якщо рядок міститься чи ні




2

Ви можете використовувати string.indexof ()функцію. Це буде нечутливим до випадку


2

Хитрість тут полягає в тому, щоб шукати рядок, ігноруючи регістр, але зберігати його точно так само (з тим самим випадком).

 var s="Factory Reset";
 var txt="reset";
 int first = s.IndexOf(txt, StringComparison.InvariantCultureIgnoreCase) + txt.Length;
 var subString = s.Substring(first - txt.Length, txt.Length);

Вихід "Скидання"


-1
public static class StringExtension
{
    #region Public Methods

    public static bool ExContains(this string fullText, string value)
    {
        return ExIndexOf(fullText, value) > -1;
    }

    public static bool ExEquals(this string text, string textToCompare)
    {
        return text.Equals(textToCompare, StringComparison.OrdinalIgnoreCase);
    }

    public static bool ExHasAllEquals(this string text, params string[] textArgs)
    {
        for (int index = 0; index < textArgs.Length; index++)
            if (ExEquals(text, textArgs[index]) == false) return false;
        return true;
    }

    public static bool ExHasEquals(this string text, params string[] textArgs)
    {
        for (int index = 0; index < textArgs.Length; index++)
            if (ExEquals(text, textArgs[index])) return true;
        return false;
    }

    public static bool ExHasNoEquals(this string text, params string[] textArgs)
    {
        return ExHasEquals(text, textArgs) == false;
    }

    public static bool ExHasNotAllEquals(this string text, params string[] textArgs)
    {
        for (int index = 0; index < textArgs.Length; index++)
            if (ExEquals(text, textArgs[index])) return false;
        return true;
    }

    /// <summary>
    /// Reports the zero-based index of the first occurrence of the specified string
    /// in the current System.String object using StringComparison.InvariantCultureIgnoreCase.
    /// A parameter specifies the type of search to use for the specified string.
    /// </summary>
    /// <param name="fullText">
    /// The string to search inside.
    /// </param>
    /// <param name="value">
    /// The string to seek.
    /// </param>
    /// <returns>
    /// The index position of the value parameter if that string is found, or -1 if it
    /// is not. If value is System.String.Empty, the return value is 0.
    /// </returns>
    /// <exception cref="ArgumentNullException">
    /// fullText or value is null.
    /// </exception>
    public static int ExIndexOf(this string fullText, string value)
    {
        return fullText.IndexOf(value, StringComparison.OrdinalIgnoreCase);
    }

    public static bool ExNotEquals(this string text, string textToCompare)
    {
        return ExEquals(text, textToCompare) == false;
    }

    #endregion Public Methods
}

-4

Простий спосіб для новачків:

title.ToLower().Contains("string");//of course "string" is lowercase.

Оголошення за те, що я був неправильним. Що робити, якщо назва = StRiNg? StRiNg! = String і StRiNg! = STRING
berniefitz

Я помилявся. Відредагувати відповідь так, занадто просто: <br/> title.ToLower (). Містить ("рядок") // звичайно "рядок" - малі літери
O Thhnh Ldt,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.