【问题标题】:C# String Comparison equates to falseC# 字符串比较等于 false
【发布时间】:2014-09-12 13:15:37
【问题描述】:

我有一个字符串比较问题 - 在大多数情况下 - 行为符合预期,但由于我的代码没有将字符串对检测为重复,因此我留下了大量的重复数据库插入。

我以为我已将其范围缩小到文化问题(西里尔字符),我解决了,但我现在得到“假阴性”(两个明显相等的字符串显示为不相等)。

我查看了以下类似问题并尝试了以下比较方法。

我检查过的类似 SO 问题:

这是一个比较字符串的例子:(标题和描述)

Feed 标题: Ellsberg:他是英雄

feed desc: Daniel Ellsberg 告诉 CNN 的 Don Lemon,NSA 泄密者 Edward Snowden 表现出了勇气,做出了巨大的贡献。

数据库标题: Ellsberg:他是英雄

db desc: Daniel Ellsberg 告诉 CNN 的 Don Lemon,NSA 泄密者 Edward Snowden 表现出了勇气, 做了一个巨大的服务。

我的应用将从 RSS 提要中获取的值与我在数据库中的值进行比较,并且应该只插入“新”值。

//fetch existing articles from DB for the current feed:
    List<Article> thisFeedArticles = (from ar in entities.Items
                                      where (ar.ItemTypeId == (int)Enums.ItemType.Article) && ar.ParentId == feed.FeedId
                                      && ar.DatePublished > datelimit
                                      select new Article
                                      {
                                           Title = ar.Title, 
                                           Description = ar.Blurb
                                      }).ToList();

以下所有比较都显示与 Ellsberg 标题/描述不匹配。即matches1到matches6都有Count()==0

(请原谅列举的变量名——它们只是为了测试)

   // comparison methods 
CompareOptions compareOptions = CompareOptions.OrdinalIgnoreCase;
CompareOptions compareOptions2 = CompareOptions.IgnoreSymbols | CompareOptions.IgnoreNonSpace;
//1
IEnumerable<Article> matches = thisFeedArticles.Where(b =>
    String.Compare(b.Title.Trim().Normalize(), a.Title.Trim().Normalize(), CultureInfo.InvariantCulture, compareOptions) == 0 &&
    String.Compare(b.Description.Trim().Normalize(), a.Description.Trim().Normalize(), CultureInfo.InvariantCulture, compareOptions) == 0
    );

//2
IEnumerable<Article> matches2 = thisFeedArticles.Where(b =>
    String.Compare(b.Title, a.Title, CultureInfo.CurrentCulture, compareOptions2) == 0 &&
    String.Compare(b.Description, a.Description, CultureInfo.CurrentCulture, compareOptions2) == 0
    );

//3
IEnumerable<Article> matches3 = thisFeedArticles.Where(b =>
    String.Compare(b.Title, a.Title, StringComparison.OrdinalIgnoreCase) == 0 &&
    String.Compare(b.Description, a.Description, StringComparison.OrdinalIgnoreCase) == 0
    );

//4
IEnumerable<Article> matches4 = thisFeedArticles.Where(b =>
    b.Title.Equals(a.Title, StringComparison.OrdinalIgnoreCase) &&
    b.Description.Equals(a.Description, StringComparison.OrdinalIgnoreCase)
    );

//5
IEnumerable<Article> matches5 = thisFeedArticles.Where(b =>
    b.Title.Trim().Equals(a.Title.Trim(), StringComparison.InvariantCultureIgnoreCase) &&
    b.Description.Trim().Equals(a.Description.Trim(), StringComparison.InvariantCultureIgnoreCase)
    );

//6
IEnumerable<Article> matches6 = thisFeedArticles.Where(b =>
    b.Title.Trim().Normalize().Equals(a.Title.Trim().Normalize(), StringComparison.OrdinalIgnoreCase) &&
    b.Description.Trim().Normalize().Equals(a.Description.Trim().Normalize(), StringComparison.OrdinalIgnoreCase)
    );


    if (matches.Count() == 0 && matches2.Count() == 0 && matches3.Count() == 0 && matches4.Count() == 0 && matches5.Count() == 0 && matches6.Count() == 0 && matches7.Count() == 0)
    {
    //insert values
    }

    //this if statement was the first approach
    //if (!thisFeedArticles.Any(b => b.Title == a.Title && b.Description == a.Description)
    // {
    // insert
    // }

显然,我一次只使用上述选项之一。

在大多数情况下,上述选项确实有效,并且检测到了大多数重复项,但仍有重复项从裂缝中溜走——我只需要了解“裂缝”是什么,因此欢迎提出任何建议。

