C#中的双字节字符串比较

时间:2010-11-10 08:10:51

标签: c# .net unicode string-comparison

我有两个字符串,一个是双字节值,另一个是单字节一个。字符串比较结果返回false,如何在忽略单字节/双字节差异后正确比较它们?

string s1 = "smatsumoto11"
string s2 = "smatsumoto11"

在同一场景中,如果SQL Server中的nvarchar列包含值smatsumoto11,则获取具有字符串smatsumoto11的where条件的数据的查询将返回同一行。我需要与C#字符串比较类似的语义。

我尝试过在MSDN上提到的一些选项,但它们似乎不起作用。

有什么想法吗?

4 个答案:

答案 0 :(得分:6)

您的s1包含所谓的“全宽”字符,因此您可以使用string.Compare并告诉它忽略字符宽度:

string.Compare(s1, s2, CultureInfo.CurrentCulture, CompareOptions.IgnoreWidth);

(当然,如有必要,请指定其他CultureInfo。)

答案 1 :(得分:3)

在进行比较之前,您可以尝试“Normalize”您的字符串:

  

返回一个新字符串,其文本值与此字符串相同,但其二进制表示形式为指定的Unicode规范化形式。

     

某些Unicode字符具有多个等效的二进制表示形式,这些表示形式由组合和/或复合Unicode字符组成。单个字符的多个表示的存在使搜索,排序,匹配和其他操作复杂化。

答案 2 :(得分:1)

我的机器说s1在MS Mincho中。

  MS Mincho(MS明朝) - 与日语版的Windows 3.1或更高版本,某些版本的Internet Explorer 3日语字体包,Windows XP中的所有区域,Microsoft Office v.X到2004年一起发布。

Arnout的回答完全废除了以下内容。

<击> 我知道技巧就像iconv中的//TRANSLIT一样,而且似乎在这里工作。

        string s1 = "smatsumoto11";
        string s2 = "smatsumoto11";

        string conv = Encoding.ASCII.GetString(Encoding.GetEncoding("Cyrillic").GetBytes(s1));

        if (conv == s2) Console.WriteLine("They are the same!");

有一天,我真的要试着找出它的工作原理......

答案 3 :(得分:1)

虽然accepted answer有效,并且关于主要问题是“广泛”字符是正确的,但在问题中存在一些应该解决的误解和技术问题,以便更好地理解在.NET和SQL Server中真的都在这里。

<强>首先

  

我有两个字符串,一个是双字节值,另一个是单字节一个。

不,你没有。你有两个Unicode字符串,编码为UTF-16 Little Endian(这是所有Windows和.NET的工作方式)。而实际上,大多数时候字符是双字节的,只包括62,000 - 63,000(左右)字符(即U + 0000和U + FFFF之间的代码点 - 或0 - 65,535 - 是“有效”的字符)。但Unicode允许映射超过110万个代码点,目前只有超过260,000个代码点already mapped。 U + FFFF / 65,535之上的代码点(称为补充字符)被映射到称为代理对的两个双字节值的集合。因此,虽然它们使用频率较低,但大多数Unicode代码点实际上都是4个字节。

<强>第二

  

字符串比较结果返回false,如何让它们正确比较

s1 = "smatsumoto11"中的字母称为“全宽”字符。您可以在此处查看完整列表:

http://unicode.org/cldr/utility/list-unicodeset.jsp?a=[:East_Asian_Width=Fullwidth:]

关于为什么首先有不同宽度的一些解释可以在这里找到:

http://unicode-table.com/en/blocks/halfwidth-and-fullwidth-forms/

如果你想比较问题中的两个字符串,使它们相等,你可以使用@ Arnout的答案中提到的String.Compare(String, String, CultureInfo, CompareOptions)方法,或者你可以使用CompareInfo.Compare(String, String, CompareOptions),如下所示:

CompareInfo.Compare(s1, s2, CompareOptions.IgnoreWidth)

<强>第三

  

在同一场景中,如果SQL Server中的nvarchar列包含值smatsumoto11,则使用字符串smatsumoto11的where条件获取数据的查询将返回相同的行。

这是一种考虑字符串比较的潜在危险方式。除非字符串是7位ASCII(值0 - 127),甚至不包括代码页,否则没有特别的方法可以在几乎任何数据库中进行字符串比较,我不知道这是否是一个选项。比较基于特定的LCID /区域/文化/整理。 SQL Server中的默认排序规则(至少在美国)是SQL_Latin1_General_CP1_CI_AS,它是Case Insensitive和Accent Sensitive。它还使用Code Page 1252(影响CHAR / VARCHAR数据,而不是NCHAR / NVARCHAR)和“en-US”文化。其他文化/ LCID的整理可能不等于全宽和“半宽”。并且,名称中_WS的排序肯定不会将这两个字符串等同,因为_WS代表“Width Sensitive”,如果您没有指定{{{}},则这是.NET比较的默认值。 1}}选项。

如果您运行以下查询以查找名称中包含CompareOptions.IgnoreWidth的排序规则,您会发现3885个匹配的排序规则中有1776个是宽度敏感且匹配这两个字符串(至少在SQL Server 2012中)。当然,还有262个二进制排序规则(即以不推荐使用的_WS或首选_BIN结尾的名称)也不会将这些字符串等同起来,但这不是宽度敏感度的问题。

_BIN2

另外,正如我刚才提到的,SELECT * FROM sys.fn_helpcollations() WHERE [name] LIKE N'%[_]WS%' ORDER BY [name]; -- 1776 out of 3885 on SQL Server 2012 的不幸(并且已弃用)默认排序规则,甚至更好的SQL_Latin1_General_CP1_CI_AS版本都是Case INsensitive。因此,您比较的字符串都是小写的,因此它们在使用Latin1_General_100_CI_AS时相同,但如果您想在SQL Server中模拟这些特定的Collat​​ions,那么.NET的默认行为将是区分大小写的与SQL Server行为不匹配。为了更好地匹配SQL Server行为(至少对于那些Collat​​ions,或任何标记为CompareOptions.IgnoreWidth_CI的行为),您还需要包含{{1选项如下:

_WS

其他资源:

Comparing Strings in the .NET Framework

Best Practices for Using Strings in the .NET Framework