Swift-如何将String转换为Double


241

我正在尝试用快速的语言编写BMI程序。我有一个问题:如何将字符串转换为Double?

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

double myDouble = [myString doubleValue];

但是如何用Swift语言实现呢?


1
在Swift 2中,这种情况发生了变化,请使用新的Double()故障初始化程序查看新答案:stackoverflow.com/a/32850058/276626
Paul

Answers:


205

Swift 4.2+字符串加倍

您应该使用新的类型初始值设定项在String和数字类型(Double,Float,Int)之间进行转换。它会返回一个可选类型(Double?),该类型将具有正确的值;如果String不是数字,则返回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")

在我的博客文章上了解更多有关将String转换为Double类型(和货币)的信息


2
另一个问题是,除了1,000.00以外,它不支持其他语言环境中的数字,如果您使用其他格式(例如1 000,00),则末尾将舍去两位小数。至少这是对于Objective-C的事情。-根据Apple文档:(它们不支持区域设置)以下便利方法都跳过初始空格字符(whitespaceSet)并忽略结尾字符。它们不支持区域设置。NSScanner或NSNumberFormatter可以用于功能更强大且可识别区域设置的数字。
mskw

@mskw我添加了有关使用NumberFormatter解析货币的其他详细信息。在我的代码中,我将使用if / let语法将多个格式化程序尝试转换在一起,以便我可以解析货币格式的数字($ 1,000.00)或格式的数字(1,000)。您并不总是知道用户将如何输入数字,因此使用一组if / let语句能够同时支持两者都是理想的。
保罗·索尔特

276

斯威夫特2更新 有新failable初始化,允许你这样做的更地道和安全的方式(如许多答案都注意到,因为它对于非数值返回0的NSString的双重价值也不是很安全的。这意味着,doubleValue"foo""0"是相同。)

let myDouble = Double(myString)

这将返回一个可选参数,因此,在传递将返回0的"foo"位置的情况下doubleValue,失败的初始化器将返回nil。您可以使用guardif-letmap处理Optional<Double>

原始帖子: 您不需要像接受的答案所建议的那样使用NSString构造函数。您可以像这样简单地桥接它:

(swiftString as NSString).doubleValue

5
哦,贾森。这种方法的一个缺点是,如果字符串不是有效的double值,它将不会失败。相反,您将只得到0.0。也就是说,结果- [NSString doubleValue]Double在迅速为并列于Double?Optional<Double>)。这与Swift String函数不同,toInt()后者Int?在文档中返回并声明“仅接受与正则表达式“ [-+]?[0-9] +”匹配的字符串”。
德里克·海瑟薇

是的,使用.toInt()会更好。但是,这更是对如何做完全相同的事情的答案,但是在Swift中。
贾森,2015年

这种方法将您与Foundation紧密地联系在一起,并且可能不适用于任何非Apple的Swift实施。
Erik Kerber

5
在Swift 2.0中,Double.init?(_ text: String)现在有可用的故障初始化程序。
mixel 2015年

3
这不是最好的方法。它将失败,具体取决于小数点分隔符。可以。但不能使用','。NSNumberFormatter将支持当前本地化中使用的十进制分隔符。
朱利安·科洛(JulianKról)'16

75

