我有两个字符串,一个是双字节值,另一个是单字节一个。字符串比较结果返回false,如何在忽略单字节/双字节差异后正确比较它们?
string s1 = "smatsumoto11"
string s2 = "smatsumoto11"
在同一场景中,如果SQL Server中的nvarchar列包含值smatsumoto11
,则获取具有字符串smatsumoto11
的where条件的数据的查询将返回同一行。我需要与C#字符串比较类似的语义。
我尝试过在MSDN上提到的一些选项,但它们似乎不起作用。
有什么想法吗?
答案 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中模拟这些特定的Collations,那么.NET的默认行为将是区分大小写的与SQL Server行为不匹配。为了更好地匹配SQL Server行为(至少对于那些Collations,或任何标记为CompareOptions.IgnoreWidth
和不有_CI
的行为),您还需要包含{{1选项如下:
_WS
其他资源: