Как не включать разрывы строк при сравнении двух строк - PullRequest
2 голосов
/ 06 августа 2010

Я сравниваю обновления с двумя строками. я сделал:

 string1 != string2

и они получаются разные. Я помещаю их в «Add Watch» и вижу, что единственное отличие состоит в том, что у одного разрывы строк, а у другого нет ».

 string1 = "This is a test. \nThis is a test";
 string2 = "This is a test. This is a test";

Я в основном хочу сравнить, но не включаю разрывы строк. Так что, если разрыв строки - единственная разница, считайте их равными.

Ответы [ 10 ]

9 голосов
/ 06 августа 2010

Быстрый и грязный способ, когда производительность не так уж важна:

3 голосов
/ 06 августа 2010

Предполагая, что:

  1. Здесь требуется прямое сравнение! = И ==, за исключением случаев перевода строки.
  2. Строки являются или могут быть достаточно большими или сравниваться достаточно часто, чтобы просто заменить "\n" пустой строкой слишком неэффективно.

Тогда:

public bool LinelessEquals(string x, string y)
{
    //deal with quickly handlable cases quickly.
    if(ReferenceEquals(x, y))//same instance
        return true;         // - generally happens often in real code,
                             //and is a fast check, so always worth doing first.
    //We already know they aren't both null as
    //ReferenceEquals(null, null) returns true.
    if(x == null || y == null)
        return false;
    IEnumerator<char> eX = x.Where(c => c != '\n').GetEnumerator();
    IEnumerator<char> eY = y.Where(c => c != '\n').GetEnumerator();
    while(eX.MoveNext())
    {
        if(!eY.MoveNext()) //y is shorter
            return false;
        if(ex.Current != ey.Current)
            return false;
    }
    return !ey.MoveNext(); //check if y was longer.
}

Это определяется как равенство, а не неравенство, поэтому вы можете легко адаптировать его для реализации IEqualityComparer<string>.Equals. Ваш вопрос для без строки string1 != string2 становится: !LinelessEquals(string1, string2)

2 голосов
/ 18 ноября 2013

Более чистый подход будет использовать:

string1.Replace(Environment.NewLine, String.Empty) != string2.Replace(Environment.NewLine, String.Empty);
1 голос
/ 31 декабря 2016

Вот сравнитель равенства для строк, который игнорирует определенные символы, такие как \r и \n.

Эта реализация не выделяет кучу памяти во время выполнения, что способствует ее производительности. Это также позволяет избежать виртуальных звонков через IEnumerable и IEnumerator.

public sealed class SelectiveStringComparer : IEqualityComparer<string>
{
    private readonly string _ignoreChars;

    public SelectiveStringComparer(string ignoreChars = "\r\n")
    {
        _ignoreChars = ignoreChars;
    }

    public bool Equals(string x, string y)
    {
        if (ReferenceEquals(x, y))
            return true;
        if (x == null || y == null)
            return false;
        var ix = 0;
        var iy = 0;
        while (true)
        {
            while (ix < x.Length && _ignoreChars.IndexOf(x[ix]) != -1)
                ix++;
            while (iy < y.Length && _ignoreChars.IndexOf(y[iy]) != -1)
                iy++;
            if (ix >= x.Length)
                return iy >= y.Length;
            if (iy >= y.Length)
                return false;
            if (x[ix] != y[iy])
                return false;
            ix++;
            iy++;
        }
    }

    public int GetHashCode(string obj)
    {
        throw new NotSupportedException();
    }
}
1 голос
/ 27 октября 2016

Это обобщенная и проверенная версия ответа Джона Ханнаса.

/// <summary>
/// Compares two character enumerables one character at a time, ignoring those specified.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="ignoreThese"> If not specified, the default is to ignore linefeed and newline: {'\r', '\n'} </param>
/// <returns></returns>
public static bool EqualsIgnoreSome(this IEnumerable<char> x, IEnumerable<char> y, params char[] ignoreThese)
{
    // First deal with quickly handlable cases quickly:
    // Same instance - generally happens often in real code, and is a fast check, so always worth doing first.
    if (ReferenceEquals(x, y))
        return true;         //
    // We already know they aren't both null as ReferenceEquals(null, null) returns true.
    if (x == null || y == null)
        return false;
    // Default ignore is newlines:
    if (ignoreThese == null || ignoreThese.Length == 0)
        ignoreThese = new char[] { '\r', '\n' };
    // Filters by specifying enumerator.
    IEnumerator<char> eX = x.Where(c => !ignoreThese.Contains(c)).GetEnumerator();
    IEnumerator<char> eY = y.Where(c => !ignoreThese.Contains(c)).GetEnumerator();
    // Compares.
    while (eX.MoveNext())
    {
        if (!eY.MoveNext()) //y is shorter
            return false;
        if (eX.Current != eY.Current)
            return false;
    }
    return !eY.MoveNext(); //check if y was longer.
}
1 голос
/ 06 августа 2010

