摘要

知其然,更要知其所以然。前段時間用 String 轉換 Int 處理時,發現一種情況返回 nil,就換成 String 轉換 Double 的方式處理。今天就要來看看這種返回 nil 的情況是怎麼造成的。

當有小數的 String 文字轉換為 Int 型別時,返回的值並不是咱們想要的向下取整後的整數,而是 nil。

  1. // Int 轉換為 String
  2. let intStr = "2.78"
  3. let int = Int(intStr) // nil

為什麼是nil?今天就來解解這個疑惑。

String 轉換 Int 本質

首先com+滑鼠左鍵彈出選項,選擇jump to Definition(跳轉到定義)一波操作,來到 Int 的定義地方,直接全域性搜尋一下 String,直接看下定義。

  1. /// Creates a new integer value from the given string.
  2. ///
  3. /// The string passed as `description` may begin with a plus or minus sign
  4. /// character (`+` or `-`), followed by one or more numeric digits (`0-9`).
  5. ///
  6. /// let x = Int("123")
  7. /// // x == 123
  8. ///
  9. /// If `description` is in an invalid format, or if the value it denotes in
  10. /// base 10 is not representable, the result is `nil`. For example, the
  11. /// following conversions result in `nil`:
  12. ///
  13. /// Int(" 100") // Includes whitespace
  14. /// Int("21-50") // Invalid format
  15. /// Int("ff6600") // Characters out of bounds
  16. /// Int("10000000000000000000000000") // Out of range
  17. ///
  18. /// - Parameter description: The ASCII representation of a number.
  19. @inlinable public init?(_ description: String)

出處找到了,不想費力看註釋的,直接看我給的結論:

String 轉換為 Int 型別,傳入 Int 的 description 引數,必須是一個或者多個0-9組合的整數,整數前可以加“+”或者“-”。通俗說,這個 text 文字必須是一個整數。否則都返回 nil。

看到現在,大致可以明白了Int("2.78")為什麼是 nil。

String 轉換 Double 本質

看完String 轉換 Double 本質後,順勢也看下String 轉換 Double 本質。同樣的查詢邏輯一波操作,找到它的定義

  1. extension Double : LosslessStringConvertible {
  2. /// Creates a new instance from the given string.
  3. ///
  4. /// The string passed as `text` can represent a real number in decimal or
  5. /// hexadecimal format or special floating-point values for infinity and NaN
  6. /// ("not a number").
  7. ///
  8. /// The given string may begin with a plus or minus sign character (`+` or
  9. /// `-`). The allowed formats for each of these representations is then as
  10. /// follows:
  11. ///
  12. /// - A *decimal value* contains the significand, a sequence of decimal
  13. /// digits that may include a decimal point.
  14. ///
  15. /// let c = Double("-1.0")
  16. /// // c == -1.0
  17. ///
  18. /// let d = Double("28.375")
  19. /// // d == 28.375
  20. ///
  21. /// 此處省略 57 行註釋------------------
  22. ///
  23. /// - Parameter text: The input string to convert to a `Double` instance. If
  24. /// `text` has invalid characters or is in an invalid format, the result
  25. /// is `nil`.
  26. @inlinable public init?<S>(_ text: S) where S : StringProtocol
  27. @available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
  28. public init?(_ text: Substring)
  29. }

Double(string)中的 string 文字可以是一個10進位制、16進位制或者浮點數的(這個非常關鍵)。也可以新增“+”,“-”符號。

這裡簡單總結一下,Double 轉換為 text,並保留幾位小數的處理方法,加深一些印象

  1. let double = Double(2.7895)
  2. // double 轉換為 String
  3. print("\(double)") // 輸出 "2.7895"
  4. // 保留兩位小數
  5. print(String(format:"%.2f", double) // 輸出 "2.79"

Int("2.78") 怎麼處理,不是 nil?

看完了上面兩個轉換的定義之後,那麼是否可以組合一下,解決可能出現的 nil?那是當然。

首先將文字轉換為 Double,然後將 Double 轉換為 Int

  1. Int(Double("2.78")!) // 2

Double 轉換 Int

程式碼驗證沒有問題,那麼就看看,Double 轉換 Int 做了什麼事情。


  1. /// Creates an integer from the given floating-point value, rounding toward
  2. /// zero.
  3. ///
  4. /// Any fractional part of the value passed as `source` is removed, rounding
  5. /// the value toward zero.
  6. ///
  7. /// let x = Int(21.5)
  8. /// // x == 21
  9. /// let y = Int(-21.5)
  10. /// // y == -21
  11. ///
  12. /// - Parameter source: A floating-point value to convert to an integer.
  13. /// `source` must be representable in this type after rounding toward
  14. /// zero.
  15. public init(_ source: Double)

定義中可以看到,Double 型別的資料,經過 Int 轉換後,會生成一個只保留整數的數(小數部分略去)。所以也就支援了上節部分的處理方式。

Double 型別整數省略 .000

在看 Double 轉換 Int定義時,無意間發現一個好玩的定義,先上定義


  1. /// Creates an integer from the given floating-point value, if it can be
  2. /// represented exactly.
  3. ///
  4. /// If the value passed as `source` is not representable exactly, the result
  5. /// is `nil`. In the following example, the constant `x` is successfully
  6. /// created from a value of `21.0`, while the attempt to initialize the
  7. /// constant `y` from `21.5` fails:
  8. ///
  9. /// let x = Int(exactly: 21.0)
  10. /// // x == Optional(21)
  11. /// let y = Int(exactly: 21.5)
  12. /// // y == nil
  13. ///
  14. /// - Parameter source: A floating-point value to convert to an integer.
  15. public init?(exactly source: Double)

定義說明,可以將一個精確標示的浮點數轉換為 Int 型別。這裡的精確標示就是沒有小數的值。有了這個定義,那麼豈不是可以解決某一個應用場景了嗎?

顯示存在需要保留2位小數的文字時,當浮點數是一個沒有小數的數值,那麼就顯示整數。

  1. // old
  2. String(format: "%.2f", 2.578) // 2.58
  3. String(format: "%.2f", 2.0) // 2.00
  4. // new
  5. if Int(exactly: 2.00) != nil {
  6. "\(Int(exactly: 2.00)!)" // 2
  7. }

題外話

感謝看到這裡,感覺有一點收穫,給個小贊。有分析的不到位,評論區留言幫我梳理。

偶爾有一些想要搞清楚的問題,評論區告訴我,咱們一起解決。