我什至尝试将字符串转换为字节数组并进行比较(不久前删除了该代码,抱歉)。

Article 对象如下:

    public class Article
    {
        public string Title;
        public string Description;
    }

更新:

我已经尝试对字符串进行规范化以及包含IgnoreSymbols CompareOption,但我仍然得到一个假阴性(不匹配)。不过,我注意到的是,撇号似乎在错误的不匹配中始终出现;所以我认为这可能是撇号与单引号的情况,即 ' vs ' (等等),但肯定 IgnoreSymbols 应该避免这种情况?

我发现了几个类似的 SO 帖子: C# string comparison ignoring spaces, carriage return or line breaks String comparison: InvariantCultureIgnoreCase vs OrdinalIgnoreCase? 下一步:尝试使用正则表达式按照这个答案去除空格:https://stackoverflow.com/a/4719009/2261245

更新 2 在 6 次比较 STILL 没有返回匹配项后,我意识到肯定有另一个因素会影响结果,所以我尝试了以下方法

//7
IEnumerable<Article> matches7 = thisFeedArticles.Where(b =>
    Regex.Replace(b.Title, "[^0-9a-zA-Z]+", "").Equals(Regex.Replace(a.Title, "[^0-9a-zA-Z]+", ""), StringComparison.InvariantCultureIgnoreCase) &&
    Regex.Replace(b.Description, "[^0-9a-zA-Z]+", "").Equals(Regex.Replace(a.Description, "[^0-9a-zA-Z]+", ""), StringComparison.InvariantCultureIgnoreCase)
    );

这确实找到了其他人错过的匹配项!

下面的字符串通过了所有 6 次比较,但不是第 7 次:

a.Title.Trim().Normalize()a.Title.Trim() 都返回:

“勘误:鉴定一种独特的 TGF-β 依赖性分子和 小胶质细胞的功能特征"

数据库中的值为:

“勘误:鉴定一种独特的 TGF-β 依赖性分子和 小胶质细胞的功能特征"

仔细检查表明,与来自提要的内容相比,DB 中的德语“eszett”字符不同:β 与 ß

我本希望至少有一个比较 1-6 能发现这一点......

有趣的是,经过一些性能比较,Regex 选项绝不是七个选项中最慢的。 Normalize 似乎比正则表达式更密集! 以下是 thisFeedArticles 对象包含 12077 个项目时所有七个的 Stopwatch 持续时间

经过的时间:00:00:00.0000662
经过的时间:00:00:00.0000009
经过的时间:00:00:00.0000009
经过的时间:00:00:00.0000009
经过的时间:00:00:00.0000009
经过的时间:00:00:00.0000009
经过时间:00:00:00.0000016

【问题讨论】:

  • 如果这些字符串来自数据库,则可能需要了解数据库列的声明方式以及使用的排序规则。
  • “但我现在得到的‘误报’(两个明显相等的字符串)显示为不相等。”这不是“假阴性”吗?
  • 还要检查比较字符串的长度,可能它们包含一些不可见的字符
  • 你也可以考虑规范化字符串,见msdn.microsoft.com/en-us/library/System.String.Normalize.aspx
  • 是的,我同意@MattBurland。另外,您可以发布您遇到问题的实际字符串吗?如果您将它们复制粘贴到文字中,比较仍然会失败吗?数据中是否有隐藏字符?

标签: c# .net string-comparison


【解决方案1】:

Unicode 字符串可以是“二进制”不同的,即使它们“语义上”相同。

尝试规范化你的字符串。欲了解更多信息,请参阅http://msdn.microsoft.com/en-us/library/System.String.Normalize.aspx

【讨论】:

  • 不幸的是 - 在我的情况下 - 这并没有被证明是一个可靠的选择。请参阅上面的更新 2。一样的感谢
  • 经过更多测试,看来您的第一条评论可能是解决我问题的正确方法。在 SSMS 中,我运行了一个包含“α”(alpha)的查询,输出不包含“α”,而是在其位置包含“a”。有问题的列是 varchar,将它们更改为 nvarchar 可能是必要的,但我很害怕(10M+ 行......) dev db 和 prod db 之间的排序规则也不同,这无济于事测试/调试。
  • @AdamHey 很明显,尤其是在处理西里尔字符时。那是我的第一个预感。但既然你没有反应,我以为你已经知道了。
猜你喜欢
  • 1970-01-01
  • 2011-01-08
  • 2017-11-23
  • 1970-01-01
  • 2016-08-10
  • 2021-11-19
  • 2016-02-08
  • 2013-11-22
  • 1970-01-01
相关资源
最近更新 更多