对于更多Swift的感觉,使用NSFormatter()避免转换为NSString,并nil在字符串不包含Double值时返回(例如“ 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似乎至少是直接的,并且更易于阅读和记住。
2014年

12
实际上,使用NSNumberFormatter()的好处是,如果字符串包含不构成双精度数的字符,则numberFromString:方法将返回nil。NSString上的doubleValue的问题在于,无论如何,它将始终返回一个double(例如,“ test”为0.0)。因此,如果您要处理各种各样的字符串,并且想测试字符串是否只是双精度字符,而没有其他字符,则可以使用NSNumberFormatter。
dirkgroten

4
这在法语上失败。
彼得·

1
这是一个很好的解决方案,但是仅使用此代码在使用逗号作为分隔符的国家(包括法国,如@PeterK。)无效。我尝试用该解决方案写评论,但由于时间太长,因此不得不将其作为答案。在这里你去:修改的方案
雅各布- [R

53

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

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

13
我希望有更好的方法可以做到这一点。例如,如何知道解析是否失败。
Ryan Hoffman 2014年

2
@RyanHoffman不幸的是,NSString doubleValue合同从未提供任何此类服务。如果字符串不是以数字的有效表示开头HUGE_VAL和-HUGE_VAL,则得到的全部为0.0。如果您想知道解析失败的原因,我相信您需要研究NSNumberFormatter,它旨在以更可靠的方式处理数字/字符串转换。
Jarsen 2014年

由于可能很难找到答案,因此,这里是:(swiftString as NSString).doubleValue
LearnCocos2D 2014年

24

这是一个扩展方法,使您可以简单地在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开始,您可以直接传递Stringconst char *parameter。

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桥接:

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 2014年

10

在Swift 2.0中,最好的方法是避免像Objective-C开发人员那样思考。因此,您不应“将字符串转换为双精度”,而应“从字符串初始化双精度”。此处的Apple文档:https : //developer.apple.com/library/ios//documentation/Swift/Reference/Swift_Double_Structure/index.html#//apple_ref/swift/structctr/Double/s : FSdcFMSdFSSGSqSd_

这是一个可选的init,因此您可以使用nil合并运算符(??)设置默认值。例:

let myDouble = Double("1.1") ?? 0.0

Double.init?(_ text: String)在Swift 2.0中可用。你应该提这个。此处的其他答案不是因为人们像Objective-C开发人员那样思考,而是因为此初始化程序较早之前不可用。
mixel 2015年

确实你是对的。这仅在Swift 2.0中可用,我将在此提及它:)但是,就像在其他语言中一样,您可以使用另一个语言初始化一个类型,并且不应期望一个类型能够转换为另一个类型。长期的辩论,但是作为Obj-C开发人员,我在使用Swift进行编码时有很多不好的做法,这就是其中之一:)
Toom 2015年

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


10

我没有看到想要的答案。我只是在这里发帖,以防它可以帮助任何人。仅当您不需要特定格式时,此答案有效。

迅捷3

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

9

试试这个:

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

注意

已在Beta 5中删除。这不再有效吗?


1
bridgeToObjectiveC()已在Xcode 6 Beta 5中删除。Mr Smiley的方法是我目前发现的最快方法
faraquet99 2014年

7

这是基于@Ryu的答案

只要您身处一个以圆点作为分隔符的国家,他的解决方案就很棒。默认情况下,NSNumberFormatter使用设备区域设置。因此,如果数字使用点作为分隔符(通常是这种情况),则在所有使用逗号作为默认分隔符的国家(包括法国,如@PeterK。),此操作将失败。将此NSNumberFormatter的语言环境设置为US,从而使用点作为分隔符替换该行

return NSNumberFormatter().numberFromString(self)?.doubleValue

let numberFormatter = NSNumberFormatter()
numberFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
return numberFormatter.numberFromString(self)?.doubleValue

因此,完整的代码变为

extension String {
    func toDouble() -> Double? {
        let numberFormatter = NSNumberFormatter()
        numberFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
        return numberFormatter.numberFromString(self)?.doubleValue
    }
}

要使用此功能,只需致电 "Your text goes here".toDouble()

这将返回一个可选 Double?

正如@Ryu提到的,您可以强制打开:

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
}

1
这是正确的答案。Swift 3版本将是 NumberFormatter().number(from: myString)?.doubleValue
nicolas leo

7

SWIFT 4

extension String {
    func toDouble() -> Double? {
        let numberFormatter = NumberFormatter()
        numberFormatter.locale = Locale(identifier: "en_US_POSIX")
        return numberFormatter.number(from: self)?.doubleValue
    }
}

6

迅捷4.0

试试这个

 let str:String = "111.11"
 let tempString = (str as NSString).doubleValue
 print("String:-",tempString)

