Swift - 如何将字符串转换为双精度浮点数

302

我正试图用Swift语言编写一个BMI计算程序。

但是我遇到了这个问题:如何将字符串转换为Double类型?

在Objective-C中,我可以这样做:

double myDouble = [myString doubleValue];

但是在Swift语言中,我应该如何实现?


1
Swift 2 中这已经发生了改变,请参考使用新的 Double() 可失败初始化程序的新答案:https://dev59.com/6mAg5IYBdhLWcg3wDHQ3#32850058 - Paul Solt
31个回答

302

Swift 2 更新 有新的可失败初始化器,可以让您以更习惯化和安全的方式执行此操作(正如许多答案所指出的那样,NSString的double值不太安全,因为它对非数字值返回0。 这意味着"foo""0"doubleValue是相同的。)

let myDouble = Double(myString)
这会返回一个可选项,所以在传递类似"foo"这样的值时,doubleValue将返回0,在这种情况下,可失败的初始化程序将返回nil。您可以使用guardif-letmap来处理Optional<Double>原始帖子: 您无需像被接受的答案中提出的那样使用NSString构造函数。您可以简单地进行桥接,如下所示:
(swiftString as NSString).doubleValue

5
嗨,Jarsen。这种方法的一个缺点是,如果字符串不是有效的浮点数,它将不会失败。相反,你将只得到0.0。也就是说,- [NSString doubleValue] 的结果在Swift中是 Double 而非 Double? (Optional<Double>)。这与Swift字符串函数 toInt() 不同,后者返回 Int? 并在文档中说明它只接受与正则表达式 "[-+]?[0-9]+" 匹配的字符串。 - Derrick Hathaway
这种方法与Foundation紧密耦合,很可能在Swift的非苹果实现上无法工作。 - Erik Kerber
5
在Swift 2.0中,现在有一个可失败的初始化器Double.init?(_ text: String) - mixel
我已经添加了一个使用Double可失败初始化程序的Swift 2答案,链接如下:https://dev59.com/6mAg5IYBdhLWcg3wDHQ3#32850058 - Paul Solt
4
这不是最佳的方法。它会根据小数分隔符而失败。使用'.'是可以的,但是使用','则不行。NSNumberFormatter将支持当前本地化所使用的小数分隔符。 - Julian
显示剩余2条评论

281

Swift 4.2+ 字符串转浮点数

你应该使用新的类型初始化器来在字符串和数字类型(Double、Float、Int)之间进行转换。它会返回一个可选类型(Double?),如果字符串不是数字,则该类型将具有正确的值或nil。

注意:不推荐使用NSString的doubleValue属性,因为如果值不能被转换(即:用户输入错误),它将返回0。

let lessPrecisePI = Float("3.14")

let morePrecisePI = Double("3.1415926536")
let invalidNumber = Float("alphabet") // nil, not a valid number

使用if/let解包值并使用它们

if let cost = Double(textField.text!) {
    print("The user entered a value price of \(cost)")
} else {
    print("Not a valid number: \(textField.text!)")
}

您可以使用NumberFormatter类来转换格式化的数字和货币。

let formatter = NumberFormatter()
formatter.locale = Locale.current // USA: Locale(identifier: "en_US")
formatter.numberStyle = .decimal
let number = formatter.number(from: "9,999.99")

货币格式

let usLocale = Locale(identifier: "en_US")
let frenchLocale = Locale(identifier: "fr_FR")
let germanLocale = Locale(identifier: "de_DE")
let englishUKLocale = Locale(identifier: "en_GB") // United Kingdom
formatter.numberStyle = .currency

formatter.locale = usLocale
let usCurrency = formatter.number(from: "$9,999.99")

formatter.locale = frenchLocale
let frenchCurrency = formatter.number(from: "9999,99€")
// Note: "9 999,99€" fails with grouping separator
// Note: "9999,99 €" fails with a space before the €

formatter.locale = germanLocale
let germanCurrency = formatter.number(from: "9999,99€")
// Note: "9.999,99€" fails with grouping separator

formatter.locale = englishUKLocale
let englishUKCurrency = formatter.number(from: "£9,999.99")

阅读我的博客文章,了解如何将字符串转换为Double类型(以及货币)


