20.5 System.Globalization命名空间

其实,全球化是设计和开发支持针对多个区域性用户的本地化用户界面和区域数据的应用程序的过程。在.NET Framework中,CultureInfo类表示有关特定区域性的信息,这些信息包括书写系统、正在使用的日历、日期和时间格式化约定、数字和货币约定以及排序规则。

在开始设计阶段之前,应该首先确定应用程序将支持哪些区域性。这样,就可以设计支持所有已确定的区域性的功能。另外,还可以集中精力,编写出在所有支持的区域性中都可正常运行的代码。

System. Globalization命名空间包含定义区域性相关信息的类,这些信息包括语言、国家/地区、正在使用的日历、日期、货币和数字的格式模式,以及字符串的排序顺序。使用这些类可以简化开发全球通用应用程序的过程。

20.5.1 日历

除了Calendar类之外,.NET Framework还提供了下面这些类来实现根据当前区域性显示和使用日历的功能:

1)EastAsianLunisolarCalendar类派生自Calendar类,它将时间分为月、日、年和纪元,并且其日期是基于太阳和月亮的循环。它不仅支持太阳年和太阳月,同时还支持年份的甲子循环(每60年重复一次)。日历中的每个太阳年都与一个甲子年((Gt Sex age naryYear)、一个天干((GtCelestialStem)和一个地支((GtTerrestrialBranch)关联,并且这些历法在一年的任何月之后都可能有闰月,而一个月中也可以有一个闰日。

2)ChineseLunisolarCalendar类派生自EastAsianLunisolarCalendar类,它将时间分成多个部分来表示,如分成年、月和日。其中,该类是基于阳历计算来计算年,基于阴历计算来计算月和日。

需要特别说明的是,目前ChineseLunisolarCalendar类未用于CultureInfo类支持的任何区域性。因此,该类只能用于计算中国阴阳历中的日期。

3)GregorianCalendar类派生自Calendar类,用于表示公历。

4)HebrewCalendar类派生自Calendar类,用于表示犹太历。

5)HijriCalendar类派生自Calendar类,用于表示回历。

6)JapaneseCalendar类派生自Calendar类,用于表示日本历。除纪元年份不同外,日本历与公历完全一样。其中,日本历将每个皇帝的统治时期标识为一个纪元。当前纪元是平成纪元,始于公历1989年。纪元名称通常会显示在年的前面。例如,公历2001年是日本历的平成13年。注意,纪元的第一年称为“元年”。因此,公历1989年是日本历的平成元年。

7)JapaneseLunisolarCalendar类派生自EastAsianLunisolarCalendar类,它将时间分成多个部分来表示,如分成年、月和日。年按日本历计算,而日和月则按阴阳历计算。目前,该类未用于CultureInfo类支持的任何区域性。因此,它只能用于计算日本阴阳历中的日期。

8)JulianCalendar类派生自Calendar类,用于表示儒略历。目前,该类未用于CultureInfo类支持的任何区域性。因此,它只能用于计算儒略历中的日期。

9)KoreanCalendar类派生自Calendar类,用于表示朝鲜历。除纪元年份不同外,朝鲜历与公历完全一样。

10)KoreanLunisolarCalendar类派生自EastAsianLunisolarCalendar类,它将时间分成多个部分来表示,如分成年、月和日。年按公历计算,而日和月按阴阳历计算。目前,该类未用于CultureInfo类支持的任何区域性。因此,它只能用于计算朝鲜阴阳历中的日期。

11)PersianCalendar类派生自Calendar类,用于表示波斯历。

12)TaiwanCalendar类派生自Calendar类,用于表示台湾日历。除纪元年份不同外,台湾日历与公历完全一样。

13)TaiwanLunisolarCalendar类派生自EastAsianLunisolarCalendar类,用于表示台湾日历。其中,年按公历计算,而日和月则按阴阳历计算。目前,该类未用于CultureInfo类支持的任何区域性。因此,它只能用于计算台湾阴阳历中的日期。

