2014-09-12 156 views
7

我有一个字符串比较问题 - 大多数情况下 - 按预期方式运行,但由于我的代码没有将字符串对检测为重复,所以留下大量f重复数据库插入。我认为我已经把它缩小到一个文化问题(西里尔文字符),我解决了这个问题,但现在我得到了'假阴性'(两个显然相同的字符串显示为不等于)。C#字符串比较相当于假

我看过以下类似的问题,并尝试了以下比较方法。我已经检查

相似,所以问题:

下面是一个字符串的例子进行比较:(标题和描述)

提要标题:埃尔斯伯格:他是一个英雄

饲料递减:丹尼尔·埃尔斯伯格告诉CNN的唐柠檬国家安全局泄密者斯诺登表现出的勇气,做了一个巨大的服务。

分贝标题:埃尔斯伯格:他是一个英雄

分贝说明:丹尼尔·埃尔斯伯格告诉CNN的唐柠檬国家安全局泄密者斯诺登表现出的勇气,有 做了一个巨大的服务。

我的应用程序将从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标题/描述不匹配。

// 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 
    // } 

很显然,我只被使用一次以上选项中的一个 - 即matches1到matches6都有Count()==0

(它们只是测试请原谅枚举变量名)。

大多数情况下,上述选项可以正常工作,并且大部分重复都可以被检测到,但是仍然有重复滑动通过裂缝 - 我只需要了解“裂缝”是什么,所以任何建议都会受到欢迎。

我甚至尝试将字符串转换为字节数组并进行比较(前一段代码已删除,对不起)。

Article对象如下:

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

UPDATE:

我已经试过规范化字符串以及包括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

UPDATE 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-β依赖的分子和 功能鉴定在小胶质细胞”签名

在DB值是:

仔细检查表明,相对于什么是通过未来的德国“eszett”字符是在数据库中不同的“勘误小胶质细胞中一种独特的TGF-β依赖的分子和 函数签名的鉴定”从进料:βVS SS

我本来期望比较1-6的至少一个来挑选起来......

有趣的是,经过一番性能比较,正则表达式的选择绝不是最慢七个。 Normalize似乎比正则表达式更强烈! 下面是Stopwatch持续时间为所有七时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

如果这些字符串来自数据库,则可能与知道数据库列的声明方式以及使用的排序顺序有关。 – 2014-09-12 13:19:39

+1

“但我现在正在'误报'(两个显然相同的字符串)显示为不等于”。这不是一个'假阴性'吗? – 2014-09-12 13:20:13

+1

还检查比较字符串的长度,可能它们包含一些不可见字符 – Charlie 2014-09-12 13:20:45

回答

4

Unicode字符串可以是 “二进制” 不同,即使是 “语义” 一样。

尝试规范化您的字符串。有关更多信息,请参见http://msdn.microsoft.com/en-us/library/System.String.Normalize.aspx

+0

不幸的是 - 在我的情况 - 这并不证明是一个可靠的选择。见上面的更新2。非常感谢 – 2014-09-17 09:59:46

+0

经过更多测试后,您的第一条评论可能是我的问题的正确解决方案。在SSMS中,我运行了一个查询,其中包含一个“α”(alpha),输出中不包含“α”,但是在它的位置有一个“a”。有问题的列是varchar,并将它们更改为nvarchar是可能需要的东西,但是我正在害怕的事情(10M +行...) 排序规则在dev db和prod db之间也不同,与测试/调试。 – 2014-09-17 12:57:10

+0

@AdamHey显然,特别是如果你正在处理西里尔字符。那是我第一次预感。但既然你没有反应,我认为你已经覆盖了。 – 2014-09-17 13:24:06