对 List<String> 进行排序时的意外行为
Unexpected behaviour when sorting a List<String>
如果我尝试按以下方式对字符串列表进行排序:
List<String> lstStrings = new List<string>();
String s1 = "KÜHLSCHRANK";
String s2 = "KUHLSCHRANK";
int i = s1.CompareTo(s2); // returns 1
int j = s2.CompareTo(s1); // return -1
i = StringComparer.InvariantCulture.Compare(s1, s2); // returns 1
j = StringComparer.InvariantCulture.Compare(s2, s1); // returns -1
lstStrings.Add("KÜHLSCHRANK1");
lstStrings.Add("KUTTER");
lstStrings.Add("KUHLSCHRANK2");
lstStrings.Add("KÜHLSCHRANK3");
var lstStrings1 = lstStrings.OrderBy(y => y).ToList();
var lstStrings2 = lstStrings.OrderBy(y => y, StringComparer.InvariantCulture).ToList();
var lstStrings3 = lstStrings.OrderBy(y => y, StringComparer.CurrentCulture).ToList();
var lstStrings4 = lstStrings.OrderBy(y => y, StringComparer.Ordinal).ToList();
我在 lstStrings1、lstStrings2 和 lstStrings3:[=20 中得到以下结果=]
[0] "KÜHLSCHRANK1"
[1] "KUHLSCHRANK2"
[2] "KÜHLSCHRANK3"
[3] "KUTTER"
只有我的 lstStrings4 显示了我预期的结果:
[0] "KUHLSCHRANK2"
[1] "KUTTER"
[2] "KÜHLSCHRANK1"
[3] "KÜHLSCHRANK3"
谁能解释为什么默认情况下德语“Ü”像普通 'U' 一样穿线?
为什么使用 StringComparer.InvariantCulture 的 OrderBy 不关心 StringComparer.InvariantCulture.Compare(s1, s2)
的结果(这意味着列表是有序的,就像我在上一个示例中的 lstStrings4)?
有什么办法可以改变这个"default behaviour"吗?
添加:
如果我将数字附加到字符串,比较方法更改的结果:
String s1 = "KÜHLSCHRANK1";
String s2 = "KUHLSCHRANK2";
int i = s1.CompareTo(s2); // returns -1
int j = s2.CompareTo(s1); // return 1
i = StringComparer.InvariantCulture.Compare(s1, s2); // returns -1
j = StringComparer.InvariantCulture.Compare(s2, s1); // returns 1
所以我什至不明白,为什么我的第一个没有数字的测试在每次比较时都没有 return 零...
第二次添加:
在 SQL 服务器上:
DECLARE @tableDE TABLE (strName NVARCHAR(MAX) COLLATE German_PhoneBook_CI_AI)
INSERT INTO @tableDE (strName)
SELECT e FROM (VALUES('KUHLSCHRANK1'), ('KÜHLSCHRANK2')) f(e)
SELECT * FROM @tableDE ORDER BY strName
给出结果:
KÜHLSCHRANK2
KUHLSCHRANK1
结果:
如果我在 LinqToSql 中执行 OrderBy
并将结果放入列表中,
List 变量上的新 OrderBy
,即使具有相同的参数,也会改变元素的顺序。
我可以告诉你为什么序数给你的结果是 "expected"。根据 StringComparer.Ordinal
的文档
The StringComparer returned by the Ordinal property performs a simple byte comparison that is independent of language. This is most appropriate when comparing strings that are generated programmatically or when comparing case-sensitive resources such as passwords.
所以 U
是 0x55
而 Ü
是 0x220
。因此,这会将您的不同 U 排序出来。但这有一个问题,假设您添加了单词 KËTTER
(可能不是真正的德语单词,但它是为了演示目的)。您的列表将按如下方式组织:
[0] "KUHLSCHRANK2"
[1] "KUTTER"
[2] "KËTTER"
[3] "KÜHLSCHRANK1"
[4] "KÜHLSCHRANK3"
如您所见,Ë
位于两个不同的 U 之间,这是因为 Ë
具有 0x203
的 unicode,而 55<203<220
表示 U<Ë<Ü
。
因此,如果您的目标是按字母排序,然后按字母的重音排序,我建议不要使用 Ordinal。
现在,我不能留下评论,但你确定要按字母组织然后按字母的重音排序。我还没有看到字典区分重音字母和正常对应字母,这可能就是为什么依赖于文化的排序没有给出您需要的结果。
添加:
我在您的测试中添加了更多案例,所以现在未排序的完整列表看起来有点像这样
[0] "KÜHLSCHRANK1"
[1] "KUHLSCHRANK1"
[2] "KUTTER"
[3] "KUHLSCHRANK2"
[4] "KÜHLSCHRANK2"
[5] "KÜHLSCHRANK3"
[6] "KËTTER"
invariantCulture 和 current Culture 产生相同的结果,即:
[0] "KËTTER"
[1] "KUHLSCHRANK1"
[2] "KÜHLSCHRANK1"
[3] "KUHLSCHRANK2"
[4] "KÜHLSCHRANK2"
[5] "KÜHLSCHRANK3"
[6] "KUTTER"
所以这表明只有在完全匹配(忽略重音)时重音才会起作用。无重音优先。
第二次添加:
根据维基百科
Ü, or ü, is a character that typically represents a close front rounded vowel [y]. It is classified as a separate letter in several extended Latin alphabets (including Azeri, Estonian, Hungarian and Turkish), but as the letter U with an umlaut/diaeresis in others such as Catalan, French, Galician, German, Occitan and Spanish.
所以在德语中,元音变音不是一个单独的字母,而只是一个重音,如果你要使用土耳其文化,它将被视为一个单独的字母。
所以文化是土耳其语的结果是:
[0] "KËTTER"
[1] "KUHLSCHRANK1"
[2] "KUHLSCHRANK2"
[3] "KUTTER"
[4] "KÜHLSCHRANK1"
[5] "KÜHLSCHRANK2"
[6] "KÜHLSCHRANK3"
我相信这会产生您想要的结果。只是这对您的语言来说是错误的文化。
评论回复:
正如您所指出的,phone 这本书确实按照您的需要进行组织,经过一些挖掘,.net 为德语使用了两种排序算法。 Documentation
当使用 phone 图书排序算法时,它会产生结果:
[0] "KËTTER"
[1] "KÜHLSCHRANK1"
[2] "KÜHLSCHRANK2"
[3] "KÜHLSCHRANK3"
[4] "KUHLSCHRANK1"
[5] "KUHLSCHRANK2"
[6] "KUTTER"
为了使用 phone图书排序算法,请使用以下内容:
var germanPhone=new CultureInfo(0x00010407);
StringComparer germanPhoneICComp = StringComparer.Create(germanPhone, true);\set to false if caps are important to you
var lstStrings7 = lstStrings.OrderBy(y => y, germanPhoneICComp).ToList();
如果我尝试按以下方式对字符串列表进行排序:
List<String> lstStrings = new List<string>();
String s1 = "KÜHLSCHRANK";
String s2 = "KUHLSCHRANK";
int i = s1.CompareTo(s2); // returns 1
int j = s2.CompareTo(s1); // return -1
i = StringComparer.InvariantCulture.Compare(s1, s2); // returns 1
j = StringComparer.InvariantCulture.Compare(s2, s1); // returns -1
lstStrings.Add("KÜHLSCHRANK1");
lstStrings.Add("KUTTER");
lstStrings.Add("KUHLSCHRANK2");
lstStrings.Add("KÜHLSCHRANK3");
var lstStrings1 = lstStrings.OrderBy(y => y).ToList();
var lstStrings2 = lstStrings.OrderBy(y => y, StringComparer.InvariantCulture).ToList();
var lstStrings3 = lstStrings.OrderBy(y => y, StringComparer.CurrentCulture).ToList();
var lstStrings4 = lstStrings.OrderBy(y => y, StringComparer.Ordinal).ToList();
我在 lstStrings1、lstStrings2 和 lstStrings3:[=20 中得到以下结果=]
[0] "KÜHLSCHRANK1"
[1] "KUHLSCHRANK2"
[2] "KÜHLSCHRANK3"
[3] "KUTTER"
只有我的 lstStrings4 显示了我预期的结果:
[0] "KUHLSCHRANK2"
[1] "KUTTER"
[2] "KÜHLSCHRANK1"
[3] "KÜHLSCHRANK3"
谁能解释为什么默认情况下德语“Ü”像普通 'U' 一样穿线?
为什么使用 StringComparer.InvariantCulture 的 OrderBy 不关心 StringComparer.InvariantCulture.Compare(s1, s2)
的结果(这意味着列表是有序的,就像我在上一个示例中的 lstStrings4)?
有什么办法可以改变这个"default behaviour"吗?
添加: 如果我将数字附加到字符串,比较方法更改的结果:
String s1 = "KÜHLSCHRANK1";
String s2 = "KUHLSCHRANK2";
int i = s1.CompareTo(s2); // returns -1
int j = s2.CompareTo(s1); // return 1
i = StringComparer.InvariantCulture.Compare(s1, s2); // returns -1
j = StringComparer.InvariantCulture.Compare(s2, s1); // returns 1
所以我什至不明白,为什么我的第一个没有数字的测试在每次比较时都没有 return 零...
第二次添加: 在 SQL 服务器上:
DECLARE @tableDE TABLE (strName NVARCHAR(MAX) COLLATE German_PhoneBook_CI_AI)
INSERT INTO @tableDE (strName)
SELECT e FROM (VALUES('KUHLSCHRANK1'), ('KÜHLSCHRANK2')) f(e)
SELECT * FROM @tableDE ORDER BY strName
给出结果:
KÜHLSCHRANK2
KUHLSCHRANK1
结果:
如果我在 LinqToSql 中执行 OrderBy
并将结果放入列表中,
List 变量上的新 OrderBy
,即使具有相同的参数,也会改变元素的顺序。
我可以告诉你为什么序数给你的结果是 "expected"。根据 StringComparer.Ordinal
The StringComparer returned by the Ordinal property performs a simple byte comparison that is independent of language. This is most appropriate when comparing strings that are generated programmatically or when comparing case-sensitive resources such as passwords.
所以 U
是 0x55
而 Ü
是 0x220
。因此,这会将您的不同 U 排序出来。但这有一个问题,假设您添加了单词 KËTTER
(可能不是真正的德语单词,但它是为了演示目的)。您的列表将按如下方式组织:
[0] "KUHLSCHRANK2"
[1] "KUTTER"
[2] "KËTTER"
[3] "KÜHLSCHRANK1"
[4] "KÜHLSCHRANK3"
如您所见,Ë
位于两个不同的 U 之间,这是因为 Ë
具有 0x203
的 unicode,而 55<203<220
表示 U<Ë<Ü
。
因此,如果您的目标是按字母排序,然后按字母的重音排序,我建议不要使用 Ordinal。
现在,我不能留下评论,但你确定要按字母组织然后按字母的重音排序。我还没有看到字典区分重音字母和正常对应字母,这可能就是为什么依赖于文化的排序没有给出您需要的结果。
添加: 我在您的测试中添加了更多案例,所以现在未排序的完整列表看起来有点像这样
[0] "KÜHLSCHRANK1"
[1] "KUHLSCHRANK1"
[2] "KUTTER"
[3] "KUHLSCHRANK2"
[4] "KÜHLSCHRANK2"
[5] "KÜHLSCHRANK3"
[6] "KËTTER"
invariantCulture 和 current Culture 产生相同的结果,即:
[0] "KËTTER"
[1] "KUHLSCHRANK1"
[2] "KÜHLSCHRANK1"
[3] "KUHLSCHRANK2"
[4] "KÜHLSCHRANK2"
[5] "KÜHLSCHRANK3"
[6] "KUTTER"
所以这表明只有在完全匹配(忽略重音)时重音才会起作用。无重音优先。
第二次添加:
根据维基百科
Ü, or ü, is a character that typically represents a close front rounded vowel [y]. It is classified as a separate letter in several extended Latin alphabets (including Azeri, Estonian, Hungarian and Turkish), but as the letter U with an umlaut/diaeresis in others such as Catalan, French, Galician, German, Occitan and Spanish.
所以在德语中,元音变音不是一个单独的字母,而只是一个重音,如果你要使用土耳其文化,它将被视为一个单独的字母。 所以文化是土耳其语的结果是:
[0] "KËTTER"
[1] "KUHLSCHRANK1"
[2] "KUHLSCHRANK2"
[3] "KUTTER"
[4] "KÜHLSCHRANK1"
[5] "KÜHLSCHRANK2"
[6] "KÜHLSCHRANK3"
我相信这会产生您想要的结果。只是这对您的语言来说是错误的文化。
评论回复:
正如您所指出的,phone 这本书确实按照您的需要进行组织,经过一些挖掘,.net 为德语使用了两种排序算法。 Documentation 当使用 phone 图书排序算法时,它会产生结果:
[0] "KËTTER"
[1] "KÜHLSCHRANK1"
[2] "KÜHLSCHRANK2"
[3] "KÜHLSCHRANK3"
[4] "KUHLSCHRANK1"
[5] "KUHLSCHRANK2"
[6] "KUTTER"
为了使用 phone图书排序算法,请使用以下内容:
var germanPhone=new CultureInfo(0x00010407);
StringComparer germanPhoneICComp = StringComparer.Create(germanPhone, true);\set to false if caps are important to you
var lstStrings7 = lstStrings.OrderBy(y => y, germanPhoneICComp).ToList();