14)ThaiBuddhistCalendar类派生自Calendar类,用于表示泰国佛历。除纪元年份不同外,泰国佛历与公历完全一样。

15)UmAlQuraCalendar类派生自Calendar类,用于表示沙特阿拉伯回历。

下面的代码示例阐述了DateTime结构和Calendar类中的类似方法如何为同一区域性检索不同的结果。其中,在应用程序里将线程的CurrentCulture属性设置为“he-IL”(即以色列希伯来语),而将当前日历设置为犹太历(即HebrewCalendar)。之后,创建和初始化了一个DateTime类型。接下来,使用DateTime和Calendar的成员返回日期、月份、年份以及这一年的月份数,并显示这些值。

在应用程序中,Calendar类的方法会根据犹太历返回日、月、年以及这一年的月份数;而DateTime方法总是使用公历来执行计算,而忽视当前日历的设置。如下面的代码所示:


protected void Page_Load(object sender, EventArgs e)

{

CultureInfo ci=new CultureInfo("he-IL");

Thread.CurrentThread.CurrentCulture=ci;

ci.DateTimeFormat.Calendar=new HebrewCalendar();

Response.Write("DisplayName:"+ci.DisplayName.ToString()

+"——Culture:"

+ci.DateTimeFormat.Calendar.ToString());

DateTime dt=new

DateTime(5760,11,4,ci.DateTimeFormat.Calendar);

Response.Write("<br/>DateTime-Day:"

+dt.Day);

Response.Write("<br/>Calendar-Day:"

+ci.DateTimeFormat.Calendar.GetDayOfMonth(dt));

Response.Write("<br/>DateTime-Month:"

+dt.Month);

Response.Write("<br/>Calendar-Month:"

+ci.DateTimeFormat.Calendar.GetMonth(dt));

Response.Write("<br/>DateTime-Year:"

+dt.Year);

Response.Write("<br/>Calendar-Year:"

+ci.DateTimeFormat.Calendar.GetYear(dt));

Response.Write("<br/>"

+ci.DateTimeFormat.Calendar.GetMonthsInYear(

ci.DateTimeFormat.Calendar.GetYear(dt)));

}


示例代码运行结果如图20-10所示。

figure_0739_0544

图 20-10 示例运行结果

20.5.2 日期和时间

在DateTime结构中,它提供了一些方法来使应用程序可以针对DateTime类型执行区分区域性的操作。可以使用DateTimeFormatInfo类来根据区域性格式化和显示DateTime类型。例如,使用ShortDatePattern,可以针对英语(美国)“en-US”区域性将日期2001年2月1日格式化为2/1/2001,而针对英语(英国)“en-GB”区域性将该日期格式化为01/02/2001。

其中,DateTimeFormatInfo类定义了如何根据区域性设置DateTime值的格式并显示这些值。它包含各种信息,例如日期模式、时间模式和AM/PM指示项。其中,与DateTimeFormatInfo属性关联的标准DateTime格式模式如表20-4所示。

figure_0739_0545

figure_0740_0546

这里需要说明的是,只能为特定区域性或固定区域性创建DateTimeFormatInfo对象,而不能为非特定区域性创建该对象。非特定区域性不提供显示正确日期格式所需的足够信息,如果应用程序尝试使用非特定区域性来创建DateTimeFormatInfo对象,将引发异常。

如果要为特定区域性创建一个DateTimeFormatInfo对象,则应为该区域性创建一个CultureInfo对象并检索CultureInfo.DateTimeFormat属性(以这种方式获得的日期/时间数据仅适用于特定的区域性);如果要为当前线程的区域性创建一个DateTimeFormatInfo对象,那么应该使用CurrentInfo属性。

下面的示例将输出en-US区域性的不同格式模式。同时,它还会显示与这些格式模式关联的属性值。代码如下所示:


protected void Page_Load(object sender, EventArgs e)

