Unicode 字符串逆序
字符串的逆序是个非常简单的算法,可以直接使用一层循环搞定,或者下面一句代码。
str = new string(str.Reverse().ToArray());
但是对于 Unicode 字符串来说,这种方法并不完全正确,因为 Unicode 里有复合字符和代理项对这两种特殊的东西。
复合字符是后跟一个或多个组合字符的基本字符,也就是说,有些符号并不是由单一的一个 char 来表示的,而是由一个基本字符后跟多个组合字符组成的。例如字符 'ë',它的 Unicode 编码是 \u00EB,但是它同样也可以使用 \u0065\u0308 来表示,其中 \u0065 对应字符 'e',\u0308 则是组合字符(表示 e 上的两点),如图 1 所示。这时候就需要用两个 char 来表示一个字符,而且它们的相对顺序不能被改变。组合字符在表示重音和数学符号时还是非常有用的。
图1 复合字符示例
代理项对是为了用 utf-16 表示 Unicode 基本多语言平面 (BMP) 以外的字符。例如符号 \U0001D160,在 C# 中是用两个 char \uD834\uDD60 表示的,其中 \uD834 是高代理项,\uDD60 是低代理项。
因此,要想更加完美的对 Unicode 字符串进行逆序,需要保证复合字符和代理项对的顺序。还好 C# 提供了 TextElementEnumerator 类来枚举字符串的文本元素,这样就不需要自己去考虑 Unicode 的具体编码方式了。
具体的实现还是一次循环,对于普通字符还是直接对 char 进行逆序,仅当遇到复合字符或代理项对时,才使用 TextElementEnumerator 进行枚举,并以文本元素为单位进行逆序。我了解有 TextElementEnumerator 这个类,也是当初在看 Microsoft.VisualBasic.Strings.StrReverse 方法的源代码才发现的,微软自己的类库考虑的的确比较全面。
using System.Globalization; namespace Cyjb { /// <summary> /// 提供 <see cref="System.String"/> 类的扩展方法。 /// </summary> public static class StringExt { /// <summary> /// 返回指定字符串的字符顺序是相反的字符串。 /// </summary> /// <param name="str">字符反转的字符串。</param> /// <returns>字符反转后的字符串。</returns> /// <remarks>参考了 Microsoft.VisualBasic.Strings.StrReverse 方法的实现。</remarks> public static string Reverse(this string str) { if (string.IsNullOrEmpty(str)) { return string.Empty; } int len = str.Length; int end = len - 1; int i = 0; char[] strArr = new char[len]; while (end >= 0) { switch (char.GetUnicodeCategory(str[i])) { case UnicodeCategory.Surrogate: case UnicodeCategory.NonSpacingMark: case UnicodeCategory.SpacingCombiningMark: case UnicodeCategory.EnclosingMark: // 字符串中包含组合字符,翻转时需要保证组合字符的顺序。 // 为了能够包含基字符,回退一位。 if (i > 0) { i--; end++; } TextElementEnumerator textElementEnumerator = StringInfo.GetTextElementEnumerator(str, i); textElementEnumerator.MoveNext(); int idx = textElementEnumerator.ElementIndex; while (end >= 0) { i = idx; if (textElementEnumerator.MoveNext()) { idx = textElementEnumerator.ElementIndex; } else { idx = len; } for (int j = idx - 1; j >= i; strArr[end--] = str[j--]) ; } goto EndReverse; } // 直接复制。 strArr[end--] = str[i++]; } EndReverse: return new string(strArr); } } }
关于 Unicode 的更多资料,可以参考《循序渐进全球化:支持 Unicode》。以上的字符串逆序也并不一定是完美的解决方案,不过条件所限,只能这样了。
代码可见 Cyjb.StringExt 类中的 Reverse 方法。
作者:CYJB
出处:http://www.cnblogs.com/cyjb/
GitHub:https://github.com/CYJB/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。