6

斯威夫特:4和5

可能有两种方法可以做到这一点:

  1. 字符串->整数->双精度:

    let strNumber = "314"
    if let intFromString = Int(strNumber){
        let dobleFromInt = Double(intFromString)
        print(dobleFromInt)
    }
  2. 字符串-> NSString->双精度

    let strNumber1 = "314"
    let NSstringFromString = NSString(string: strNumber1)
    let doubleFromNSString = NSstringFromString.doubleValue
    print(doubleFromNSString)

根据您的代码需求随时使用它。


第一种方法对我不起作用,但是第二种方法效果很好。
萨拉巴

@Saurabh,能否请您详细说明一下您的琴弦是什么??
Abhirajsinh Thakore

5

请在操场上检查!

let sString = "236.86"

var dNumber = NSNumberFormatter().numberFromString(sString)
var nDouble = dNumber!
var eNumber = Double(nDouble) * 3.7

顺便说一句在我的Xcode

.toDouble()-不存在

.doubleValue从非数字字符串创建值0.0 ...


4

1。

let strswift = "12"
let double = (strswift as NSString).doubleValue

2。

var strswift= "10.6"
var double : Double = NSString(string: strswift).doubleValue 

可能对您有帮助。


4

如前所述,实现此目标的最佳方法是直接投射:

(myString as NSString).doubleValue

在此基础上,您可以制作一个漂亮的本机Swift String扩展:

extension String {
    var doubleValue: Double {
        return (self as NSString).doubleValue
    }
}

这使您可以直接使用:

myString.doubleValue

它将为您执行转换。如果Apple确实doubleValue向本地String 添加了a ,则只需删除扩展名,其余代码将自动编译!


苹果的方法将被称为.toDouble ...与toInt相同。这是一个奇怪的遗漏。可能会在以后的Swift版本中更正。我最喜欢扩展方法,可以保持代码整洁。
n13 2015年

返回值应为Double NOT Float
Leo Dabus

4

SWIFT 3

要清除,如今有一个默认方法:

public init?(_ text: String)Double类。

它可以用于所有类。

let c = Double("-1.0") let f = Double("0x1c.6") let i = Double("inf")


2

具有可选语言环境的扩展

斯威夫特2.2

extension String {
    func toDouble(locale: NSLocale? = nil) -> Double? {
        let formatter = NSNumberFormatter()
        if let locale = locale {
            formatter.locale = locale
        }
        return formatter.numberFromString(self)?.doubleValue
    }
}

斯威夫特3.1

extension String {
    func toDouble(_ locale: Locale) -> Double {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.locale = locale
        formatter.usesGroupingSeparator = true
        if let result = formatter.number(from: self)?.doubleValue {
            return result
        } else {
            return 0
        }
    }
}

1
我添加了Swift 3.1示例。您所说的“ ...并添加语言环境示例”是什么意思?🙂–
imike

1

或者您可以这样做:

var myDouble = Double((mySwiftString.text as NSString).doubleValue)

1

您可以使用StringEx。它扩展String了字符串到数字的转换,包括toDouble()

extension String {
    func toDouble() -> Double?
}

它验证字符串,如果无法将其转换为double则失败。

例:

import StringEx

let str = "123.45678"
if let num = str.toDouble() {
    println("Number: \(num)")
} else {
    println("Invalid string")
}


0

什么也可以:

// Init default Double variable
var scanned: Double()

let scanner = NSScanner(string: "String to Scan")
scanner.scanDouble(&scanned)

// scanned has now the scanned value if something was found.

注意:'scanDouble'在iOS 13.0中已弃用
uplearnedu.com

0

在Swift 2.0中使用此代码

let strWithFloat = "78.65" let floatFromString = Double(strWithFloat)


0

Scanner在某些情况下使用是从字符串中提取数字的一种非常方便的方法。它几乎与NumberFormatter解码和处理不同数字格式和区域设置一样强大。它可以提取具有不同小数点和组分隔符的数字和货币。