{

DateTime dt=DateTime.Now;

DateTimeFormatInfo dtfi=

new CultureInfo("en-US",false).DateTimeFormat;

Response.Write("d:    "

+dt.ToString("d",dtfi));

Response.Write("<br/>"+dtfi.ShortDatePattern

+"((SortDatePattern)");

Response.Write("<hr/>");

Response.Write("D:    "

+dt.ToString("D",dtfi));

Response.Write("<br/>"+dtfi.LongDatePattern

+"((LngDatePattern)");

Response.Write("<hr/>");

Response.Write("f:    "

+dt.ToString("f",dtfi));

Response.Write("<br/>F:    "

+dt.ToString("F",dtfi));

Response.Write("<br/>"+dtfi.FullDateTimePattern

+"((FllDateTimePattern)");

Response.Write("<hr/>");

Response.Write("g:    "

+dt.ToString("g",dtfi));

Response.Write("<br/>G:    "

+dt.ToString("G",dtfi));

Response.Write("<br/>m:    "

+dt.ToString("m",dtfi));

Response.Write("<br/>"+dtfi.MonthDayPattern

+"((MnthDayPattern)");

Response.Write("<hr/>");

Response.Write("M:    "

+dt.ToString("M",dtfi));

Response.Write("<br/>"+dtfi.MonthDayPattern

+"((MnthDayPattern)");

Response.Write("<hr/>");

Response.Write("o:    "

+dt.ToString("o",dtfi));

Response.Write("<br/>r:    "

+dt.ToString("r",dtfi));

Response.Write("<br/>"+dtfi.RFC1123Pattern

+"((RC1123Pattern)");

Response.Write("<hr/>");

Response.Write("R:    "

+dt.ToString("R",dtfi));

Response.Write("<br/>"+dtfi.RFC1123Pattern

+"((RC1123Pattern)");

Response.Write("<hr/>");

Response.Write("s:    "

+dt.ToString("s",dtfi));

Response.Write("<br/>"+dtfi.SortableDateTimePattern

+"((SrtableDateTimePattern)");

Response.Write("<hr/>");

Response.Write("t:    "

+dt.ToString("t",dtfi));

Response.Write("<br/>"+dtfi.ShortTimePattern

+"((SortTimePattern)");

Response.Write("<hr/>");

Response.Write("T:    "

+dt.ToString("T",dtfi));

Response.Write("<br/>"+dtfi.LongTimePattern

+"((LngTimePattern)");

Response.Write("<hr/>");

Response.Write("u:    "

+dt.ToString("u",dtfi));

Response.Write("<br/>"

+dtfi.UniversalSortableDateTimePattern

+"((UiversalSortableDateTimePattern)");

Response.Write("<hr/>");

Response.Write("U:    "

+dt.ToString("U",dtfi));

Response.Write("<br/>y:    "

+dt.ToString("y",dtfi));

Response.Write("<br/>"+dtfi.YearMonthPattern

+"((YarMonthPattern)");

Response.Write("<hr/>");

Response.Write("Y:    "

+dt.ToString("Y",dtfi));

Response.Write("<br/>"+dtfi.YearMonthPattern

+"((YarMonthPattern)");

}


示例运行结果如图20-11所示。

figure_0742_0547

图 20-11 示例运行结果

