zoukankan      html  css  js  c++  java
  • 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 方法。

  • 相关阅读:
    linux环境下,使用Navicat连接mysql时,提示本地IP无法连接虚拟环境下的mysql解决方案
    数组-(Zero Sum Subarray)返回数组中和为某个数的子串数组
    hash、hashlib使用
    优先队列
    Jensen 不等式
    ML-集成学习:AdaBoost、Bagging、随机森林、Stacking(mlxtend)、GBDT、XGBoost、LightGBM、CatBoost原理推导及实现
    scipy.optimize优化器的各种使用
    RL-马尔科夫决策过程(MDP)-原理及实现
    ML-sklearn参数随机优化:GridSearchCV、RandomizedSearchCV、hyperopt
    5.matplotlib绘制-meshgrid区域图-可视化ML
  • 原文地址:https://www.cnblogs.com/cyjb/p/UnicodeStringReverse.html
Copyright © 2011-2022 走看看