2014-09-12 158 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