Без учета регистра 'Содержит (строка)'
Есть ли способ сделать следующее возвращение верным?
string title = "ASTRINGTOTEST";
title.Contains("string");
Кажется, что нет перегрузки, которая позволяла бы мне устанавливать чувствительность к регистру. В настоящее время я ОБОСНОВЛЯЮ их обоих, но это просто глупо (под этим я имею в виду проблемы i18n, которые идут с корпусом вверх и вниз).
ОБНОВИТЬ
Этот вопрос древний, и с тех пор я понял, что попросил простой ответ на действительно обширную и трудную тему, если вы хотите исследовать его полностью.
В большинстве случаев в одноязычных, английских кодовых базах этого ответа будет достаточно. Я подозреваю, потому что большинство людей, приходящих сюда, попадают в эту категорию, это самый популярный ответ.
Этот ответ, однако, поднимает внутреннюю проблему, заключающуюся в том, что мы не можем сравнивать текст без учета регистра, пока мы не знаем, что оба текста - это одна и та же культура, и мы не знаем, что это за культура. Возможно, это менее популярный ответ, но я думаю, что он более правильный, и поэтому я отметил его как таковой.
33 ответа
Чтобы проверить, если строка paragraph
содержит строку word
(спасибо @QuarterMeister)
culture.CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0
куда culture
является примером CultureInfo
описание языка, на котором написан текст
Это решение прозрачно в отношении определения чувствительности к регистру, которое зависит от языка. Например, английский язык использует символы I
а также i
для заглавных и строчных версий девятой буквы, тогда как турецкий язык использует эти символы для одиннадцатой и двенадцатой букв своего алфавита длиной 29 букв. Турецкая версия "i" в верхнем регистре - это незнакомый символ "İ".
Таким образом, строки tin
а также TIN
одно и то же слово на английском языке, но разные слова на турецком языке. Как я понимаю, один означает "дух", а другой - слово звукоподражания. (Турки, поправьте меня, если я ошибаюсь, или предложите лучший пример)
Подводя итог, вы можете ответить на вопрос "являются ли эти две строки одинаковыми, но в разных случаях", если вы знаете, на каком языке находится текст. Если вы не знаете, вам придется взять с собой пунт. Учитывая гегемонию английского языка в программном обеспечении, вы, вероятно, должны прибегнуть к CultureInfo.InvariantCulture
потому что это будет неправильно знакомыми способами.
Вы можете использовать метод 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);
Ты можешь использовать IndexOf()
как это:
string title = "STRING";
if (title.IndexOf("string", 0, StringComparison.CurrentCultureIgnoreCase) != -1)
{
// The string exists in the original
}
Поскольку 0 (ноль) может быть индексом, вы проверяете по отношению к -1.
Начинающаяся с нуля позиция индекса значения, если эта строка найдена, или -1, если это не так. Если значение равно String.Empty, возвращаемое значение равно 0.
Только.NET Core 2.0+ (на данный момент)
.NET Core имеет пару методов для решения этой проблемы начиная с версии 2.0:
- String.Contains (Char, StringComparison)
- String.Contains (String, StringComparison)
Пример:
"Test".Contains("test", System.StringComparison.CurrentCultureIgnoreCase);
Со временем они, вероятно, перейдут в стандарт.NET, а оттуда во все другие реализации библиотеки базовых классов.
Альтернативное решение с использованием Regex:
bool contains = Regex.IsMatch("StRiNG to search", Regex.Escape("string"), RegexOptions.IgnoreCase);
Вы всегда можете сначала поднять или опустить струны.
string title = "string":
title.ToUpper().Contains("STRING") // returns true
Ой, только что видел это последнее. Сравнение без учета регистра будет *
наверное*
в любом случае сделайте то же самое, и если производительность не является проблемой, я не вижу проблем с созданием копий в верхнем регистре и их сравнением. Я мог бы поклясться, что однажды увидел сравнение без учета регистра...
Одна из проблем с ответом состоит в том, что он выдаст исключение, если строка имеет значение 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;
}
Класс 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;
}
}
Это чисто и просто.
Regex.IsMatch(file, fileNamestr, RegexOptions.IgnoreCase)
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
Это самые простые решения.
По индексу
string title = "STRING"; if (title.IndexOf("string", 0, StringComparison.CurrentCultureIgnoreCase) != -1) { // contains }
Изменяя дело
string title = "STRING"; bool contains = title.ToLower().Contains("string")
По регулярному выражению
Regex.IsMatch(title, "string", RegexOptions.IgnoreCase);
Как просто так и работает
title.ToLower().Contains("String".ToLower())
Вы можете использовать параметр сравнения строк (доступен в .net 2.1 и выше) String.Contains Method .
public bool Contains (string value, StringComparison comparisonType);
Пример:
string title = "ASTRINGTOTEST";
title.Contains("string", StringComparison.InvariantCultureIgnoreCase);
Именно так:
string s="AbcdEf";
if(s.ToLower().Contains("def"))
{
Console.WriteLine("yes");
}
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);
}
Я знаю, что это не 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");
Использовать этот:
string.Compare("string", "STRING", new System.Globalization.CultureInfo("en-US"), System.Globalization.CompareOptions.IgnoreCase);
В конечном счете, общая операция "содержит" сводится к такой функции,
/// <summary>
/// Determines whether the source contains the sequence.
/// </summary>
/// <typeparam name="T">The type of the items in the sequences.</typeparam>
/// <param name="sourceEnumerator">The source enumerator.</param>
/// <param name="sequenceEnumerator">The sequence enumerator.</param>
/// <param name="equalityComparer">An equality comparer.</param>
/// <remarks>
/// An empty sequence will return <c>true</c>.
/// The sequence must support <see cref="IEnumerator.Reset"/>
/// if it does not begin the source.
/// </remarks>
/// <returns>
/// <c>true</c> if the source contains the sequence;
/// otherwise <c>false</c>.
/// </returns>
public static bool Contains<T>(
IEnumerator<T> sourceEnumerator,
IEnumerator<T> sequenceEnumerator,
IEqualityComparer<T> equalityComparer)
{
if (equalityComparer == null)
{
equalityComparer = EqualityComparer<T>.Default;
}
while (sequenceEnumerator.MoveNext())
{
if (sourceEnumerator.MoveNext())
{
if (!equalityComparer.Equals(
sourceEnumerator.Current,
sequenceEnumerator.Current))
{
sequenceEnumerator.Reset();
}
}
else
{
return false;
}
}
return true;
}
это может быть тривиально обернуто в расширенной версии, принимающей IEnumerable
как это,
public static bool Contains<T>(
this IEnumerable<T> source,
IEnumerable<T> sequence,
IEqualityComparer<T> equalityComparer = null)
{
if (sequence == null)
{
throw new ArgumentNullException("sequence");
}
using(var sequenceEnumerator = sequence.GetEnumerator())
using(var sourceEnumerator = source.GetEnumerator())
{
return Contains(
sourceEnumerator,
sequenceEnumerator,
equalityComparer);
}
}
Теперь это будет работать для порядкового сравнения любых последовательностей, включая строки, так как string
инвентарь IEnumerable<char>
,
// The optional parameter ensures the generic overload is invoked
// not the string.Contains() implementation.
"testable".Contains("est", EqualityComparer<char>.Default)
Однако, как мы знаем, строки не являются общими, они специализированы. Есть два ключевых фактора в игре.
- Проблема "обсадной колонны", которая сама по себе имеет различные языковые крайние случаи
- Довольно сложный вопрос о том, как набор "текстовых элементов" (букв / цифр / символов и т. Д.) Представлен кодовыми точками Юникода и какие допустимые последовательности символов могут представлять данную строку, подробности раскрыты в этих ответах.
Чистый эффект тот же. Строки, которые вы можете утверждать, лингвистически равны, могут быть корректно представлены различными комбинациями символов. Более того, правила действительности меняются между культурами.
Все это приводит к специальной реализации типа "Содержит" на основе строк.
using System.Globalization;
public static bool Contains(
this string source,
string value,
CultureInfo culture = null,
CompareOptions options = CompareOptions.None)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
var compareInfo = culture == null ?
CultureInfo.CurrentCulture.CompareInfo :
culture.CompareInfo;
var sourceEnumerator = StringInfo.GetTextElementEnumerator(source);
var sequenceEnumerator = StringInfo.GetTextElementEnumerator(value);
while (sequenceEnumerator.MoveNext())
{
if (sourceEnumerator.MoveNext())
{
if (!(compareInfo.Compare(
sourceEnumerator.Current,
sequenceEnumerator.Current,
options) == 0))
{
sequenceEnumerator.Reset();
}
}
else
{
return false;
}
}
return true;
}
Эта функция может использоваться для выполнения нечувствительного к регистру, специфичного для культуры "содержимого", которое будет работать независимо от нормализации строк. например
"testable".Contains("EST", StringComparer.CurrentCultureIgnoreCase)
Просто, чтобы развить ответ здесь, вы можете создать метод расширения строки, чтобы сделать его более удобным для пользователя:
public static bool ContainsIgnoreCase(this string paragraph, string word)
{
return new CultureInfo("en-US").CompareInfo.IndexOf(paragraph, word, CompareOptions.IgnoreCase) >= 0;
}
Это очень похоже на другой пример здесь, но я решил упростить enum до 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) )
....
Использование RegEx - прямой способ сделать это:
Regex.IsMatch(title, "string", RegexOptions.IgnoreCase);
Если вы хотите проверить, находится ли переданная вами строка в строке, то для этого есть простой метод.
string yourStringForCheck= "abc";
string stringInWhichWeCheck= "Test abc abc";
bool isContaines = stringInWhichWeCheck.ToLower().IndexOf(yourStringForCheck.ToLower()) > -1;
This boolean value will return if string contains or not
Аналогично предыдущим ответам (с использованием метода расширения), но с двумя простыми нулевыми проверками (C# 6.0 и выше):
public static bool ContainsIgnoreCase(this string source, string substring)
{
return source?.IndexOf(substring ?? "", StringComparison.OrdinalIgnoreCase) >= 0;
}
Если источник равен нулю, вернуть ложь (с помощью оператора распространения нуля?.)
Если подстрока равна нулю, рассматривать ее как пустую строку и возвращать истину (с помощью оператора объединения с нулем??)
При необходимости StringComparison, конечно, можно отправить в качестве параметра.
Несколько самых популярных ответов по-своему хороши и верны , я пишу здесь, чтобы добавить больше информации, контекста и перспективы.
Для ясности давайте рассмотрим, что строка A содержит строку B, если есть какая-либо подпоследовательность кодовых точек в A, которая равна B. Если мы принимаем это, проблема сводится к вопросу о том, равны ли две строки.
Вопрос о равенстве струн подробно рассматривался на протяжении многих десятилетий. Большая часть современных знаний заключена в сопоставлениях SQL. Нормальные формы Unicode близки к правильному подмножеству этого. Но есть еще кое-что, помимо сопоставления SQL.
Например , в сопоставлении SQL вы можете
Строго чувствителен к двоичному разряду - так что разные формы нормализации Unicode (например, предварительно объединенные или комбинированные акценты) сравниваются по-разному.
Например, может быть представлено как (предварительно комбинированное) или (e с комбинированным острым ударением).
Они такие же или разные?
Unicode normalized - в этом случае приведенные выше примеры будут равны друг другу, но не или.
без акцента (например, для текста на испанском, немецком, шведском и т. д.). В этом случае = = = =
без учета регистра и акцента , так что (например, для текста на испанском, немецком, шведском и т. д.). В этом случае
U+00e9
знак равноU+0065 U+0301
знак равноU+00c9
знак равноU+0045 U+0301
знак равноU+0049
знак равноU+0065
знак равноE
знак равноe
знак равноÉ
знак равноé
Канатип чувствителен или нечувствителен , то есть вы можете рассматривать японские хирагану и катакану как эквивалентные или разные. Два слоговых словаря содержат одинаковое количество символов, организованных и произносимых (в основном) одинаково, но написанных по-разному и используемых для разных целей. Например, катакана используется для заимствованных слов или иностранных имен, но хирагана используется для детских книг, руководств по произношению (например, рубинов) и там, где нет кандзи для слова (или, возможно, писатель не знает кандзи или думает читатель может этого не знать).
Чувствительность к полной или половинной ширине - японские кодировки включают два представления некоторых символов по историческим причинам - они отображались в разных размерах.
Лигатуры считаются эквивалентными или нет : см. Https://en.wikipedia.org/wiki/Ligature_(writing)
Является
æ
такой же какae
или не? У них разные кодировки Unicode, как и у акцентированных символов, но в отличие от акцентированных символов они также выглядят иначе.Что подводит нас к ...
Эквивалентность форм арабского представления
В арабском письме есть культура красивой каллиграфии, где определенные последовательности смежных букв имеют определенные представления. Многие из них были закодированы в стандарте Unicode. Я не совсем понимаю правила, но мне они кажутся аналогами лигатур.
Другие скрипты и системы: Я не знаю ни каннада, ни малаялам, ни сингальского, тайского, гуджаратского, тибетского, ни почти всех десятков или сотен не упомянутых скриптов. Я предполагаю, что у них есть аналогичные проблемы для программиста, и, учитывая количество проблем, упомянутых до сих пор, и для такого небольшого количества скриптов, у них, вероятно, также есть дополнительные проблемы, которые программист должен учитывать.
Это избавляет нас от "кодирующих" сорняков.
Теперь мы должны войти в «смысловые» сорняки.
равно ? Если нет, то равно? Если нет, то почему? Это романизация пиньинь.
Равно ? Если нет, то почему? Это романизация Уэйда-Джайлса.
Равно ? Если нет, то почему?
Зачем ты вообще это делаешь?
Например, если вы хотите знать, возможно ли, что две строки (A и B) относятся к одному и тому же географическому положению или к одному и тому же человеку, вы можете спросить:
Могут ли эти строки быть представлениями Уэйд-Джайлза или пиньинь набора последовательностей китайских иероглифов? Если да, то есть ли совпадения между соответствующими наборами?
Может ли одна из этих строк быть кириллической транскрипцией китайского иероглифа?
Может ли одна из этих строк быть кириллической транслитерацией романизации пиньинь?
Может ли одна из этих строк быть кириллической транслитерацией романизации пиньинь китаизации английского имени?
Ясно, что это сложные вопросы, на которые нет однозначных ответов, и в любом случае ответ может отличаться в зависимости от цели вопроса.
В заключение приведем конкретный пример.
- Если вы доставляете письмо или посылку, очевидно,
Peking
,Bĕijīng
а также北京
все равны. Для этого все они одинаково хороши. Несомненно, китайские почтовые отделения признают и многие другие варианты, такие какPékin
На французском,Pequim
на португальском,Bắc Kinh
на вьетнамском языке иБээжин
на монгольском.
Слова не имеют фиксированного значения.
Слова - это инструменты, которые мы используем, чтобы ориентироваться в мире, выполнять наши задачи и общаться с другими людьми.
Хотя, похоже, было бы полезно, если бы такие слова, как
equality
,
Beijing
, или
meaning
имели фиксированные значения, но, к сожалению, это не так.
Тем не менее, мы, кажется, как-то заблудились.
TL;DR: Если вы имеете дело с вопросами, относящимися к реальности, во всей ее туманности (облачность, неопределенность, отсутствие четких границ), в основном есть три возможных ответа на каждый вопрос:
- Наверное
- Возможно нет
- Может быть
в новой версии .net есть возможность игнорировать регистр
examplestring.Contains("exampleSTRING", StringComparison.OrdinalIgnoreCase)
Что ж, я наткнулся на этот пост, поэтому решил провести тест на основе некоторых популярных ответов, и, короче говоря, ответ ДжаредПара — самый быстрый с 0 выделением памяти, а ответ полковника Паника — самый медленный.
Информация о тесте
- Дата: 2023/05 г.
- .NET SDK: 7.0.103
- БенчмаркДотНет: 0.13.5
- Процессор: i3-8100
- ОС: Арч Линукс
Используемый код
[MemoryDiagnoser]
public class StringContains
{
[Params("How to install Arch Linux?")]
public string Phrase { get; set; }
[Params("How to", "arch", "blazor", "random long string to see if it effects the time needed")]
public string search { get; set; }
[Benchmark(Baseline = true)]
public bool Contains() =>
Phrase.Contains(search, System.StringComparison.CurrentCultureIgnoreCase);
[Benchmark]
public bool toUpper() =>
Phrase.ToUpper().Contains(search.ToUpper());
[Benchmark]
public bool toLower() =>
Phrase.ToLower().Contains(search.ToLower());
[Benchmark]
public bool IndexeOf() =>
Phrase.IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0;
[Benchmark]
public bool CultureCompareInfo()
{
var culture = new CultureInfo("en-US");
return culture.CompareInfo.IndexOf(Phrase, search, CompareOptions.IgnoreCase) >= 0;
}
}
Полученные результаты
я удалил несколько столбцов, потому что они не имеют особого значения
Легенды
Фраза: значение параметра «Фраза».
search : Значение параметра «поиск».
Среднее значение: среднее арифметическое всех измерений.
Коэффициент: среднее значение распределения коэффициента ([Текущий]/[Базовый уровень]).
Gen0: сборщик мусора поколения 0 собирает данные за 1000 операций.
Выделено: выделенная память на одну операцию (только управляемая, включительно, 1 КБ = 1024 Б).
1 нс: 1 наносекунда (0,000000001 сек)
if ("strcmpstring1".IndexOf(Convert.ToString("strcmpstring2"), StringComparison.CurrentCultureIgnoreCase) >= 0){return true;}else{return false;}
Хитрость здесь в том, чтобы искать строку, игнорируя регистр, но сохраняя ее точно такой же (с тем же регистром).
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);
Выход "Сброс"
Ты можешь использовать string.indexof ()
функция. Это будет без учета регистра
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
}