知其然,更要知其所以然。前段时间用 String 转换 Int 处理时,发现一种情况返回 nil,就换成 String 转换 Double 的方式处理。今天就要来看看这种返回 nil 的情况是怎么造成的。
当有小数的 String 文本转换为 Int 类型时,返回的值并不是咱们想要的向下取整后的整数,而是 nil。
// Int 转换为 String
let intStr = "2.78"
let int = Int(intStr) // nil
为什么是nil?今天就来解解这个疑惑。
String 转换 Int 本质
首先com+鼠标左键
弹出选项,选择jump to DeFinition
(跳转到定义)一波操作,来到 Int 的定义地方,直接全局搜索一下 String
,直接看下定义。
/// Creates a new integer value from the given string.
///
/// The string passed as `description` may begin with a plus or minus sign
/// character (`+` or `-`),followed by one or more numeric digits (`0-9`).
///
/// let x = Int("123")
/// // x == 123
///
/// If `description` is in an invalid format,or if the value it denotes in
/// base 10 is not representable,the result is `nil`. For example,the
/// following conversions result in `nil`:
///
/// Int(" 100") // Includes whitespace
/// Int("21-50") // Invalid format
/// Int("ff6600") // Characters out of bounds
/// Int("10000000000000000000000000") // Out of range
///
/// - Parameter description: The ASCII representation of a number.
@inlinable public init?(_ description: String)
出处找到了,不想费力看注释的,直接看我给的结论:
String 转换为 Int 类型,传入 Int 的 description 参数,必须是一个或者多个0-9组合的整数,整数前可以加“+”或者“-”。通俗说,这个 text 文本必须是一个整数。否则都返回 nil。
看到现在,大致可以明白了Int("2.78")
为什么是 nil。
String 转换 Double 本质
看完String 转换 Double 本质后,顺势也看下String 转换 Double 本质。同样的查找逻辑一波操作,找到它的定义
extension Double : LosslessstringConvertible {
/// Creates a new instance from the given string.
///
/// The string passed as `text` can represent a real number in decimal or
/// hexadecimal format or special floating-point values for infinity and NaN
/// ("not a number").
///
/// The given string may begin with a plus or minus sign character (`+` or
/// `-`). The allowed formats for each of these representations is then as
/// follows:
///
/// - A *decimal value* contains the significand,a sequence of decimal
/// digits that may include a decimal point.
///
/// let c = Double("-1.0")
/// // c == -1.0
///
/// let d = Double("28.375")
/// // d == 28.375
///
/// 此处省略 57 行注释------------------
///
/// - Parameter text: The input string to convert to a `Double` instance. If
/// `text` has invalid characters or is in an invalid format,the result
/// is `nil`.
@inlinable public init?<S>(_ text: S) where S : StringProtocol
@available(macOS 11.0,iOS 14.0,watchOS 7.0,tvOS 14.0,*)
public init?(_ text: Substring)
}
Double(string)
中的 string 文本可以是一个10进制、16进制或者浮点数的数(这个非常关键)。也可以添加“+”,“-”符号。
这里简单总结一下,Double 转换为 text,并保留几位小数的处理方法,加深一些印象
let double = Double(2.7895)
// double 转换为 String
print("\(double)") // 输出 "2.7895"
// 保留两位小数
print(String(format:"%.2f",double) // 输出 "2.79"
Int("2.78") 怎么处理,不是 nil?
看完了上面两个转换的定义之后,那么是否可以组合一下,解决可能出现的 nil?那是当然。
首先将文本转换为 Double,然后将 Double 转换为 Int。
Int(Double("2.78")!) // 2
Double 转换 Int
代码验证没有问题,那么就看看,Double 转换 Int 做了什么事情。
/// Creates an integer from the given floating-point value,rounding toward
/// zero.
///
/// Any fractional part of the value passed as `source` is removed,rounding
/// the value toward zero.
///
/// let x = Int(21.5)
/// // x == 21
/// let y = Int(-21.5)
/// // y == -21
///
/// - Parameter source: A floating-point value to convert to an integer.
/// `source` must be representable in this type after rounding toward
/// zero.
public init(_ source: Double)
定义中可以看到,Double 类型的数据,经过 Int 转换后,会生成一个只保留整数的数(小数部分略去)。所以也就支持了上节部分的处理方式。
Double 类型整数省略 .000
在看 Double 转换 Int定义时,无意间发现一个好玩的定义,先上定义
/// Creates an integer from the given floating-point value,if it can be
/// represented exactly.
///
/// If the value passed as `source` is not representable exactly,the result
/// is `nil`. In the following example,the constant `x` is successfully
/// created from a value of `21.0`,while the attempt to initialize the
/// constant `y` from `21.5` fails:
///
/// let x = Int(exactly: 21.0)
/// // x == Optional(21)
/// let y = Int(exactly: 21.5)
/// // y == nil
///
/// - Parameter source: A floating-point value to convert to an integer.
public init?(exactly source: Double)
定义说明,可以将一个精确标示的浮点数转换为 Int 类型。这里的精确标示就是没有小数的值。有了这个定义,那么岂不是可以解决某一个应用场景了吗?
显示存在需要保留2位小数的文本时,当浮点数是一个没有小数的数值,那么就显示整数。
// old
String(format: "%.2f",2.578) // 2.58
String(format: "%.2f",2.0) // 2.00
// new
if Int(exactly: 2.00) != nil {
"\(Int(exactly: 2.00)!)" // 2
}
题外话
感谢看到这里,感觉有一点收获,给个小赞。有分析的不到位,评论区留言帮我梳理。