正確地反轉一個字串

大多數情況下,當人們不得不反轉一個字串時,他們會或多或少地這樣做:

char[] a = s.ToCharArray();
System.Array.Reverse(a);
string r = new string(a);

然而,這些人沒有意識到的是,這實際上是錯誤的。
我並不是因為缺少 NULL 檢查。

它實際上是錯誤的,因為 Glyph / GraphemeCluster 可以由幾個程式碼點(也就是字元)組成。

要知道為什麼會這樣,我們首先必須意識到角色一詞的實際含義。

參考:

角色是一個超載的術語,可能意味著許多事情。

程式碼點是資訊的原子單位。文字是一系列程式碼點。每個程式碼點都是一個由 Unicode 標準賦予的數字。

字素是一個或多個程式碼點的序列,它們顯示為單個圖形單元,讀者將其識別為書寫系統的單個元素。例如,a 和ä都是字形,但它們可能由多個程式碼點組成(例如,ä可能是兩個程式碼點,一個用於基本字元 a,後面跟一個用於 diaresis;但也有一個替代的,遺留的,單個程式碼代表這個字形的點)。某些程式碼點永遠不會是任何字形的一部分(例如,零寬度非聯結器或方向覆蓋)。

字形是一種影象,通常以字型(字形集合)儲存,用於表示字形或其部分。字型可以將多個字形組合成單個表示,例如,如果上面的ä是單個程式碼點,則字型可以選擇將其呈現為兩個單獨的,空間上重疊的字形。對於 OTF,字型的 GSUB 和 GPOS 表包含替換和定位資訊以使其工作。字型也可以包含同一字素的多個替代字形。

所以在 C#中,一個字元實際上是一個 CodePoint。

這意味著,如果你只是反轉像 Les Misérables 這樣的有效字串,它可能看起來像這樣

string s = "Les Mise\u0301rables";

作為一系列人物,你會得到:

selbaŕesiMseL

如你所見,重音在 R 字元上,而不是 e 字元。
雖然如果你們兩次反轉 char 陣列,string.reverse.reverse 將產生原始字串,但這種反轉絕對不會與原始字串相反。

你只需要反轉每個 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;
    }

    // this 
    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);

        // This would be wrong:
        // char[] a = s.ToCharArray();
        // System.Array.Reverse(a);
        // string r = new string(a);

        System.Console.WriteLine(r);
    }

並且 - 哦,快樂 - 你會意識到如果你這樣做正確,它也適用於亞洲/南亞/東亞語言(以及法語/瑞典語/挪威語等)……