Swift 2/Xcode 7更新:
从Swift 2开始,所有整数类型都有一个(可失败)的构造函数。
init?(_ text: String, radix: Int = default)
这个方法替换了String
的toInt()
方法,因此不再需要特定的自定义代码来完成此任务:
print(UInt("1234")) // Optional(1234)
// This is UInt.max on a 64-bit platform:
print(UInt("18446744073709551615")) // Optional(18446744073709551615)
print(UInt("18446744073709551616")) // nil (overflow)
print(UInt("1234x")) // nil (invalid character)
print(UInt("-12")) // nil (invalid character)
Swift 1.x的旧答案:
这看起来有点复杂,但应该适用于UInt
完整范围内的所有数字,并且可以正确检测所有可能的错误(例如溢出或尾随无效字符):
extension String {
func toUInt() -> UInt? {
if contains(self, "-") {
return nil
}
return self.withCString { cptr -> UInt? in
var endPtr : UnsafeMutablePointer<Int8> = nil
errno = 0
let result = strtoul(cptr, &endPtr, 10)
if errno != 0 || endPtr.memory != 0 {
return nil
} else {
return result
}
}
}
}
备注:
用于转换的BSD库函数strtoul
。
endPtr
设置为输入字符串中第一个 "无效字符",因此如果所有字符都可以转换,则必须保持endPtr.memory == 0
。
在转换错误的情况下,全局变量errno
被设置为非零值(例如,对于溢出,ERANGE
)。
检查负号是必要的,因为strtoul()
接受负数(这些负数被转换为具有相同位模式的无符号数)。
当传递给需要char *
参数的函数时,Swift字符串会在“幕后”转换为C字符串,因此人们可能会尝试调用strtoul(self, &endPtr, 0)
(这就是我在答案的第一个版本中所做的)。问题在于自动创建的C字符串仅是临时的,并且在strtoul()
返回时可能已经无效,因此endPtr
不再指向输入字符串中的字符。当我在Playground中测试代码时,就出现了这种情况。使用self.withCString { ... }
则不会出现此问题,因为C字符串在闭包的执行期间有效。
一些测试:
println("1234".toUInt()) // Optional(1234)
// This is UInt.max on a 64-bit platform:
println("18446744073709551615".toUInt()) // Optional(18446744073709551615)
println("18446744073709551616".toUInt()) // nil (overflow)
println("1234x".toUInt()) // nil (invalid character)
println("-12".toUInt()) // nil (invalid character)
let uIntString = "4"
let nonUIntString = "foo"
extension String {
func toUInt() -> UInt? {
let scanner = NSScanner(string: self)
var u: UInt64 = 0
if scanner.scanUnsignedLongLong(&u) && scanner.atEnd {
return UInt(u)
}
return nil
}
}
uIntString.toUInt() // Optional(4)
nonUIntString.toUInt() // nil
toInt()
函数的行为不匹配,即 "4blah".toInt()
返回 nil
,但 "4blah".toUInt()
返回 4
。 - Airspeed Velocity&& scanner.atEnd
轻松解决。另一个问题是没有检测到溢出。 - Martin RDecimal
并进行适当的检查来添加 overflow 检测。这个快速解决方案可以适用于大多数常见情况,避免了 !
崩溃。 - Matteo PiomboNSScanner
会默默地吞掉溢出的部分,这也太不友好了吧。 - Airspeed Velocity!
来完成此操作,以免导致崩溃。toInt
后面附加map
,就可以将其转换为UInt
可选项。let str = "4"
let myUInt = str.toInt().flatMap { $0 < 0 ? nil : UInt($0) }
然后在myUInt
上使用常规展开技术。
如果您发现自己需要经常这样做:
extension String {
func toUInt() -> UInt? {
return self.toInt().flatMap { $0 < 0 ? nil : UInt($0) }
}
}
let str = "-4"
if let myUInt = str.toUInt() {
println("yay, \(myUInt)")
}
else {
println("nuh-uh")
}
编辑:正如@MartinR所指出的那样,虽然安全,但这并不能提取Int
无法覆盖的所有可能值的完整范围,请参见其他两个答案。
NSScanner
解决方案就是正确的选择。 - Airspeed Velocitylet string = "123"
let number = UInt(string) //here number is of type *optional UInt*
//Forced Unwrapping
if number != nil {
//converted to type UInt
}
只需使用UInt的init方法:
let someString = "4"
UInt(someString.toInt()!) // 4