我在比较两个字符串的更新。我执行了:
string1 != string2
它们最终会变得不同。我将它们放在“添加监视器”中,我发现唯一的区别是一个有换行符,而另一个没有。
string1 = "This is a test. \nThis is a test";
string2 = "This is a test. This is a test";
我基本上想进行比较,但不包括换行符。因此,如果换行符是唯一的区别,则将它们视为相等。
我在比较两个字符串的更新。我执行了:
string1 != string2
它们最终会变得不同。我将它们放在“添加监视器”中,我发现唯一的区别是一个有换行符,而另一个没有。
string1 = "This is a test. \nThis is a test";
string2 = "This is a test. This is a test";
我基本上想进行比较,但不包括换行符。因此,如果换行符是唯一的区别,则将它们视为相等。
如果性能不是很重要的话,一种快速而简单的方法是:
string1.Replace("\n", "") != string2.Replace("\n", "")
\r
字符。 - Drew Noakes我建议使用正则表达式将每个 空格
、制表符
、\r
、\n
缩减为一个空格:
Regex.Replace(string1, @"\s+", " ") != Regex.Replace(string2, @"\s+", " ")
"\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)
\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();
}
}
string1.Replace(Environment.NewLine, String.Empty) != string2.Replace(Environment.NewLine, String.Empty);
exception.tostring()
调用的两种不同样式..一个有\r\n,另一个只有\n!不知道为什么,但这真是一件让人头疼的事情。然而,我还是想评论一下,提醒其他人这可能不如希望的那样彻底。 - Chris Watts/// <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.
}
string1.replace('\n','') != string2.replace('\n','')
string1.replace('\n','') != string2.replace('\n','')
在编写需要将多行字符串与实际输出字符串进行比较的单元测试时,我遇到了这个问题多次。
例如,如果我正在编写一个输出多行字符串的方法,我关心每一行的外观,但我不关心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;
}
最让我惊讶的是,我使用过的任何单元测试框架中都没有包含这样的方法。
我在单元测试中遇到了行尾问题。
//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:由于这只是对单元测试的丑陋修复,因此未考虑性能。