如何在比较两个字符串时不包括换行符

11

我在比较两个字符串的更新。我执行了:

 string1 != string2

它们最终会变得不同。我将它们放在“添加监视器”中,我发现唯一的区别是一个有换行符,而另一个没有。

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

我基本上想进行比较,但不包括换行符。因此,如果换行符是唯一的区别,则将它们视为相等。


1
请参见https://dev59.com/w3I-5IYBdhLWcg3wbHq-。 - Mikael Svenson
从技术角度来说,这两个字符串是不同的,如果你在屏幕上或者打印出来,它们会显示不同。话虽如此,任何一个提供的答案都可以达到你想要的效果。 - ThunderGr
11个回答

13

如果性能不是很重要的话,一种快速而简单的方法是:

string1.Replace("\n", "") != string2.Replace("\n", "")

4
这个回答没有考虑到\r字符。 - Drew Noakes
1
在提到性能的基础上,潜在的问题在于每个比较都涉及在堆上分配每个字符串的副本。如果性能很重要,请查看我的答案 - Drew Noakes

3

我建议使用正则表达式将每个 空格制表符\r\n 缩减为一个空格:

Regex.Replace(string1, @"\s+", " ") != Regex.Replace(string2, @"\s+", " ")

但是这将删除空格和制表符 - 我认为 OP 只想忽略换行符。 - barrylloyd
是的,就像我说的那样,但我不明白为什么多个空格会有影响,因为换行无关紧要。 - Diadistis
@Diadistis:恰好我有一个使用案例需要这个——为Visual Studio扩展提供智能缩进的单元测试(缩进很重要,但行尾不重要) :-) - Cameron

3
假设:
  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)


1
让我们看看,+1 是因为花时间提供了一个好答案,+1 是因为你在第一部分检查引用相等和 null,-1 是因为在字符串比较方法中使用了 Linq 过度。总体来说 +1 :) - Diadistis
我主要是将lambda放在那里,因为它使代码更紧凑,而且它是一个相当直观的表达式,可以快速执行。老派的方法是添加一个帮助函数,该函数返回不是换行符的字符。真正老派的(.NET 1.1)方法要么有一个帮助类,要么在迭代中变得更加复杂。通常情况下,在字符串比较方法中很难过度,因为字符串比较可能非常复杂(考虑ß在无大小写比较中匹配SS的常见情况)。 - Jon Hanna

3
这是一个字符串相等比较器,会忽略某些字符,例如\r\n
这个实现在执行期间不分配任何堆内存,有助于提高性能。它还通过IEnumerableIEnumerator避免了虚拟调用。
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();
    }
}

2
更为简洁的方法是使用:
string1.Replace(Environment.NewLine, String.Empty) != string2.Replace(Environment.NewLine, String.Empty);

我知道这已经过去将近十年了,但这仍然是我的逻辑答案...但由于某种原因,我的单元测试返回了相同的exception.tostring()调用的两种不同样式..一个有\r\n,另一个只有\n!不知道为什么,但这真是一件让人头疼的事情。然而,我还是想评论一下,提醒其他人这可能不如希望的那样彻底。 - Chris Watts

1
这是Jon Hannas答案的通用且经过测试的版本。
/// <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','')

@Paul Creasey:你比我快了! - barrylloyd

0

在编写需要将多行字符串与实际输出字符串进行比较的单元测试时,我遇到了这个问题多次。

例如,如果我正在编写一个输出多行字符串的方法,我关心每一行的外观,但我不关心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

我在单元测试中遇到了行尾问题。

//compare files ignoring line ends
    org.junit.Assert.assertEquals(
            read.readPayload("myFile.xml")
                    .replace("\n", "")
                    .replace("\r", ""),
            values.getFile()
                    .replace("\n", "")
                    .replace("\r", ""));

我通常不喜欢进行这种比较(比较整个文件),更好的方法是验证字段。但它回答了这里的问题,因为它会删除大多数系统的行尾(replace调用就是关键)。

PS:read.readPayload从资源文件夹中读取文本文件并将其放入字符串中,values是一个包含文件原始内容(作为字符串)的字符串的结构。

PS2:由于这只是对单元测试的丑陋修复,因此未考虑性能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接