C#正确反转字符串
例子
大多数情况下,当人们必须反转字符串时,他们或多或少会这样做:
char[] a = s.ToCharArray(); System.Array.Reverse(a); string r = new string(a);
然而,这些人没有意识到的是,这实际上是错误的。
我的意思不是因为缺少NULL检查。
它实际上是错误的,因为Glyph/GraphemeCluster可以由多个代码点(又名字符)组成。
要了解为什么会这样,我们首先必须了解“字符”一词的实际含义这一事实。
参考:
性格是一个超载的术语,它可以意味着很多东西。
代码点是信息的原子单位。文本是一系列代码点。每个代码点都是一个数字,由Unicode标准赋予其含义。
字素是一个或多个代码点的序列,显示为单个图形单元,读者将其识别为书写系统的单个元素。例如,a和ä都是字素,但它们可能由多个代码点组成(例如ä可能是两个代码点,一个用于基本字符a后跟一个用于日记;但还有一种替代的遗留单一代码代表这个字素的点)。某些代码点从不属于任何字素(例如,零宽度非连接器或方向覆盖)。
字形是图像,通常存储在字体(字形的集合)中,用于表示字素或其部分。字体可以将多个字形组合成单个表示,例如,如果上述ä是单个代码点,则字体可能会选择将其呈现为两个独立的、空间重叠的字形。对于OTF,字体的GSUB和GPOS表包含替换和定位信息来完成这项工作。一个字体也可能包含同一个字素的多个替代字形。
所以在C#中,一个字符实际上是一个CodePoint。
这意味着,如果你只是反转一个像那样的有效字符串LesMisérables,它看起来像这样
string s = "Les Mise\u0301rables";
作为字符序列,您将获得:
selbaŕesiMseL
如您所见,重音位于R字符上,而不是e字符上。
虽然string.reverse.reverse会在两次反转char数组时产生原始字符串,但这种反转绝对不是原始字符串的反转。
您只需要反转每个GraphemeCluster。
因此,如果操作正确,您可以像这样反转字符串:
private static System.Collections.Generic.ListGraphemeClusters(string s) { System.Collections.Generic.List ls = new System.Collections.Generic.List (); System.Globalization.TextElementEnumerator enumerator = System.Globalization.StringInfo.GetTextElementEnumerator(s); while (enumerator.MoveNext()) { ls.Add((string)enumerator.Current); } return ls; } //这 private static string ReverseGraphemeClusters(string s) { if(string.IsNullOrEmpty(s) ||s.Length== 1) return s; System.Collections.Generic.List ls = GraphemeClusters(s); ls.Reverse(); return string.Join("", ls.ToArray()); } public static void TestMe() { string s = "Les Mise\u0301rables"; // s = "noël"; string r = ReverseGraphemeClusters(s); //这 would be wrong: //char[]a=s.ToCharArray(); //System.Array.Reverse(a); //字符串r=新字符串(a); System.Console.WriteLine(r); }
而且-哦,喜悦-你会意识到如果你这样做正确,它也适用于亚洲/南亚/东亚语言(以及法语/瑞典语/挪威语等)......