Как не включать разрывы строк при сравнении двух строк

2

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

 string1 != string2

и они оказываются разными. Я помещал их в "Добавить часы", и я вижу, что единственное различие заключается в том, что у одного есть разрывы строк, а у другого нет.:

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

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

  • 1
    Также см. Stackoverflow.com/questions/1862314/…
  • 0
    Технически говоря, 2 строки различны и будут отображаться по- разному на экране или при печати их. Тем не менее, любой из предоставленных ответов, конечно, будет делать то, что вы хотите.
Теги:
string
comparison

9 ответов

8
Лучший ответ

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

string1.Replace("\n", "") != string2.Replace("\n", "")
  • 0
    Этот ответ не учитывает символ \r .
  • 1
    Расширяя ссылку на производительность, потенциальная проблема здесь заключается в том, что каждое сравнение включает в себя выделение копий каждой строки в куче. Если производительность важна, взгляните на мой ответ .
3

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

  • Сопряженное сравнение char -value-for-char -value of!= и == - это то, что здесь требуется, за исключением вопроса о переводах строк.
  • Строки являются или могут быть достаточно большими или сравниваться достаточно часто, чтобы просто заменить "\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. Ваш вопрос для linebreak-less string1 != string2 будет выглядеть следующим образом: !LinelessEquals(string1, string2)

  • 0
    давайте посмотрим, +1 за то, что нашли время, чтобы дать хороший ответ, +1 за первую часть, где вы проверяете равенство ссылок и null, и -1 за перебор linq в методе сравнения строк. +1 в целом :)
  • 0
    Я поместил лямбду туда главным образом потому, что она сделала код более компактным, и это довольно прямолинейное выражение, которое будет выполнено достаточно быстро. Старомодным способом было бы добавить вспомогательную функцию, которая возвращала бы символы, которые не были переводами строк. Действительно старомодный (.NET 1.1) способ должен иметь вспомогательный класс или быть намного более сложным в своей итерации. Трудно иметь избыточное количество в методе сравнения строк, в общем случае сравнение строк может быть действительно очень сложным (рассмотрим общий случай ß-соответствия SS в случае сравнения без учета регистра).
2

Я предлагаю регулярное выражение для сокращения всех space, tab, \r, \n до одного пробела:

Regex.Replace(string1, @"\s+", " ") != Regex.Replace(string2, @"\s+", " ")
  • 0
    Но это уберет пробел и символы табуляции - я думаю, что ОП хотел только игнорировать разрывы строк
  • 0
    Да, как я уже сказал, но я не могу понять, почему несколько пробелов должны иметь значение, когда новые строки не имеют значения
Показать ещё 1 комментарий
1

Здесь сопоставляет равенство для строк, которые игнорируют определенные символы, такие как \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

Более чистый подход заключается в использовании:

string1.Replace(Environment.NewLine, String.Empty) != string2.Replace(Environment.NewLine, String.Empty);
0

Здесь версия в VB.net на основе Drew Noakes отвечает

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

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

/// <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.
}
0
string1.replace('\n','') != string2.replace('\n','')
0

Не могли бы вы просто разбить разрывы строк перед сравнением строк?

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

string1.replace('\n','') != string2.replace('\n','')
  • 0
    @ Пол Крисей: Вы победили меня в этом!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню