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。

这意味着,如果你只是反转一个像 那样的有效字符串Les Misérables,它看起来像这样

string s = "Les Mise\u0301rables";

作为字符序列,您将获得:

selbaŕesiM seL

如您所见,重音位于 R 字符上,而不是 e 字符上。
虽然 string.reverse.reverse 会在两次反转 char 数组时产生原始字符串,但这种反转绝对不是原始字符串的反转。

您只需要反转每个 GraphemeCluster。
因此,如果操作正确,您可以像这样反转字符串:

    private static System.Collections.Generic.List<string> GraphemeClusters(string s)
    {
        System.Collections.Generic.List<string> ls = new System.Collections.Generic.List<string>();

        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<string> 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);
    }

而且 - 哦,喜悦 - 你会意识到如果你这样做正确,它也适用于亚洲/南亚/东亚语言(以及法语/瑞典语/挪威语等)......