Питання Регістр нечутливий 'містить (рядок)'


Чи є спосіб зробити наступне повернення істинним?

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

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

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


2420
2018-01-14 21:39


походження


Як це нерозумно? Ви маєте на увазі, що ви виконуєте два пропуски на рядок? Я вважаю, що порівняння, нечутливі до випадків, просто поєднують два етапи. - Calyth
Оскільки я буду використовувати його на worldwebz, я повинен брати до уваги іноземні символи. Як згадувалося в відповіді нижче, посилення та зниження рівня спричиняють проблеми інтернаціоналізації. - Boris Callens
Обидва ці рядки вгорі оболонки є дурними, оскільки ви створюєте дві нові рядки, а потім виконуєте пошук за чутками до регістру. Існує необгрунтована додаткова обробка та пам'ять, пов'язані з створенням нових рядків, як-от, особливо якщо ви шукаєте через набір рядків, а ви великі випадки пошукових або вихідних умов надмірно. Метод IndexOf, який дозволяє точно визначити значення StringComparison, є кращим. - 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, тому що це буде неправильним у знайомих способів.


1087
2018-03-17 18:22



Чому ні culture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0? Вона використовує правильну культуру і нечутлива до регістру, вона не виділяє тимчасові рядки малих рядків, і це дозволяє уникнути питання про те, чи перетворення в нижній регістр і зіставлення завжди є таким самим, як нечутливе порівняння. - Quartermeister
Це рішення також без потреби забруднює купу, виділяючи пам'ять за те, що повинна бути пошуковою функцією - JaredPar
Порівняння з ToLower () дасть різні результати від неактуального IndexOf, коли два різні букви мають однакову малу літеру. Наприклад, зателефонувавши ToLower () на або U + 0398 "Greek Capital Letter Theta" або U + 03F4 "Greek Capital Letter Theta Symbol" призводить до U + 03B8, "Greek Small Letter Theta", але великі літери вважаються різними. Обидва рішення розглядають малі літери з однаковою іншою великою буквою, такі як U + 0073 "Латинський маленький букв S" та U + 017F "Латинський маленький букв довгий S", тому рішення IndexOf виглядає більш послідовним. - Quartermeister
+1 для повноти - відповіді з відповідною формою пояснення - це єдиний спосіб, яким користувачі дійсно навчаться на SO - TheGeekZn
Чому ви не написали "ddddfg" .IndexOf ("Df", StringComparison.OrdinalIgnoreCase)? - Chen


Ви можете скористатись String.IndexOf Method і пройти 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



Великий метод розширення рядка! Я відредагував шахту, щоб перевірити вихідний рядок не нуль, щоб запобігти будь-яким помилкам посилання на об'єкт під час виконання .IndexOf (). - Richard Pursehouse
Це дає таку саму відповідь, як paragraph.ToLower(culture).Contains(word.ToLower(culture)) з CultureInfo.InvariantCulture і це не вирішує будь-яких питань локалізації. Навіщо ускладнювати речі? stackoverflow.com/a/15464440/284795 - Colonel Panic
@ ColonelPanic the ToLower версія включає в себе 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   якщо це не так. Якщо значення є 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 може допомогти Regex все ще здається непотрібним, коли IndexOf / розширення Contains є простим (і, можливо, більш зрозумілим). - Dan Mangiarelli
Зауважте, що я не мав на увазі, що це рішення Regex було найкращим способом. Я просто додав до списку відповідей на початкове опубліковане питання "Чи є спосіб зробити наступне повернення істинним?". - Jed


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

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

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


63
2018-01-14 21:42



Цікаво, що я бачив ToUpper (), що рекомендував використовувати ToLower () у такому сценарії, тому що, мабуть, ToLower () може "втратити вірність" у деяких культурах, тобто дві різні символи великими літерами перекладаються на одне і те ж нижній регістр - Matt Hamilton
Шукайте "Туреччина тест" :) - Jon Skeet
У деяких французьких локалізаціях великі літери не мають діакритик, тому ToUpper () може бути не кращим, ніж ToLower (). Я б сказав, що використовуйте відповідні інструменти, якщо вони доступні - порівняйте регістр. - Blair Conrad
Не використовуйте ToUpper або ToLower, і виконуйте те, що сказав Джон Скет - Peter Gfader
Просто це побачило знову через два роки і нове падіння ... все одно я згоден, що є кращі способи порівняння струн. Проте не всі програми будуть локалізовані (більшість з них не будуть), і багато хто з них є внутрішніми або віддаленими додатками. Оскільки я не можу сподіватися на те, що поради краще підуть на користь віддалених програм ... Я рухаюсь: D - Ed S.


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

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



Якщо checkCleck - це порожній рядок, то він повинен повернути істину на документацію Contains: "true, якщо значення параметра відбувається в цьому рядку, або якщо значення - порожній рядок (" "), в іншому випадку - false". - amurra
Виходячи з вищезазначеного коментаря Амурри, чи не рекомендується коректувати запропонований код? І чи слід це додавати до прийнятої відповіді, так що найкраща відповідь є першою? - David White
Тепер це повернеться true, якщо джерелом є порожня рядок або нуль, незалежно від того, що таке check. Це не може бути правильним. Також IndexOf вже повертає true, якщо checkCleck - це порожня рядок і джерело не нульове. Тут потрібно перевірити нуль. Я пропоную якщо (source == null || value == null) return 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?

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

Дос

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


Так само:

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

12
2017-07-13 09:54



Це не специфічно для культури, і в деяких випадках це може призвести до невдач. culture.CompareInfo.IndexOf (параграф, слово, CompareOptions.IgnoreCase) слід використовувати. - hikalkan
Чому слід уникати string.ToLower () при порівнянні без регістру? Tl; др Це дорого, тому що новий рядок "виготовлено". - Liam


Я знаю, що це не 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");

10
2017-09-09 13:23



Ви також знаєте, як воно працює внутрішньо? - Boris Callens