2
另一个问题是它不支持除了1,000.00之外的不同地区的数字,如果使用其他格式比如1 000,00,它会在末尾截断2个小数位。至少Objective-C是这样的。根据Apple文档:以下便利方法都跳过初始空格字符(whitespaceSet)并忽略尾随字符。它们不具有区域设置感知能力。NSScanner或NSNumberFormatter可用于更强大和区域设置感知的数字解析。 - mskw
@mskw 我已经添加了有关使用NumberFormatter解析货币的详细信息。在我的代码中,我将串联多个格式化程序尝试使用if/let语法进行转换,以便我可以解析格式化的货币数字($1,000.00)或格式化的数字(1,000)。您并不总是知道用户如何输入数字,因此能够支持两者是理想的,使用一组if/let语句即可实现。 - Paul Solt
应该使用 Double(myString) ?? 0.0 来避免初始化错误。 - Rami Alloush
我想在if/let或guard let中使用表达式,并具有适当的逻辑。这样,您可以显示有关格式不正确的适当用户面向消息。如果您只将其视为0,则可能会使用户感到困惑。 - Paul Solt

86
为了更快速的处理,可以使用NSFormatter()而不是强制转换为NSString,当字符串中不包含Double值时返回nil(例如"test"不会返回0.0)。
let double = NSNumberFormatter().numberFromString(myString)?.doubleValue

或者,扩展Swift的String类型:

extension String {
    func toDouble() -> Double? {
        return NumberFormatter().number(from: self)?.doubleValue
    }
}

并像使用 toInt() 一样使用它:

var myString = "4.2"
var myDouble = myString.toDouble()

这将返回一个可选的 Double?,需要进行解包。

可以通过强制解包来实现:

println("The value is \(myDouble!)") // prints: The value is 4.2

或者使用if let语句:
if let myDouble = myDouble {
    println("The value is \(myDouble)") // prints: The value is 4.2
}

更新: 对于本地化,可以按照以下方式将区域设置应用到NSFormatter中:

let formatter = NSNumberFormatter()
formatter.locale = NSLocale(localeIdentifier: "fr_FR")
let double = formatter.numberFromString("100,25")

最后,如果你要处理的是包含货币符号的货币字符串,你可以在格式化器上使用 NSNumberFormatterCurrencyStyle


我喜欢为将在多个地方使用转换的应用程序扩展String类的想法。但是,我不认为调用NSNumberFormatter的复杂性有什么好处,因为将其强制转换为NSString并使用.doubleValue似乎至少同样直接,更易于阅读和记忆。 - clearlight
12
使用NSNumberFormatter()的优点在于,如果字符串包含不属于double类型的字符,则numberFromString:方法将返回nil。而NSString上的doubleValue存在问题,它总是返回一个double(例如对于"test"返回0.0),无论如何都会返回一个double。因此,如果您正在处理各种字符串,并且想要测试该字符串是否只是一个double,没有额外的字符,那么使用NSNumberFormatter是正确的选择。 - dirkgroten
4
这在法语上失败了。 - Peter K.
2
这是一个很好的解决方案,但仅使用此代码,在使用逗号作为分隔符的国家中将无法正常工作(包括@PeterK提到的法国)。我尝试使用该解决方案编写评论,但它太长了,所以我不得不将其作为答案。请看:修改后的解决方案 - Jacob R

55

这里的另一个选项是将其转换为NSString并使用它:

let string = NSString(string: mySwiftString)
string.doubleValue

13
我希望有一种更好的方法来做这件事。例如,我怎么知道解析失败了呢? - Ryan Hoffman
2
很遗憾,NSString的doubleValue合约从未提供任何这方面的信息。如果字符串不以有效数字表示开始,则只会得到0.0,在溢出/下溢时为HUGE_VAL和-HUGE_VAL。如果您想知道解析失败的原因,我认为您需要查看NSNumberFormatter,它被设计为以更健壮的方式处理数字/字符串转换。 - Jarsen
由于可能很难找到答案,这里是:(swiftString as NSString).doubleValue - CodeSmile

25
这是一个扩展方法,可以让你在 Swift 字符串上简单地调用 doubleValue() 并获得一个双精度浮点数返回(示例输出在前)。

这里是一个扩展方法,允许你在 Swift 字符串上调用 doubleValue() 方法并返回一个双精度浮点数(示例输出先显示)

println("543.29".doubleValue())
println("543".doubleValue())
println(".29".doubleValue())
println("0.29".doubleValue())

println("-543.29".doubleValue())
println("-543".doubleValue())
println("-.29".doubleValue())
println("-0.29".doubleValue())

//prints
543.29
543.0
0.29
0.29
-543.29
-543.0
-0.29
-0.29

