在Swift中,用于字符的isDigit()方法的替代方法是什么?

33

此贴中所述,在 Xcode 6 Beta 4 之前,我们可以使用 c.isDigit()c.isAlpha() 来判断字符 c: Character 是数字还是字母。该帖子提到这种方法对 ASCII 字符才有效。

我的问题是,有什么替代方法吗?除了设置一个具有 switch 语句的函数来测试包含字母和数字选项的字符,我该怎么测试一个字符是否为数字?


1
你链接的问题所接受的答案包含一个仍然有效的示例。 - Greg Hewgill
我的问题是我只有一个字符。假设我有 var c : Character 并且我想查看它是否符合 /[0-9]/。根据那个答案,我应该执行 NSCharacterSet.decimalDigitCharacterSet().longCharacterIsMember(c),但它告诉我“'Character is not convertible to 'UTF32Char'”,所以我尝试 NSCharacterSet.decimalDigitCharacterSet().longCharacterIsMember(UTF32Char(c)) 并得到了“Cannot invoke 'init' with an argument of type 'UTF32Char'” 的错误提示。 - Alex Mitchell
6个回答

76
“问题”在于,Swift 字符并不直接对应于一个 Unicode 代码点,而是表示一个“扩展字形簇”,可以由多个 Unicode 标量组成。例如:
let c : Character = "" // REGIONAL INDICATOR SYMBOL LETTERS US

"

实际上是由两个Unicode标量组成的序列。

如果我们忽略这个事实,那么您可以检索字符的初始Unicode标量(比较如何获取字符的Unicode代码点?)并测试其是否属于字符集:

"
let c : Character = "5"

let s = String(c).unicodeScalars
let uni = s[s.startIndex]

let digits = NSCharacterSet.decimalDigitCharacterSet()
let isADigit = digits.longCharacterIsMember(uni.value)

这个函数对于字符 "0" ... "9" 返回 "true",但实际上对于所有"十进制数字类别"的 Unicode 标量都返回 "true",例如:

let c1 : Character = "৯" // BENGALI DIGIT NINE U+09EF
let c2 : Character = "" // MATHEMATICAL DOUBLE-STRUCK DIGIT ONE U+1D7D9

如果你只关心(ASCII)数字“0”到“9”,那么最简单的方法可能是:
if c >= "0" && c <= "9" { }

或者,使用范围:
if "0"..."9" ~= c { }

更新:自Swift 5起,您可以使用以下方法检查ASCII数字

if c.isASCII && c.isNumber { }

使用SE-0221引入的“字符属性”解决了数字被选择U+FE0F修改的问题,例如键帽表情符号“1️⃣”。(感谢Lukas Kukacka报告此问题。)
let c: Character = "1️⃣"
print(Array(c.unicodeScalars)) // ["1", "\u{FE0F}", "\u{20E3}"]
print(c.isASCII && c.isNumber) // false

