Как я могу сравнить 2 строки в С#, игнорируя случай, пробелы и любые разрывы строк. Мне также нужно проверить, являются ли обе строки равными нулю, тогда они отмечены как одинаковые.
Спасибо!
Как я могу сравнить 2 строки в С#, игнорируя случай, пробелы и любые разрывы строк. Мне также нужно проверить, являются ли обе строки равными нулю, тогда они отмечены как одинаковые.
Спасибо!
Удалите все символы, которые вам не нужны, а затем используйте метод ToLower(), чтобы игнорировать регистр.
edit: В то время как вышеизложенное работает, лучше использовать StringComparison.OrdinalIgnoreCase
. Просто передайте его как второй аргумент методу Equals
.
Вы должны нормализовать каждую строку, удалив символы, которые вы не хотите сравнивать, и затем вы можете выполнить String.Equals
с StringComparison
, который игнорирует случай.
Что-то вроде этого:
string s1 = "HeLLo wOrld!";
string s2 = "Hello\n WORLd!";
string normalized1 = Regex.Replace(s1, @"\s", "");
string normalized2 = Regex.Replace(s2, @"\s", "");
bool stringEquals = String.Equals(
normalized1,
normalized2,
StringComparison.OrdinalIgnoreCase);
Console.WriteLine(stringEquals);
Здесь Regex.Replace
используется сначала для удаления всех пробельных символов. Частный случай, когда обе строки имеют значение null, здесь не обрабатывается, но вы можете легко обработать этот случай до выполнения нормализации строки.
Если вам нужна производительность, решения Regex на этой странице работают слишком медленно для вас. Возможно, у вас есть большой список строк, которые вы хотите отсортировать. (Однако решение Regex более читаемо)
У меня есть класс, который смотрит на каждый отдельный char в обеих строках и сравнивает их, игнорируя случай и пробел. Он не выделяет никаких новых строк. Он использует char.IsWhiteSpace(ch)
для определения пробелов и char.ToLowerInvariant(ch)
для нечувствительности к регистру (если требуется). В моем тестировании мое решение работает примерно на 5x-8 раз быстрее, чем решение на основе Regex. Мой класс также реализует метод IEqualityComparer GetHashCode(obj)
, используя этот код в другом ответе SO. Этот GetHashCode(obj)
также игнорирует пробелы и, возможно, игнорирует регистр.
Здесь мой класс:
private class StringCompIgnoreWhiteSpace : IEqualityComparer<string>
{
public bool Equals(string strx, string stry)
{
if (strx == null) //stry may contain only whitespace
return string.IsNullOrWhiteSpace(stry);
else if (stry == null) //strx may contain only whitespace
return string.IsNullOrWhiteSpace(strx);
int ix = 0, iy = 0;
for (; ix < strx.Length && iy < stry.Length; ix++, iy++)
{
char chx = strx[ix];
char chy = stry[iy];
//ignore whitespace in strx
while (char.IsWhiteSpace(chx) && ix < strx.Length)
{
ix++;
chx = strx[ix];
}
//ignore whitespace in stry
while (char.IsWhiteSpace(chy) && iy < stry.Length)
{
iy++;
chy = stry[iy];
}
if (ix == strx.Length && iy != stry.Length)
{ //end of strx, so check if the rest of stry is whitespace
for (int iiy = iy + 1; iiy < stry.Length; iiy++)
{
if (!char.IsWhiteSpace(stry[iiy]))
return false;
}
return true;
}
if (ix != strx.Length && iy == stry.Length)
{ //end of stry, so check if the rest of strx is whitespace
for (int iix = ix + 1; iix < strx.Length; iix++)
{
if (!char.IsWhiteSpace(strx[iix]))
return false;
}
return true;
}
//The current chars are not whitespace, so check that they're equal (case-insensitive)
//Remove the following two lines to make the comparison case-sensitive.
chx = char.ToLowerInvariant(chx);
chy = char.ToLowerInvariant(chy);
if (chx != chy)
return false;
}
//If strx has more chars than stry
for (; ix < strx.Length; ix++)
{
if (!char.IsWhiteSpace(strx[ix]))
return false;
}
//If stry has more chars than strx
for (; iy < stry.Length; iy++)
{
if (!char.IsWhiteSpace(stry[iy]))
return false;
}
return true;
}
public int GetHashCode(string obj)
{
if (obj == null)
return 0;
int hash = 17;
unchecked // Overflow is fine, just wrap
{
for (int i = 0; i < obj.Length; i++)
{
char ch = obj[i];
if(!char.IsWhiteSpace(ch))
//use this line for case-insensitivity
hash = hash * 23 + char.ToLowerInvariant(ch).GetHashCode();
//use this line for case-sensitivity
//hash = hash * 23 + ch.GetHashCode();
}
}
return hash;
}
}
private static void TestComp()
{
var comp = new StringCompIgnoreWhiteSpace();
Console.WriteLine(comp.Equals("abcd", "abcd")); //true
Console.WriteLine(comp.Equals("abCd", "Abcd")); //true
Console.WriteLine(comp.Equals("ab Cd", "Ab\n\r\tcd ")); //true
Console.WriteLine(comp.Equals(" ab Cd", " A b" + Environment.NewLine + "cd ")); //true
Console.WriteLine(comp.Equals(null, " \t\n\r ")); //true
Console.WriteLine(comp.Equals(" \t\n\r ", null)); //true
Console.WriteLine(comp.Equals("abcd", "abcd h")); //false
Console.WriteLine(comp.GetHashCode(" a b c d")); //-699568861
//This is -699568861 if you #define StringCompIgnoreWhiteSpace_CASE_INSENSITIVE
// Otherwise it -1555613149
Console.WriteLine(comp.GetHashCode("A B c \t d"));
}
Здесь мой тестовый код (с примером Regex):
private static void SpeedTest()
{
const int loop = 100000;
string first = "a bc d";
string second = "ABC D";
var compChar = new StringCompIgnoreWhiteSpace();
Stopwatch sw1 = Stopwatch.StartNew();
for (int i = 0; i < loop; i++)
{
bool equals = compChar.Equals(first, second);
}
sw1.Stop();
Console.WriteLine(string.Format("char time = {0}", sw1.Elapsed)); //char time = 00:00:00.0361159
var compRegex = new StringCompIgnoreWhiteSpaceRegex();
Stopwatch sw2 = Stopwatch.StartNew();
for (int i = 0; i < loop; i++)
{
bool equals = compRegex.Equals(first, second);
}
sw2.Stop();
Console.WriteLine(string.Format("regex time = {0}", sw2.Elapsed)); //regex time = 00:00:00.2773072
}
private class StringCompIgnoreWhiteSpaceRegex : IEqualityComparer<string>
{
public bool Equals(string strx, string stry)
{
if (strx == null)
return string.IsNullOrWhiteSpace(stry);
else if (stry == null)
return string.IsNullOrWhiteSpace(strx);
string a = System.Text.RegularExpressions.Regex.Replace(strx, @"\s", "");
string b = System.Text.RegularExpressions.Regex.Replace(stry, @"\s", "");
return String.Compare(a, b, true) == 0;
}
public int GetHashCode(string obj)
{
if (obj == null)
return 0;
string a = System.Text.RegularExpressions.Regex.Replace(obj, @"\s", "");
return a.GetHashCode();
}
}
Сначала замените все пробелы посредством регулярного выражения из обеих строк, а затем используйте метод String.Compare
с параметром ignoreCase = true.
string a = System.Text.RegularExpressions.Regex.Replace("void foo", @"\s", "");
string b = System.Text.RegularExpressions.Regex.Replace("voidFoo", @"\s", "");
bool isTheSame = String.Compare(a, b, true) == 0;
Я бы, вероятно, начал с удаления символов, которые вы не хотите сравнивать со строкой перед сравнением. Если производительность является проблемой, вы можете посмотреть сохранение каждой строки с уже удаленными символами.
В качестве альтернативы вы можете написать процедуру сравнения, которая пропускает символы, которые вы хотите игнорировать. Но мне кажется, что это больше для меня.
Вы также можете использовать следующую настраиваемую функцию
public static string ExceptChars(this string str, IEnumerable<char> toExclude)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.Length; i++)
{
char c = str[i];
if (!toExclude.Contains(c))
sb.Append(c);
}
return sb.ToString();
}
public static bool SpaceCaseInsenstiveComparision(this string stringa, string stringb)
{
return (stringa==null&&stringb==null)||stringa.ToLower().ExceptChars(new[] { ' ', '\t', '\n', '\r' }).Equals(stringb.ToLower().ExceptChars(new[] { ' ', '\t', '\n', '\r' }));
}
И затем используйте его следующим образом
"Te st".SpaceCaseInsenstiveComparision("Te st");
Другим вариантом является метод LINQ SequenceEquals
, который согласно моим тестам более чем в два раза быстрее, чем подход Regex, используемый в других ответах и очень легко читать и поддерживать.
public static bool Equals_Linq(string s1, string s2)
{
return Enumerable.SequenceEqual(
s1.Where(c => !char.IsWhiteSpace(c)).Select(char.ToUpperInvariant),
s2.Where(c => !char.IsWhiteSpace(c)).Select(char.ToUpperInvariant));
}
public static bool Equals_Regex(string s1, string s2)
{
return string.Equals(
Regex.Replace(s1, @"\s", ""),
Regex.Replace(s2, @"\s", ""),
StringComparison.OrdinalIgnoreCase);
}
Вот простой код проверки производительности, который я использовал:
var s1 = "HeLLo wOrld!";
var s2 = "Hello\n WORLd!";
var watch = Stopwatch.StartNew();
for (var i = 0; i < 1000000; i++)
{
Equals_Linq(s1, s2);
}
Console.WriteLine(watch.Elapsed); // ~1.7 seconds
watch = Stopwatch.StartNew();
for (var i = 0; i < 1000000; i++)
{
Equals_Regex(s1, s2);
}
Console.WriteLine(watch.Elapsed); // ~4.6 seconds
Это также может работать.
String.Compare(s1, s2, CultureInfo.CurrentCulture, CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols) == 0
Подход не оптимизирован для производительности, но для полноты.
null
фрагмент кода:
public static class StringHelper
{
public static bool AreEquivalent(string source, string target)
{
if (source == null) return target == null;
if (target == null) return false;
var normForm1 = Normalize(source);
var normForm2 = Normalize(target);
return string.Equals(normForm1, normForm2);
}
private static string Normalize(string value)
{
Debug.Assert(value != null);
// normalize unicode, combining characters, diacritics
value = value.Normalize(NormalizationForm.FormC);
// normalize new lines to white space
value = value.Replace("\r\n", "\n").Replace("\r", "\n");
// normalize white space
value = Regex.Replace(value, @"\s", string.Empty);
// normalize casing
return value.ToLowerInvariant();
}
}
Trim()
чтобы удалить все StringComparison.OrdinalIgnoreCase
чтобы игнорировать регистр, например. stringA.Equals(stringB, StringComparison.OrdinalIgnoreCase)
Другой вариант, гибкий (с добавлением любых пропущенных символов), без изменения исходных строк или создания новых.
string str = "Hel lo world!";
string rts = "Hello\n world!";
char[] skips = { ' ', '\n' };
if (CompareExcept(str, rts, skips))
Console.WriteLine("The strings are equal.");
else
Console.WriteLine("The strings are not equal.");
static bool CompareExcept(string str, string rts, char[] skips)
{
if (str == null && rts == null) return true;
if (str == null || rts == null) return false;
var strReader = new StringReader(str);
var rtsReader = new StringReader(rts);
char nonchar = char.MaxValue;
Predicate<char> skiper = delegate (char chr)
{
foreach (var skp in skips)
{
if (skp == chr) return true;
}
return false;
};
while (true)
{
char a = strReader.GetCharExcept(skiper);
char b = rtsReader.GetCharExcept(skiper);
if (a == b)
{
if (a == nonchar) return true;
else continue;
}
else return false;
}
}
class StringReader : System.IO.StringReader
{
public StringReader(string str) : base(str) { }
public char GetCharExcept(Predicate<char> compare)
{
char ch;
while (compare(ch = (char)Read())) { }
return ch;
}
}
Попробуйте использовать это вместо этого, его намного чище и берет его из операционной системы, которую вы используете:
Environment.NewLine