在时区方面,DateTime结构总是使用本地时区来执行计算和比较。利用它的Parse和ParseExact方法允许你将日期和时间的字符串表示形式转换为DateTime类型。当然,也可以针对特定区域性来格式化DateTime类型。如果没有在传递给这些方法的字符串中指定时区,则这些方法将检索经过分析的日期和时间,而不会执行时区调整。日期和时间基于操作系统的时区设置。如果应用程序指定了时区偏移量,则这些方法将分析日期/时间字符串,将其转换为协调世界时((UC:以前称为格林尼治标准时间,即GMT),然后将其转换为本地系统的时间。

可以使用ToUniversalTime方法将本地DateTime类型转换为它的UTC等效类型。若要分析日期/时间字符串并将其转换为UTC DateTime类型,应用程序应将DateTimeStyles枚举(该枚举类型定义一些格式设置选项,这些选项可自定义Parse和ParseExact方法的字符串分析方法)AdjustToUniversal(该枚举值以协调UTC形式返回日期和时间。如果输入字符串表示本地时间,则会将日期和时间从本地时间转换为UTC;如果输入字符串表示UTC时间,则不进行任何转换;如果输入字符串不表示本地时间或UTC时间,同样不会进行任何转换,并且生成的Kind属性为Unspecified。)值与Parse方法或ParseExact方法一起使用。

例如,下面的示例代码针对本地时间创建了一个DateTime类型,然后将其转换为UTC等效类型。同时,再将这两种类型均转换为字符串并输出到页面。

而后,继续将这两种时间字符串使用ParseExact方法重新转换为DateTime类型。其中,若要捕获存储在utcdt中的时区信息,必须将DateTimeStyles的AdjustToUniversal值指定为ParseExact方法的参数。如下面的代码所示:


protected void Page_Load(object sender, EventArgs e)

{

CultureInfo en=new CultureInfo("en-US");

Thread.CurrentThread.CurrentCulture=en;

DateTime dt=DateTime.Now;

DateTime utcdt=dt.ToUniversalTime();

String format="MM/dd/yyyy hh:mm:sszzz";

String str=dt.ToString(format);

Response.Write("转换的本地时间:"+str);

String utcstr=utcdt.ToString(format);

Response.Write("<br/>转换的UTC:"+utcstr);

DateTime parsedBack=

DateTime.ParseExact(str, format, en.DateTimeFormat);

Response.Write("<br/>本地时间((PrseExact):"+parsedBack);

DateTime parsedBackUTC=DateTime.ParseExact(

str, format, en.DateTimeFormat,

DateTimeStyles.AdjustToUniversal);

Response.Write("<br/>UTC(ParseExact)"+parsedBackUTC);

}


运行上面的示例代码,结果如图20-12所示。但这里需要注意的是,由于本地时区和UTC之间存在UTC偏移量,因此,这两种时间字符串之间存在差异。

figure_0743_0548

图 20-12 示例运行结果

20.5.3 数值型数据

对于数值型数据,NumberFormatInfo类定义如何根据区域性来格式化和显示货币、小数点分隔符和其他数值符号。例如,

对于区域性英语(美国)“en-US”,将十进制数字10000.50格式化为10,000.50;对于区域性德语(德国)“de-DE”,则将其格式化为10.000,50。

表20-5描述了每个标准格式说明符的格式模式,以及可通过设置来修改标准格式的关联NumberFormatInfo属性。

figure_0743_0549

与DateTimeFormatInfo类一样,只能为特定区域性或固定区域性创建NumberFormatInfo对象,而不能为非特定区域性创建该对象。非特定区域性不提供显示正确数字格式所需的足够信息。如果应用程序尝试使用非特定区域性来创建NumberFormatInfo对象,将引发异常。

其中,如果要为特定区域性创建一个NumberFormatInfo对象,那么应用程序应为该区域性创建一个CultureInfo对象并检索CultureInfo.NumberFormat属性;如果要为当前线程的区域性创建一个NumberFormatInfo对象,那么应用程序应该使用CurrentInfo属性;如果要为固定区域性创建一个NumberFormatInfo对象,那么应用程序应对只读版本使用InvariantInfo属性,对可写版本则要使用NumberFormatInfo构造函数。

例如,下面的示例演示了如何使用当前区域性的NumberFormatInfo标准货币格式(“c”)来显示整数。代码如下所示:


protected void Page_Load(object sender, EventArgs e)

{

int i=123;

NumberFormatInfo nfi;

CultureInfo bz=new CultureInfo("en-BZ");

nfi=bz.NumberFormat;

Response.Write(bz.DisplayName+"——"+nfi.CurrencySymbol);

Response.Write("<br/>"+i.ToString("c",bz));

CultureInfo us=new CultureInfo("en-US");

nfi=us.NumberFormat;

Response.Write("<br/>"+us.DisplayName+"——"

+nfi.CurrencySymbol);

Response.Write("<br/>"+i.ToString("c",us));

CultureInfo dk=new CultureInfo("da-DK");

nfi=dk.NumberFormat;

Response.Write("<br/>"+dk.DisplayName+"——"

+nfi.CurrencySymbol);

Response.Write("<br/>"+i.ToString("c",dk));

}


示例运行结果如图20-13所示。

figure_0744_0550

图 20-13 示例运行结果

除此之外,许多欧洲国家或地区都通用两种货币:即欧元和本地货币。因此,可能需要在一个应用程序中同时显示这两种货币。

例如,下面的示例为默认货币为欧元的区域性“fr-FR”创建了一个CultureInfo对象。为了显示本地货币的货币符号,必须使用NumberFormatInfo.Clone方法为CultureInfo再克隆一个新的NumberFormatInfo,并用本地货币符号替换默认货币符号。代码如下所示:


protected void Page_Load(object sender, EventArgs e)

{

int i=123;

CultureInfo ci=new CultureInfo("fr-FR");

Thread.CurrentThread.CurrentCulture=ci;

NumberFormatInfo LocalFormat=

((NmberFormatInfo)NumberFormatInfo.CurrentInfo.Clone();

LocalFormat.CurrencySymbol="F";

Response.Write(i.ToString("c",LocalFormat));

Response.Write("<br/>"

+i.ToString("c",NumberFormatInfo.CurrentInfo));

}


示例运行结果如图20-14所示。

figure_0745_0551

图 20-14 示例运行结果

最后,需要特别说明的是,CultureInfo和RegionInfo类都包含有关货币的信息,并为每一个区域性仅指定一种货币。建议编写对区域性使用.NET Framework默认货币设置的代码,以使应用程序不会受到操作系统差异所带来的影响,并确保货币格式化的一致性。应用程序应使用接受useUserOverride参数的构造函数重载之一来创建CultureInfo对象,并将此参数设置为false。该设置会使用户操作系统上的默认货币设置被.NET Framework的正确默认设置重写,如下面的代码所示:


Thread.CurrentThread.CurrentCulture=

new CultureInfo("fr-FR",false);


20.5.4 数据的比较和排序

其实,用于对项排序的字母顺序和约定随区域性的不同而不同。例如,排序顺序可以是区分大小写的,也可以不区分大小写。它可以是基于发音的,也可以是基于字符外观的。在东亚语言中,按文字的笔画和部首进行排序。排序也可能随语言和区域性使用的字母表基本顺序的不同而不同。例如,瑞典语中有“Æ”字符,它在字母表中排在“Z”之后;而德语中也有该字符,但它在字母表中同“ae”一样排在“A”之后。因此,应用程序应该能够针对每个区域性对数据进行比较和排序,以支持区域性特定和语言特定的排序约定。

在.NET Framework中,CompareInfo类提供了一组方法,可用于执行区分区域性的字符串比较。同时,CultureInfo类还具有一个CompareInfo属性,它是此类的一个实例,它定义如何针对特定区域性对字符串进行比较和排序。

1.字符串比较

在字符串比较方面,CompareInfo类提供了Compare方法。其原型如下:

1)比较两个字符串。


public virtual int Compare(string string1,string string2)


2)使用指定的CompareOptions值比较两个字符串。