“仅 ASCII” 检查似乎并不完全可靠。对于像“1️⃣”(键盘表情符号:https://emojipedia.org/keycap-digit-one/)这样的字符,它将失败。例如,`("0"..."9").contains(Character("1️⃣"))` 返回 true,这对于简单的 ASCII 0-9 检查来说是意外的。 - Lukas Kukacka
1
@LukasKukacka:你说得对。我认为在Swift 4(或5)中比较字符串(或字符)的方式已经改变了。在Swift 5中检查ASCII数字的最简单方法是if c.isASCII && c.isNumber。我稍后会检查并相应更新答案。 - Martin R

20

使用 Swift 5,根据您的需求,您可以选择以下一种方式来解决问题。


#1. 使用 CharacterisNumber 属性

Character 有一个名为 isNumber 的属性。 isNumber 的声明如下:

var isNumber: Bool { get }

一个布尔值,表示该字符是否代表数字。

下面的Playground示例代码展示了如何使用isNumber来检查一个字符是否代表数字:

let character: Character = "9"
print(character.isNumber) // true
let character: Character = "½"
print(character.isNumber) // true
let character: Character = "④"
print(character.isNumber) // true
let character: Character = "1⃣"
print(character.isNumber) // true
let character: Character = "1️⃣"
print(character.isNumber) // true
let character: Character = "৯"
print(character.isNumber) // true
let character: Character = ""
print(character.isNumber) // true
let character: Character = "F"
print(character.isNumber) // false

#2. 使用 CharacterisWholeNumber 属性

如果您想检查一个字符是否代表整数,您可以使用 CharacterisWholeNumber 属性:

let character: Character = "9"
print(character.isWholeNumber) // true
let character: Character = "½"
print(character.isWholeNumber) // false
let character: Character = "④"
print(character.isWholeNumber) // true
let character: Character = "1⃣"
print(character.isWholeNumber) // false
let character: Character = "1️⃣"
print(character.isWholeNumber) // false
let character: Character = "৯"
print(character.isWholeNumber) // true
let character: Character = ""
print(character.isWholeNumber) // true
let character: Character = "F"
print(character.isWholeNumber) // false

#3.使用Unicode.Scalar.PropertiesgeneralCategory 属性和 Unicode.GeneralCategory.decimalNumber

以下Playground示例代码展示了如何使用generalCategoryUnicode.GeneralCategory.decimalNumber 来检查一个字符的第一个Unicode标量是否为十进制数字:

let character: Character = "9"
let scalar = character.unicodeScalars.first! // DIGIT NINE
print(scalar.properties.generalCategory == .decimalNumber) // true
let character: Character = "½"
let scalar = character.unicodeScalars.first! // VULGAR FRACTION ONE HALF
print(scalar.properties.generalCategory == .decimalNumber) // false
let character: Character = "④"
let scalar = character.unicodeScalars.first! // CIRCLED DIGIT FOUR
print(scalar.properties.generalCategory == .decimalNumber) // false
let character: Character = "1⃣"
let scalar = character.unicodeScalars.first! // DIGIT ONE
print(scalar.properties.generalCategory == .decimalNumber) // true
let character: Character = "1️⃣"
let scalar = character.unicodeScalars.first! // DIGIT ONE
print(scalar.properties.generalCategory == .decimalNumber) // true
let character: Character = "৯"
let scalar = character.unicodeScalars.first! // BENGALI DIGIT NINE
print(scalar.properties.generalCategory == .decimalNumber) // true
let character: Character = ""
let scalar = character.unicodeScalars.first! // MATHEMATICAL DOUBLE-STRUCK DIGIT ONE
print(scalar.properties.generalCategory == .decimalNumber) // true
let character: Character = "F"
let scalar = character.unicodeScalars.first! // LATIN CAPITAL LETTER F
print(scalar.properties.generalCategory == .decimalNumber) // false

#4. 使用 Unicode.Scalar.PropertiesgeneralCategory 属性和 Unicode.GeneralCategory.otherNumber

类似地,你可以使用 generalCategoryUnicode.GeneralCategory.otherNumber 来检查一个字符的第一个 Unicode 标量是否对应于 Unicode 标准中的 Other_Number 类别:

let character: Character = "9"
let scalar = character.unicodeScalars.first!
print(scalar.properties.generalCategory == .otherNumber) // false
let character: Character = "½"
let scalar = character.unicodeScalars.first!
print(scalar.properties.generalCategory == .otherNumber) // true
let character: Character = "④"
let scalar = character.unicodeScalars.first!
print(scalar.properties.generalCategory == .otherNumber) // true
let character: Character = "1⃣"
let scalar = character.unicodeScalars.first!
print(scalar.properties.generalCategory == .otherNumber) // false
let character: Character = "1️⃣"
let scalar = character.unicodeScalars.first!
print(scalar.properties.generalCategory == .otherNumber) // false
let character: Character = "৯"
let scalar = character.unicodeScalars.first!
print(scalar.properties.generalCategory == .otherNumber) // false
let character: Character = ""
let scalar = character.unicodeScalars.first!
print(scalar.properties.generalCategory == .otherNumber) // false
let character: Character = "F"
let scalar = character.unicodeScalars.first!
print(scalar.properties.generalCategory == .otherNumber) // false

#5. 使用 CharacterSetdecimalDigits 属性

作为替代方案,你可以导入 Foundation 框架,并检查 CharacterSet.decimalDigits 是否包含某个字符的第一个 Unicode 标量:

import Foundation

let character: Character = "9"
let scalar = character.unicodeScalars.first!
print(CharacterSet.decimalDigits.contains(scalar)) // true
import Foundation

let character: Character = "½"
let scalar = character.unicodeScalars.first!
print(CharacterSet.decimalDigits.contains(scalar)) // false
import Foundation

let character: Character = "④"
let scalar = character.unicodeScalars.first!
print(CharacterSet.decimalDigits.contains(scalar)) // false
import Foundation

let character: Character = "1⃣"
let scalar = character.unicodeScalars.first!
print(CharacterSet.decimalDigits.contains(scalar)) // true
import Foundation

let character: Character = "1️⃣"
let scalar = character.unicodeScalars.first!
print(CharacterSet.decimalDigits.contains(scalar)) // true
import Foundation

let character: Character = "৯"
let scalar = character.unicodeScalars.first!
print(CharacterSet.decimalDigits.contains(scalar)) // true
import Foundation

let character: Character = ""
let scalar = character.unicodeScalars.first!
print(CharacterSet.decimalDigits.contains(scalar)) // true
import Foundation

let character: Character = "F"
let scalar = character.unicodeScalars.first!
print(CharacterSet.decimalDigits.contains(scalar)) // false

#6. 使用 Unicode.Scalar.PropertiesnumericType

苹果公司的文档中指出,numericType 属性用于表示一个标量代表的数字类型。如果标量不是数字,则该属性的值为 nil

下面的示例代码显示给定字符的第一个标量可能的数字类型(decimaldigitnumeric):

let character: Character = "9"
let scalar = character.unicodeScalars.first!
print(scalar.properties.numericType) // Optional(Swift.Unicode.NumericType.decimal)
let character: Character = "½"
let scalar = character.unicodeScalars.first!
print(scalar.properties.numericType) // Optional(Swift.Unicode.NumericType.numeric)
let character: Character = "④"
let scalar = character.unicodeScalars.first!
print(scalar.properties.numericType) // Optional(Swift.Unicode.NumericType.digit)
let character: Character = "1⃣"
let scalar = character.unicodeScalars.first!
print(scalar.properties.numericType) // Optional(Swift.Unicode.NumericType.decimal)
let character: Character = "1️⃣"
let scalar = character.unicodeScalars.first!
print(scalar.properties.numericType) // Optional(Swift.Unicode.NumericType.decimal)
let character: Character = "৯"
let scalar = character.unicodeScalars.first!
print(scalar.properties.numericType) // Optional(Swift.Unicode.NumericType.decimal)
let character: Character = ""
let scalar = character.unicodeScalars.first!
print(scalar.properties.numericType) // Optional(Swift.Unicode.NumericType.decimal)
let character: Character = "F"
let scalar = character.unicodeScalars.first!
print(scalar.properties.numericType) // nil

9

Swift 3看起来更容易:

let str = "abcdef12345"
let digitSet = CharacterSet.decimalDigits

for ch in str.unicodeScalars {
    if digitSet.contains(ch) {
        // is digit!
    }
}

4

关于 Swift 3:

根据伟大的Martin R的回答,对于那些在使用新版Swift时遇到问题的人:

请将以下内容替换为:

let digits = NSCharacterSet.decimalDigitCharacterSet()

使用:

let digits = NSCharacterSet.decimalDigits as NSCharacterSet

4

对于单个字符:

CharacterSet.decimalDigits.contains(string.unicodeScalars.first!)

如果您想知道一个字符串是否包含数字,可以非常方便快捷地使用此方法。 - Inder Kumar Rathore

0

我已经创建了一个变量来检查字符是否为数字。

var isNumber : Bool {
    get{
        return !self.isEmpty && self.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted) == nil
    }
}

// 使用

var initial = "1"
if let isNum = initial.isNumber, isNum { // true
    initial = "#"
} 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接