Я бы предложил регулярное выражение, чтобы уменьшить каждое space, tab, \r, \n до одного пробела:

Regex.Replace(string1, @"\s+", " ") != Regex.Replace(string2, @"\s+", " ")
0 голосов
/ 27 марта 2019

Я сталкивался с этой проблемой несколько раз, когда пишу модульные тесты, в которых необходимо сравнить ожидаемые строки из нескольких строк с фактическими выходными строками.

Например, если я пишуметод, который выводит многострочную строку. Меня интересует, как выглядит каждая строка, но мне нет дела до конкретного символа новой строки, используемого на компьютере с Windows или Mac.

В моем случае я просто хочу заявитьчто каждая строка равна в моих модульных тестах и ​​выручает, если один из них не.

public static void AssertAreLinesEqual(string expected, string actual)
{
    using (var expectedReader = new StringReader(expected))
    using (var actualReader = new StringReader(actual))
    {
        while (true)
        {
            var expectedLine = expectedReader.ReadLine();
            var actualLine = actualReader.ReadLine();

            Assert.AreEqual(expectedLine, actualLine);

            if(expectedLine == null || actualLine == null)
                break;
        }
    }
}

Конечно, вы также можете сделать метод немного более универсальным и написать, чтобы вернуть boolвместо этого.

public static bool AreLinesEqual(string expected, string actual)
{
    using (var expectedReader = new StringReader(expected))
    using (var actualReader = new StringReader(actual))
    {
        while (true)
        {
            var expectedLine = expectedReader.ReadLine();
            var actualLine = actualReader.ReadLine();

            if (expectedLine != actualLine)
                return false;

            if(expectedLine == null || actualLine == null)
                break;
        }
    }

    return true;
}

Что меня больше всего удивляет, так это то, что подобный метод не включен ни в одну из модулей модульного тестирования, которые я использовал.

0 голосов
/ 05 апреля 2017

Вот версия на VB.net, основанная на ответе Дрю Ноакса

Dim g_sIgnore As String = vbSpace & vbNewLine & vbTab 'String.Format("\n\r\t ")

Public Function StringCompareIgnoringWhitespace(s1 As String, s2 As String) As Boolean
    Dim i1 As Integer = 0
    Dim i2 As Integer = 0
    Dim s1l As Integer = s1.Length
    Dim s2l As Integer = s2.Length

    Do
        While i1 < s1l AndAlso g_sIgnore.IndexOf(s1(i1)) <> -1
            i1 += 1
        End While
        While i2 < s2l AndAlso g_sIgnore.IndexOf(s2(i2)) <> -1
            i2 += 1
        End While
        If i1 = s1l And i2 = s2l Then
            Return True
        Else
            If i1 < s1l AndAlso i2 < s2l AndAlso s1(i1) = s2(i2) Then
                i1 += 1
                i2 += 1
            Else
                Return False
            End If
        End If
    Loop
    Return False
End Function

Я также проверил ее с

Try
    Debug.Assert(Not StringCompareIgnoringWhitespace("a", "z"))
    Debug.Assert(Not StringCompareIgnoringWhitespace("aa", "zz"))
    Debug.Assert(StringCompareIgnoringWhitespace("", ""))
    Debug.Assert(StringCompareIgnoringWhitespace(" ", ""))
    Debug.Assert(StringCompareIgnoringWhitespace("", " "))
    Debug.Assert(StringCompareIgnoringWhitespace(" a", "a "))
    Debug.Assert(StringCompareIgnoringWhitespace(" aa", "aa "))
    Debug.Assert(StringCompareIgnoringWhitespace(" aa ", " aa "))
    Debug.Assert(StringCompareIgnoringWhitespace(" aa a", " aa a"))
    Debug.Assert(Not StringCompareIgnoringWhitespace("a", ""))
    Debug.Assert(Not StringCompareIgnoringWhitespace("", "a"))
    Debug.Assert(Not StringCompareIgnoringWhitespace("ccc", ""))
    Debug.Assert(Not StringCompareIgnoringWhitespace("", "ccc"))
Catch ex As Exception
    Console.WriteLine(ex.ToString)
End Try
0 голосов
/ 06 августа 2010

Неужели вы просто удаляете разрывы строк перед сравнением строк?

Например (псевдокод) ...

string1.replace('\n','') != string2.replace('\n','')
0 голосов
/ 06 августа 2010
string1.replace('\n','') != string2.replace('\n','')
...