public virtual int Compare(string string1,string string2,

CompareOptions options)


3)将一个字符串的结尾部分与另一个字符串的结尾部分相比较。


public virtual int Compare(string string1,int offset1,

string string2,int offset2)


4)使用指定的CompareOptions值将一个字符串的结尾部分与另一个字符串的结尾部分相比较。


public virtual int Compare(string string1,int offset1,

string string2,int offset2,CompareOptions options)


5)将一个字符串的一部分与另一个字符串的一部分相比较。


public virtual int Compare(string string1,int offset1,

int length1,string string2,int offset2,int length2)


6)使用指定的CompareOptions值将一个字符串的一部分与另一个字符串的一部分相比较。


public virtual int Compare(string string1,int offset1,

int length1,string string2,int offset2,

int length2,CompareOptions options)


Compare方法使用CompareInfo属性中的信息来比较字符串。如果string1小于string2,则String.Compare方法返回一个负整数;如果string1和string2相等,则返回零;如果string1大于string2,则返回一个正整数。

例如,下面的示例演示如何根据用于执行比较的区域性,通过String.Compare方法以不同的方式来计算两个字符串。代码如下所示:


protected void Page_Load(object sender, EventArgs e)

{

string s1="Apple";

string s2="Æble";

Thread.CurrentThread.CurrentCulture=

new CultureInfo("da-DK");

int result=String.Compare(s1,s2);

Response.Write("((d-DK)"+s1+"与"+s2

+"的比较结果是:"+result);

Thread.CurrentThread.CurrentCulture=

new CultureInfo("en-US");

result=String.Compare(s1,s2);

Response.Write("<br/>((e-US)"+s1+"与"+s2

+"的比较结果是:"+result);

}