import Foundation
// The code below includes manual fix for whitespaces (for French case)
let strings = ["en_US": "My salary is $9,999.99",
               "fr_FR": "Mon salaire est 9 999,99€",
               "de_DE": "Mein Gehalt ist 9999,99€",
               "en_GB": "My salary is £9,999.99" ]
// Just for referce
let allPossibleDecimalSeparators = Set(Locale.availableIdentifiers.compactMap({ Locale(identifier: $0).decimalSeparator}))
print(allPossibleDecimalSeparators)
for str in strings {
    let locale = Locale(identifier: str.key)
    let valStr = str.value.filter{!($0.isWhitespace || $0 == Character(locale.groupingSeparator ?? ""))}
    print("Value String", valStr)

    let sc = Scanner(string: valStr)
    // we could do this more reliably with `filter` as well
    sc.charactersToBeSkipped = CharacterSet.decimalDigits.inverted
    sc.locale = locale

    print("Locale \(locale.identifier) grouping separator: |\(locale.groupingSeparator ?? "")| . Decimal separator: \(locale.decimalSeparator ?? "")")
    while !(sc.isAtEnd) {
        if let val = sc.scanDouble() {
            print(val)
        }

    }
}

但是,分隔符存在一些问题,可以将其视为字定界符。

// This doesn't work. `Scanner` just ignores grouping separators because scanner tends to seek for multiple values
// It just refuses to ignore spaces or commas for example.
let strings = ["en_US": "$9,999.99", "fr_FR": "9999,99€", "de_DE": "9999,99€", "en_GB": "£9,999.99" ]
for str in strings {
    let locale = Locale(identifier: str.key)
    let sc = Scanner(string: str.value)
    sc.charactersToBeSkipped = CharacterSet.decimalDigits.inverted.union(CharacterSet(charactersIn: locale.groupingSeparator ?? ""))
    sc.locale = locale
    print("Locale \(locale.identifier) grouping separator: \(locale.groupingSeparator ?? "") . Decimal separator: \(locale.decimalSeparator ?? "")")
    while !(sc.isAtEnd) {
        if let val = sc.scanDouble() {
            print(val)
        }

    }
}
//     sc.scanDouble(representation: Scanner.NumberRepresentation) could help if there were .currency case

自动检测语言环境没有问题。请注意,法语语言环境中的groupingSeparator字符串“ Mon salaire est 9 999,99€”不是空格,尽管它可以完全按空格显示(此处不是)。这就是为什么下面的代码可以正常工作而!$0.isWhitespace不会过滤掉字符的原因。

let stringsArr = ["My salary is $9,999.99",
                  "Mon salaire est 9 999,99€",
                  "Mein Gehalt ist 9.999,99€",
                  "My salary is £9,999.99" ]

let tagger = NSLinguisticTagger(tagSchemes: [.language], options: Int(NSLinguisticTagger.Options.init().rawValue))
for str in stringsArr {
    tagger.string = str
    let locale = Locale(identifier: tagger.dominantLanguage ?? "en")
    let valStr = str.filter{!($0 == Character(locale.groupingSeparator ?? ""))}
    print("Value String", valStr)

    let sc = Scanner(string: valStr)
    // we could do this more reliably with `filter` as well
    sc.charactersToBeSkipped = CharacterSet.decimalDigits.inverted
    sc.locale = locale

    print("Locale \(locale.identifier) grouping separator: |\(locale.groupingSeparator ?? "")| . Decimal separator: \(locale.decimalSeparator ?? "")")
    while !(sc.isAtEnd) {
        if let val = sc.scanDouble() {
            print(val)
        }

    }
}
// Also will fail if groupingSeparator == decimalSeparator (but don't think it's possible)

-1

我发现为String添加扩展更具可读性,如下所示:

extension String {
    var doubleValue: Double {
        return (self as NSString).doubleValue
    }
}

然后您可以编写代码:

myDouble = myString.doubleValue



By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.