如何把string解析为int?[C#]
如何把string解析为int?[C#]
How to Parse a string to an int? [C#]
Writen by Allen Lee
Q:如何把string解析为int?
A:简单的方法有三种:
int result = 0;
// 使用Convert.ToInt32(string value);
result = Convert.ToInt32(source);
// 使用Int32.Parse(string value);
result = Int32.Parse(source);
// 使用Int32.TryParse(string s, out int result);
Int32.TryParse(source, out result);
Q:这三种方法有什么不同?
A:一个简单的回答是:
如果解析失败,Int32.Parse(source)总会抛出异常;Convert.ToInt32(source)在source为null的情况下不会抛出异常而是简单的返回0给调用方;而Int32.TryParse(source, result)则无论如何都不抛出异常,只会返回true或false来说明解析是否成功,如果解析失败,调用方将会得到0值。
Q:如果我要解析的字符串的字面数值不是十进制的话,那么从这些方法中得到的返回值是有问题的。有什么方法解决?
A:那么你就需要这些方法的对应重载版本了,一个简单的方法是使用Convert类的
其中fromBase的值只能为2、8、10或者16,用于指定进制方式。如果fromBase不是指定的数值或者value不为十进制而又带有前缀正负号,就会抛出ArgumentException。
int result = Convert.ToInt32(source, 16);
当然,你还可以通过为Int32类的
指定NumberStyles.AllowHexSpecifier或者NumberStyles.HexNumber为第二个参数来解析十六进制字面值的字符串,此时,你需要引用System.Globalization命名空间。
或者使用Int32类的
并指定NumberStyles.AllowHexSpecifier或者NumberStyles.HexNumber为第二个参数,null为第三个参数来解析十六进制字面值的字符串。你当然也应该引用System.Globalization命名空间。
这里有一点要提醒的是,无论使用Parse或者TryParse方法来解析十六进制,字符串都不能出现0x或0X前缀,否则将会抛出异常。
Q:如果我要把使用科学记数法表示的string转换为int又该如何呢?
A:你可以通过把NumberStyles.AllowDecimalPoint | NumberStyles.AllowExponent(把两个NunberStyles枚举进行位运算,其中前者说明可能存在小数点,而后者则说明可能存在科学记数法的指数符号)作为第二个参数传递给Int32类的
或者
如果解析出来的结果与int不兼容的,就要考虑把结果储存在别的类型了。例如"1.412e2"就应该把解析结果存放到float或者double或者decimal类型的变量里,当然,你也应该使用与储存变量相对应的类型的方法来解析:
整个字符串表达式应该没有任何任何空格,否则将有可能抛出异常。
科学记数法的格式为[{+|-}]m.dddddd{e|E}[{+|-}]xx,可以分解为以下几种形式:
- [-]m.dddddde+xx
- [-]m.dddddde-xx
- [-]m.ddddddE+xx
- [-]m.ddddddE-xx
下面列举一些不能正常解析的:
- "1.412 e3"
- "1.412E 3"
- "1.412e +3"
- "141200E- 2"
Q:如何处理待解析string里所包含的空格?
A:对于字符串的前缀或后缀空格,你同样有多种选择,一般情况下,你可以直接使用String类的
来取掉头尾可能包含的空格:
又或者,你使用NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite来告诉Parse或TryParse待解析字符串的头尾可能包含着空格。
如果待解析的字符串使用科学记数法来表示,那么你可以
其中NumberStyles.Float告诉Parse方法待解析的字符串可能前缀或后缀的空格、前缀正负号、(十进制)小数点、科学记数法指数表示等。
Q:如何处理货币字符串的解析?
A:你可以通过指定NumberStyles.Currency来告诉Parse或TryParse待解析的字符串是货币样式的。NumberStyles.Currency说明待解析字符串可能包含前缀或后缀空格、前缀正负号、十进制小数点、千分位符号、字面数值可能为整数或小数等:
但货币样式字符串不能带有任何十六进制符号。另外,货币样式字符串格式的相关设定以Region and Languages Options(区域与语言选项)里面的设定为基准。
Q:如果我更改系统的Region and Languages Options(区域与语言选项)里面的设置,但又希望不按照里面的设置来解析字符串呢?
A:这个时候就轮到NumberFormatInfo类出场了,这个类能够让你无需对系统的设置作任何修改而直接告诉相关的方法如何处理字符串里的相关符号。
对应上图,我们通过NumberFormatInfo的相关属性来设定Customerize Regional Options(自定义区域选项)中Currency(货币)选项卡里的相关设定:
NumberFormatInfo | Currency(货币) |
CurrencySymbol | Currency symbol(货币符号) |
CurrencyPositivePattern | Positive currency format(货币正数格式) |
CurrencyNegativePattern | Negative currency format(货币符号格式) |
CurrencyDecimalSeparator | Decimal symbol(小数点) |
CurrencyDecimalDigits | No. of digits after decimal(小数数位) |
CurrencyGroupSeparator | Digit grouping symbol(数字分组符号) |
其中,Customerize Regional Options(自定义区域选项)中Currency(货币)选项卡里的Digit grouping(数字分组)需要结合NumberFormatInfo的CurrencyGroupSeparator和CurrencyGroupSizes两个属性来设定。而NumberFormatInfo的CurrencyPositivePattern和CurrencyNegativePattern属性的设值是有限制的:
CurrencyPositivePattern | |
Value | Associated Pattern |
0 | $n |
1 | n$ |
2 | $ n |
3 | n $ |
CurrencyNegativePattern | |
Value | Associated Pattern |
0 | ($n) |
1 | -$n |
2 | $-n |
3 | $n- |
4 | (n$) |
5 | -n$ |
6 | n-$ |
7 | n$- |
8 | -n $ |
9 | -$ n |
10 | n $- |
11 | $ n- |
12 | $ -n |
13 | n- $ |
14 | ($ n) |
15 | (n $) |
Q:设置好的NumberFormatInfo的实例应该怎么使用?
A:NumberFormatInfo是IFormatProvider接口的一个实现,你可以把NumberFormatInfo的实例作为参数传递给对应的重载方法。
Q:那么,Customerize Regional Options(自定义区域选项)中Numbers(数字)选项卡里的设定是否也能通过NumberFormatInfo的对应属性来设置?
A:不全是,其中Display leading zeros(零起始显示)、List separator(列表分隔符)、Measurement system(度量衡系统)这三项没有;Negative number format(负数格式)需要同时结合使用NumberFormatInfo的NegativeSign、NumberDecimalDigits、NumberDecimalSeparator等来设置;其他的都能在NumberFormatInfo中找到与之对应的属性。
Q:那么,Display leading zeros(零起始显示)、List separator(列表分隔符)、Measurement system(度量衡系统)这三项在哪里有对应的设置呢?
A:很抱歉,到目前为止我也找不到对应的地方来设置,如果你或者其他人找到了,请务必告诉我。(不过,你可以通过RegionInfo.IsMetric这个只读属性来检测当前系统是否使用Metric(公制)度量系统。)
Q:如果我无法推断待解析的字符串应该使用哪一个或多个NumberStyles枚举呢?
A:这种情况一般不会发生,待解析的字符串肯定是有一定的目的或用途的,这些目的或用途就是字符串字面样式的约束,而NumberStyles枚举的作用就是说明这种约束的。当然,如果你目前确实无法推断,那么你可以使用NumberStyles.Any枚举。
Q:在具体的编码过程中,我如何知道应该选择哪一种方法来解析字符串呢?
A:就我个人来说,我会:
- 首先,你得清楚你所使用的CLR版本是否提供某种方法的支持,例如,TryParse是.NET 2.0或以上才支持的。
- 其次,你可以通过方法的重载签名来选择,如果你要解析非十进制的字符串,Convert.ToInt32可能是你唯一的选择,除了十进制和十六进制,你可以用它来解析二进制、八进制,但你不能用Int32.Parse或Int32.TryParse来解释这些进制的字符串。当然如果是十进制或者十六进制就哪种方法都可以。
- 另外,如果你需要使用NumberStyles枚举来告知相关方法待解析的字符串的样式,那么Convert.ToInt32就帮不了你了,此时你应该选择Int32.Parse或者Int32.TryParse相应的重载。
- 再次,如果你不希望在你的代码里处理异常,那么Int32.TryParse可能是你唯一的选择,你可能根据它的返回至来判断解析是否成功,进而决定是否使用解析结果。但如果你希望透过异常机制通知调用方解析失败,那么Int32.Parse或者Convert.ToInt32都是不错的选择。
- 最后,选择准则不可能唯一或者通用的的,你可能会在实践总结出适合你自己的选择准则,如果你有更好的选择方法,请你也告诉我一声。
Q:在.NET里面,如果要将string解析为其它的基元类型(Primitive Type)是否也有类似的方法?
A:是的,在.NET里面,我们为这些基元类型提供了Convert.ToPrimitiveType、PrimitiveType.Parse、PrimitiveType.TryParse(.NET 2.0或以上)方法,用于把string解析成对应的基元类型,当然,你得首先保证待解析的string字面值与对应的基元类型兼容。这些基元类型是(括号中是对应的C#关键字):SByte (sbyte), Byte (byte), Int16 (short), UInt16 (ushort), Int32 (int), UInt32 (uint), Int64 (long) , UInt64 (ulong), Char (char), Single (float), Double (double) , Boolean (bool), Decimal (decimal)。另外,.NET也提供了把string解析为DateTime结构的方法,除了有上面提到的三种形式(Convert.ToDateTime、DateTime.Parse、DateTime.TryParse)之外,我们还可以使用DateTime.TryParseExact。所有的这些方法都是大同小异,差别在于对字符串字面值格式(包括其所包含符号)的规定,有兴趣的话,你可以参考以下MSDN的文档或者其他专题资料。