在上面的示例代码中,首先,将CurrentCulture设置为da-DK以表示丹麦语(丹麦)区域性,并比较字符串“Apple”和“Æble”。丹麦语将字符“Æ”视为单个字母,并在字母表中将其排在“Z”之后。因此,对于丹麦语区域性,字符串“Æble”比“Apple”大。

接下来,再将CurrentCulture设置为en-US以表示英语(美国)区域性,并再次比较字符串“Apple”和“Æble”。这次,字符串“Æble”被确定为小于“Apple”。英语语言将字符“Æ”视为一个特殊符号,并在字母表中将其排在字母“A”之前。因此,示例运行结果如图20-15所示。

figure_0746_0552

图 20-15 示例运行结果

上面的比较字符串示例是一种最简单的比较办法。但在实际开发中,应用程序应该使用接受CompareOptions值来指定预期的比较类型的字符串比较方法。其中,CompareOptions枚举定义要用于CompareInfo的字符串比较选项,其枚举值如表20-6所示。

figure_0747_0553

例如,下面的示例演示了与CultureInfo对象关联的CompareInfo对象如何影响字符串。在该示例中,还可以看见使用CompareOptions枚举与不使用CompareOptions枚举的效果。如下面的代码所示:


protected void Page_Load(object sender, EventArgs e)

{

String[]sign=new String[]{"<","=",">"};

StringBuilder str=new StringBuilder();

String s1="Coté",s2="coté",s3="côte";

CompareInfo ci=new CultureInfo("fr-FR").CompareInfo;

str.Append("区域性名称:"+ci.Name

+"——区域性标识符:"+ci.LCID);

str.Append("<br/>((f-FR)比较:"+s1

+sign[ci.Compare(s1,s2,CompareOptions.IgnoreCase)+1]+s2);

str.Append("<br/>((f-FR)比较:"+s2

+sign[ci.Compare(s2,s3,CompareOptions.None)+1]+s3);

ci=new CultureInfo("ja-JP").CompareInfo;

str.Append("<br/>区域性名称:"+ci.Name

+"——区域性标识符:"+ci.LCID);

str.Append("<br/>((j-JP)比较:coté"

+sign[ci.Compare(s2,s3)+1]+"côte");

Response.Write(str.ToString());

}


示例运行结果如图20-16所示。

figure_0748_0554

图 20-16 示例运行结果

2.字符串检索

在字符串检索方面,CompareInfo类提供了IndexOf方法。如果在指定字符串中未找到该字符或子字符串,则该方法将检索到一个负整数。其原型如下:

1)搜索指定的字符并返回整个源字符串内第一个匹配项的从零开始的索引。


public virtual int IndexOf(string source, char value)


2)搜索指定的子字符串并返回整个源字符串内第一个匹配项的从零开始的索引。


public virtual int IndexOf(string source, string value)


3)使用指定的CompareOptions值,搜索指定的字符,并返回整个源字符串内第一个匹配项的从零开始的索引。


public virtual int IndexOf(string source, char value,

CompareOptions options)


