如果你的程序希望在不同语言的操作系统上平滑移植,你必须要正确理解和设置区域性信息(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 值。
您也可以使用搜索 char 的 String.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
四、如何指定当前默认的区域性信息
当前区域性信息在控制面板的 区域和语言选项中指定,修改第一个tab 中这两个参数将会影响操作系统当前默认的区域性信息。