eaglet

本博专注于基于微软技术的搜索相关技术
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

.Net 的区域性信息对程序移植性的影响

Posted on 2010-06-08 09:19  eaglet  阅读(3964)  评论(16编辑  收藏  举报

如果你的程序希望在不同语言的操作系统上平滑移植,你必须要正确理解和设置区域性信息(CultureInfo),这个问题对于习惯了中文windows 操作系统的我们来说,往往非常容易忽略。一旦忽略这个问题,我们在中文windows操作系统下运行正常的程序跑在英文或者其他语言的操作系统上,比如台湾或香港版本的windows下,轻则显示不对,重则逻辑错误。下面就谈谈这个区域性信息对程序移植性的影响

一、对字符串排序的影响

在.net 下,字符串的大小比较并不是如C++那样按照字符串字符内码大小顺序从头到尾来比较的。由于我是从C/C++转过来的,我一直以来都以为.net 下字符串的比较规则和C++是一样的,直到有一天我的程序在英文操作系统下出错。

.net 下,字符串的排序受 System.Threading.Thread.CurrentThread.CurrentCulture 这个当前区域性信息影响,不同的区域性信息,字符串的排序结果会完全不同。

比如简体中文操作系统的默认当前区域性信息为 zh-CN 而英文操作系统(美国销售的)默认为 en-US ,我们就来看看这两者对中文字符串的排序有什么不同

先看 zh-CN

 

            string[] stringList = { "不", "啊", "从", "的","一" };
 
            System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("zh-CN");
 
            Array.Sort(stringList);
 
            foreach (string str in stringList)
            {
                Console.WriteLine(str);
            }

 

 

输出结果为:




我们再看 en-US

 

            string[] stringList = { "不", "啊", "从", "的","一" };
 
            System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
 
            Array.Sort(stringList);
 
            foreach (string str in stringList)
            {
                Console.WriteLine(str);
            }

输出结果为:




我们可以看出,不同的区域性信息,上述字符串的排序结果完全不同,简体中文下,排序按照汉字的拼音顺序来排序,而en-US 下则是按汉字的unicode 内码顺序排序。

其实就是简体中文下,排序顺序也有两种,一种是拼音顺序,一种是笔画顺序,下面我们看看按笔画顺序排序的结果

 

            string[] stringList = { "不", "啊", "从", "的","一" };
 
            System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("zh-CN_stroke");
 
            Array.Sort(stringList);
 
            foreach (string str in stringList)
            {
                Console.WriteLine(str);
            }

输出结果为:





排序顺序对程序移植性的影响

显而易见,如果不注意这个问题,当程序从中文操作系统移植到英文操作系统上运行时,中文字符串的排序结果会完全不同,如果这个排序结果仅仅用于显示,则显示结果会不同,如果排序结果被作为一种类似主键的方式存储在文件,那么在中文操作系统下排序的文档,到了英文操作系统下就变成了不排序的文档,整个程序逻辑都会发生错误。

为了防止这种情况发生,我们必须在排序时指定一个固定的区域性信息,而不是使用操作系统默认的区域性信息。

 

二、对字符串查找的影响

           List<string> list = new List<string>(stringList);

            Console.WriteLine(list.BinarySearch("啊"));

上面代码,如果stringList 是从文件中读出,而这个文件是在中文操作系统下生成,如果当前是英文操作系统,则这里二分法查找字符串的结果就不确定,因为输入的字符串在英文操作系统下被认为不是排序的。

 

三、对Indexof的影响

这一节直接转载 MSDN 上的原文 http://msdn.microsoft.com/zh-cn/library/a7zyyk0c%28v=VS.80%29.aspx

 

您可以使用重载的 CompareInfo.IndexOf 方法返回指定字符串中某个字符或子字符串的从零开始的索引。如果在指定字符串中未找到该字符或子字符串,此方法将返回一个负整数。在使用 CompareInfo.IndexOf 搜索指定字符时,注意接受 CompareOptions 参数的方法重载执行比较的方式与不接受 CompareOptions 参数的方法重载不同。搜索 char(在 Visual Basic 中为 Char)并且不使用 CompareOptions 类型的参数的 CompareInfo.IndexOf 重载执行区分区域性的搜索。这就是说,如果 char 是一个表示预先撰写的字符的 Unicode 值,如连字“Æ”(\u00C6),则根据区域性的不同,它可能被视为等效于它的以正确顺序排列的任何组成部分,如“AE”(\u0041 \u0045)。若要执行序号(不区分区域性)搜索(即两个 char 只有 Unicode 值相同时才被视为相等),请使用带 CompareOptions 参数的 CompareInfo.IndexOf 重载之一。CompareOptions 参数设置为 CompareOptions.Ordinal 值。

您也可以使用搜索 charString.IndexOf 方法重载来执行序号搜索。请注意,搜索字符串的 String.IndexOf 方法重载执行区分区域性的搜索。

下面的代码示例阐释了根据区域性的不同,CompareInfo.IndexOf(string, char) 方法返回的结果的差异。针对“da-DK”(丹麦的丹麦语)创建 CultureInfo。接下来,使用 CompareInfo.IndexOf 方法的重载在字符串“Æble”和“aeble”中搜索字符“Æ”。请注意,对于“da-DK”区域性,带 CompareOptions.Ordinal 参数的 CompareInfo.IndexOf 方法与不带 CompareOptions.Ordinal 参数的 CompareInfo.Index 方法将返回相同的结果。字符“Æ”仅被视为等效于 Unicode 代码值 \u00E6。

using System;
using System.Globalization;
using System.Threading;
 
public class CompareClass
{
 
   public static void Main()
   {
      string str1 = "Æble";
      string str2 = "aeble"; 
      char find = 'Æ';
 
      // Creates a CultureInfo for Danish in Denmark.
      CultureInfo ci= new CultureInfo("da-DK");
 
      int result1 = ci.CompareInfo.IndexOf(str1, find);
      int result2 = ci.CompareInfo.IndexOf(str2, find);
      int result3 = ci.CompareInfo.IndexOf(str1, find,   
         CompareOptions.Ordinal);
      int result4 = ci.CompareInfo.IndexOf(str2, find, 
         CompareOptions.Ordinal);
 
      Console.WriteLine("\nCultureInfo is set to {0} ", ci.DisplayName);
      Console.WriteLine("\nUsing CompareInfo.IndexOf(string, char) 
         method\nthe result of searching for {0} in the string {1} is: 
         {2}", find, str1, result1);
      Console.WriteLine("\nUsing CompareInfo.IndexOf(string, char) 
         method\nthe result of searching for {0} in the string {1} is: 
         {2}", find, str2, result2);
      Console.WriteLine("\nUsing CompareInfo.IndexOf(string, char, 
         CompareOptions) method\nthe result of searching for {0} in the 
         string {1} is: {2}", find, str1, result3);
      Console.WriteLine("\nUsing CompareInfo.IndexOf(string, char, 
         CompareOptions) method\nthe result of searching for {0} in the 
         string {1} is: {2}", find, str2, result4);
   }
}

此代码产生下列输出:

CultureInfo is set to Danish (Denmark)

Using CompareInfo.IndexOf(string, char) method
the result of searching for Æ in the string Æble is: 0

Using CompareInfo.IndexOf(string, char) method
the result of searching for Æ in the string aeble is: -1

Using CompareInfo.IndexOf(string, char, CompareOptions) method
the result of searching for Æ in the string Æble is: 0

Using CompareInfo.IndexOf(string, char, CompareOptions) method
the result of searching for Æ in the string aeble is: -1

如果用 CultureInfo ci = new CultureInfo ("en-US") 替换 CultureInfo ci = new CultureInfo ("da-DK");,则带 CompareOptions.Ordinal 参数的 CompareInfo.Index 方法与不带 CompareOptions.Ordinal 参数的 CompareInfo.Index 方法将返回不同的结果。由 CompareInfo.IndexOf(string, char) 执行的区分区域性的比较认为字符“Æ”与其组成部分“ae”等效。由 CompareInfo.IndexOf(string, char, CompareOptions.Ordinal) 方法执行的序号(不区分区域性)比较则不返回与“ae”等效的字符“Æ”,因为它们的 Unicode 代码值不匹配。

当针对“en-US”区域性重新编译并执行这些代码时,将产生以下输出:

 

The CurrentCulture property is set to English (United States) 

Using CompareInfo.IndexOf(string, char) method
the result of searching for Æ in the string Æble is: 0

Using CompareInfo.IndexOf(string, char) method
the result of searching for Æ in the string aeble is: 0

Using CompareInfo.IndexOf(string, char, CompareOptions) method
the result of searching for Æ in the string Æble is: 0

Using CompareInfo.IndexOf(string, char, CompareOptions) method
the result of searching for Æ in the string aeble is: -1

 

四、如何指定当前默认的区域性信息

image

 

当前区域性信息在控制面板的 区域和语言选项中指定,修改第一个tab 中这两个参数将会影响操作系统当前默认的区域性信息。