4)搜索指定的字符,并返回源字符串内从指定的索引位置到字符串结尾这一部分中第一个匹配项的从零开始的索引。


public virtual int IndexOf(string source, char value,

int startIndex)


5)使用指定的CompareOptions值,搜索指定的子字符串,并返回整个源字符串内第一个匹配项的从零开始的索引。


public virtual int IndexOf(string source, string value,

CompareOptions options)


6)搜索指定的子字符串,并返回源字符串内从指定的索引位置到字符串结尾这一部分中第一个匹配项的从零开始的索引。


public virtual int IndexOf(string source, string value,

int startIndex)


7)使用指定的CompareOptions值,搜索指定的字符,并返回源字符串中从指定的索引位置到字符串结尾这一部分中第一个匹配项的从零开始的索引。


public virtual int IndexOf(string source, char value,

int startIndex, CompareOptions options)


8)搜索指定的字符,并返回源字符串内从指定的索引位置开始、包含所指定元素数的部分中第一个匹配项的从零开始的索引。


public virtual int IndexOf(string source, char value,

int startIndex, int count)


9)使用指定的CompareOptions值,搜索指定的子字符串,并返回源字符串内从指定的索引位置到字符串结尾这一部分中第一个匹配项的从零开始的索引。


public virtual int IndexOf(string source, string value,

int startIndex, CompareOptions options)


10)搜索指定的子字符串,并返回源字符串内从指定的索引位置开始、包含所指定元素数的部分中第一个匹配项的从零开始的索引。


public virtual int IndexOf(string source, string value,

int startIndex, int count)


11)使用指定的CompareOptions值,搜索指定的字符,并返回源字符串内从指定的索引位置开始、包含所指定元素数的部分中第一个匹配项的从零开始的索引。


public virtual int IndexOf(string source, char value,

int startIndex, int count, CompareOptions options)


12)使用指定的CompareOptions值,搜索指定的子字符串,并返回源字符串内从指定的索引位置开始、包含所指定元素数的部分中第一个匹配项的从零开始的索引。


public virtual int IndexOf(string source, string value,

int startIndex, int count, CompareOptions options)


与字符串比较操作一样,在使用IndexOf方法检索指定字符时,需要考虑CompareOptions参数,接受CompareOptions参数与不接受此参数是不同的。

例如,下面的示例演示了在不同区域性下,IndexOf方法的检索结果的差异。

首先,将CultureInfo对象设置为da-DK以表示丹麦语(丹麦)区域性。接下来,使用IndexOf方法在字符串“Æble”和“aeble”中搜索字符“Æ”。详细代码如下所示:


protected void Page_Load(object sender, EventArgs e)

{

StringBuilder str=new StringBuilder();

string str1="Æble";

string str2="aeble";

char find='Æ';

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

str.Append("result1:"+result1);

str.Append("<br/>result2:"+result2);

str.Append("<br/>result3:"+result3);

str.Append("<br/>result4:"+result4);

Response.Write(str.ToString());

}


在上面的代码中,需要注意的是,对于da-DK来讲,接受设置为Ordinal的CompareOptions参数的IndexOf方法与不接受此参数的同一方法检索到的内容是一样的。字符“Æ”仅被视为等效于Unicode代码值\u00E6。因此,示例运行结果如图20-17所示。

figure_0750_0555

图 20-17 示例运行结果

3.字符串排序

在字符串排序方面,Array类提供了重载的Sort方法,应用程序可根据CurrentCulture属性使用该方法对数组进行排序。

在下面的示例中,首先创建一个由三个字符串组成的数组。然后将CurrentCulture属性设置为en-US,并调用Sort方法进行排序。其结果排序顺序基于英语(美国)区域性的排序约定。

接下来,继续将CurrentCulture属性设置为da-DK,并再次调用Sort方法进行排序。其结果排序顺序与使用en-US时的结果是不一样的,因为使用的是da-DK的排序约定。详细代码如下所示:


protected void Page_Load(object sender, EventArgs e)