这里是扩展方法:

extension String {
    func doubleValue() -> Double
    {
        let minusAscii: UInt8 = 45
        let dotAscii: UInt8 = 46
        let zeroAscii: UInt8 = 48

        var res = 0.0
        let ascii = self.utf8

        var whole = [Double]()
        var current = ascii.startIndex

        let negative = current != ascii.endIndex && ascii[current] == minusAscii
        if (negative)
        {
            current = current.successor()
        }

        while current != ascii.endIndex && ascii[current] != dotAscii
        {
            whole.append(Double(ascii[current] - zeroAscii))
            current = current.successor()
        }

        //whole number
        var factor: Double = 1
        for var i = countElements(whole) - 1; i >= 0; i--
        {
            res += Double(whole[i]) * factor
            factor *= 10
        }

        //mantissa
        if current != ascii.endIndex
        {
            factor = 0.1
            current = current.successor()
            while current != ascii.endIndex
            {
                res += Double(ascii[current] - zeroAscii) * factor
                factor *= 0.1
                current = current.successor()
           }
        }

        if (negative)
        {
            res *= -1;
        }

        return res
    }
}

没有错误检查,但如果需要可以添加它。


20

从Swift 1.1开始,您可以直接将String传递给const char *参数。

import Foundation

let str = "123.4567"
let num = atof(str) // -> 123.4567

atof("123.4567fubar") // -> 123.4567

如果您不喜欢已经废弃的 atof 函数:

strtod("765.4321", nil) // -> 765.4321

一个警告:转换的行为与NSString.doubleValue不同。

atofstrtod 接受以0x为前缀的十六进制字符串:

atof("0xffp-2") // -> 63.75
atof("12.3456e+2") // -> 1,234.56
atof("nan") // -> (not a number)
atof("inf") // -> (+infinity)

如果您喜欢 .doubleValue 的行为,我们仍然可以使用 CFString bridging:

let str = "0xff"
atof(str)                      // -> 255.0
strtod(str, nil)               // -> 255.0
CFStringGetDoubleValue(str)    // -> 0.0
(str as NSString).doubleValue  // -> 0.0

我喜欢这种方法,而不是将其转换为 NSString - Sam Soffes

12

我没有看到我想要的答案。我在这里发布我的答案,以帮助其他人。只有当您不需要特定格式时,此答案才有效。

Swift 3

extension String {
    var toDouble: Double {
        return Double(self) ?? 0.0
    }
}

11
在Swift 2.0中,最好的方法是避免像Objective-C开发人员一样思考。因此,您不应该“将字符串转换为双精度浮点数”,而应该“从字符串初始化一个双精度浮点数”。苹果文档在这里:https://developer.apple.com/library/ios//documentation/Swift/Reference/Swift_Double_Structure/index.html#//apple_ref/swift/structctr/Double/s:FSdcFMSdFSSGSqSd_ 这是一个可选的初始化方法,所以你可以使用nil合并运算符(??)来设置默认值。例如:
let myDouble = Double("1.1") ?? 0.0

Double.init?(_ text: String) 在 Swift 2.0 中可用。你应该提到这一点。其他答案之所以没有提到,不是因为人们像 Objective-C 开发者那样思考,而是因为此初始化程序早期不可用。 - mixel
确实,你说得对。这只在Swift 2.0中可用,我会提到它的 :) 但是像其他语言一样,您可以使用另一个类型来初始化类型,并且不应该期望一种类型能够转换为另一种类型。长时间的辩论,但作为Obj-C开发人员,我在使用Swift编码时有很多不良习惯,这就是其中之一 :) - Toom

10
SWIFT 3中,你可以使用:
if let myDouble = NumberFormatter().number(from: yourString)?.doubleValue {
   print("My double: \(myDouble)")
}

注意: - 如果一个字符串包含有除了数字、本地适用的分组或小数分隔符之外的字符,则解析会失败。 - 一个字符串中前导或尾随的空格分隔符字符都会被忽略。例如,“5”,“ 5” 和 “5 ”都可以生成数字 5。
摘自文档: https://developer.apple.com/reference/foundation/numberformatter/1408845-number

9

试试这个:

   var myDouble = myString.bridgeToObjectiveC().doubleValue
   println(myDouble)

注意:

Beta 5版本已将其移除,不再可用。


1
bridgeToObjectiveC() 在 Xcode 6 Beta 5 中已被移除。目前,我发现 Smiley 先生的方法是最快的。 - faraquet99

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