C#字符串比较结果为false。

8
我有一个字符串比较问题,大部分情况下表现正常,但由于我的代码无法将字符串对检测为重复项,导致我有很多重复的DB插入。
我认为这是一个文化差异问题(西里尔字母),我已经解决了它,但现在我得到了“假阴性”(两个明显相等的字符串显示为不相等)。
我已经查看了以下类似的问题,并尝试了以下的比较方法。
我查阅了类似的SO问题:

以下是被比较的字符串示例:(标题和描述)

feed title: 艾尔斯伯格:他是英雄

feed desc: 丹尼尔·艾尔斯伯格告诉CNN的唐·莱蒙,NSA泄密者爱德华·斯诺登表现出了勇气,做出了巨大贡献。

db title: 艾尔斯伯格:他是英雄

db desc: 丹尼尔·艾尔斯伯格告诉CNN的唐·莱蒙,NSA泄密者爱德华·斯诺登表现出了勇气,做出了巨大贡献。

我的应用程序将从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://dev59.com/H2445IYBdhLWcg3wustT#4719009

更新2: 经过6次比较仍然没有匹配,我意识到必须有另一个因素扭曲了结果,所以我尝试了以下操作。

//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-ß依赖性分子和功能特征签名"

仔细检查后发现,德语“eszett”字符在数据库中与来自源的字符不同:β vs ß

我本来期望至少有一个比较1-6能够捕捉到这一点...

有趣的是,在一些性能比较之后,正则表达式选项并不是七个选项中最慢的。 Normalize 显然比正则表达式更加密集!以下是当 thisFeedArticles 对象包含12077个项目时的所有七个计时器持续时间。

已过时间: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


2
如果这些字符串来自数据库,了解数据库列的声明方式和使用的排序规则可能是相关的。 - Kris Vandermotten
1
还要检查比较字符串的长度,可能它们包含一些不可见字符。 - Charlie
3
您也可以考虑对字符串进行规范化处理,参见http://msdn.microsoft.com/zh-cn/library/System.String.Normalize.aspx。 - Kris Vandermotten
1
为了确定隐藏字符是否是您的问题,您可以尝试另一种比较方法,该方法对字符串进行字节级比较(使用string.ToCharArray()、Encoding.XXX.GetBytes()或类似方法)。 - Rudism
1
这并不是批评,如果让你有这种感觉我很抱歉。但我注意到目前还没有人回答你的问题。你的问题涉及多个技术,有2个额外的编辑、3个代码部分和2个输入部分,对我来说信息太多了。我认为,如果你将问题表述为“我有这个字符串输入,我想要这个结果。这是我尝试过的,但没有解决它”,省略掉例如输入来源等部分,你可能会得到更多的答案。其他评论也更多地涉及数据库,并试图更好地隔离你的问题。 - default
显示剩余8条评论
1个回答

5

不幸的是,在我的情况下,这并没有证明是一个可靠的选择。请参见上面的更新2。无论如何感谢大家。 - Adam Hey
经过更多测试,似乎您的第一条评论可能是我问题的正确解决方案。在SSMS中,我运行了一个包含“α”(alpha)的查询,输出结果不包含“α”,而是用“a”代替。相关列为varchar类型,将它们更改为nvarchar类型可能是必要的,但这是我一直担心的事情(10M+行...)。开发数据库和生产数据库之间的排序规则也不同,这并没有帮助测试/调试。 - Adam Hey
@AdamHey 显然,特别是当你处理西里尔字母时。这是我的第一个想法。但既然你没有反应,我就假设你已经解决了这个问题。 - Kris Vandermotten

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