{

String str1="Apple";

String str2="Æble";

String str3="Zebra";

Array stringArray=Array.CreateInstance(typeof(String),3);

stringArray.SetValue(str1,0);

stringArray.SetValue(str2,1);

stringArray.SetValue(str3,2);

Response.Write("Array初始排序:<br/>");

PrintIndexAndValues(stringArray);

Thread.CurrentThread.CurrentCulture=

new CultureInfo("en-US");

Array.Sort(stringArray);

Response.Write("((e-US)排序:<br/>");

PrintIndexAndValues(stringArray);

Thread.CurrentThread.CurrentCulture=

new CultureInfo("da-DK");

Array.Sort(stringArray);

Response.Write("((d-DK)排序:<br/>");

PrintIndexAndValues(stringArray);

}

private void PrintIndexAndValues(Array myArray)

{

for(int i=myArray.GetLowerBound(0);

i<=myArray.GetUpperBound(0);i++)

{

Response.Write("["+i+"]:"

+myArray.GetValue(i)+"<br/>");

}

}


示例运行结果如图20-18所示。

figure_0751_0556

图 20-18 示例运行结果

除此之外,还可以使用GetSortKey方法为指定的字符串创建排序关键字(排序关键字用于支持区分区域性的排序。根据“Unicode标准”,字符串中的每个字符都有若干类别给定的排序权重,包括字母、大小写和音调权重。排序关键字充当特定字符串的这些权重的储存库。例如,排序关键字可以包含字母权重字符串,后跟大小写权重字符串等)。指定字符串的结果排序关键字是一个字节序列,它可能会因指定的CurrentCulture和CompareOptions值的不同而不同。例如,如果在创建排序关键字时,应用程序指定值IgnoreCase,则使用该排序关键字进行的字符串比较操作将忽略大小写。

为字符串创建排序关键字后,应用程序可以将该关键字作为参数传递给SortKey类提供的方法。Compare方法可以对排序关键字进行比较。实际上,在进行大量排序的应用程序中,通过为所用的所有字符串生成并存储排序关键字,可以提高性能。需要执行排序或比较操作时,应用程序可以使用这些排序关键字,而不必使用字符串。与此同时,使用SortKey.Compare方法排序比使用String.Compare方法排序更快。

下面的示例演示了排序关键字的使用情况。

首先,将CurrentCulture设置为da-DK,同时为两个字符串创建排序关键字sc1与sc2,然后使用SortKey.Compare方法比较这两个字符串,并输出显示结果。如果str1小于str2,该方法返回一个负整数;如果str1和str2相等,则返回零(0);如果str1大于str2,则返回一个正整数。

接下来,继续将CurrentCulture属性设置为en-US,同样再为这些字符串创建排序关键字sc3与sc4。然后使用SortKey.Compare方法比较这两个字符串,并输出显示结果。这里需要注意的是,排序结果因CurrentCulture的设置的不同而不同。因此,这两次排序所得的结果是不一样的。如下面的代码所示:


protected void Page_Load(object sender, EventArgs e)

{

StringBuilder str=new StringBuilder();

string str1="Apple";

String str2="Æble";

CultureInfo dk=new CultureInfo("da-DK");

Thread.CurrentThread.CurrentCulture=dk;

SortKey sc1=dk.CompareInfo.GetSortKey(str1);

SortKey sc2=dk.CompareInfo.GetSortKey(str2);

int result1=SortKey.Compare(sc1,sc2);

str.Append("((d-DK)"+str1+"与"+str2

+"的比较结果为:"+result1+"<br/>");

CultureInfo enus=new CultureInfo("en-US");

Thread.CurrentThread.CurrentCulture=enus;

SortKey sc3=enus.CompareInfo.GetSortKey(str1);

SortKey sc4=enus.CompareInfo.GetSortKey(str2);

int result2=SortKey.Compare(sc3,sc4);

str.Append("((e-US)"+str1+"与"+str2

+"的比较结果为:"+result2);

Response.Write(str.ToString());

}


示例运行结果如图20-19所示。

figure_0752_0557

图 20-19 示例运行结果