ChatGPT解决这个技术问题 Extra ChatGPT

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

我正在尝试用 swift 语言编写一个 BMI 程序。我遇到了这个问题:如何将 String 转换为 Double?

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

double myDouble = [myString doubleValue];

但是我怎样才能在 Swift 语言中实现这一点呢?

这在 Swift 2 中发生了变化,使用新的 Double() 可失败初始化程序查看新答案:stackoverflow.com/a/32850058/276626

J
Jarsen

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

哦,贾森。这种方法的一个缺点是,如果字符串不是有效的双精度字符串,它不会失败。相反,您只会得到 0.0。也就是说,与 Double? (Optional<Double>) 相对的 - [NSString doubleValue] 的结果在 swift 中是 Double。这与 Swift 字符串函数 toInt() 不同,后者返回 Int? 并在文档中声明它“...仅接受与正则表达式“[-+]?[0-9]+”匹配的字符串。”
是的,使用 .toInt() 会更好。然而,这更像是对如何做完全相同的事情的答案,但在 Swift 中。
这种方法将您与 Foundation 紧密结合,并且可能不适用于任何非 Apple 的 Swift 实现。
在 Swift 2.0 中,现在可以使用 Double.init?(_ text: String) 可失败初始化程序。
这不是最好的方法。根据小数点分隔符,它会失败。 '.' 会很好。但不是','。 NSNumberFormatter 将支持当前本地化中使用的小数分隔符。
P
Paul Solt

Swift 4.2+ 字符串加倍

您应该使用新的类型初始化器在 String 和数字类型(Double、Float、Int)之间进行转换。它将返回一个 Optional 类型(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")

阅读我的 blog post about converting String to Double types (and currency) 的更多信息。


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

为了更加 Swift 的感觉,使用 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 似乎至少是直接的,并且更容易阅读和记忆。
实际上,使用 NSNumberFormatter() 的优点是 numberFromString: 方法将返回 nil 如果字符串包含不构成双精度的字符。 NSString 上 doubleValue 的问题在于它总是会返回一个双精度值(例如,0.0 表示“测试”)。因此,如果您正在处理各种字符串并且您想测试字符串是否只是一个双精度字符,没有额外的字符,那么使用 NSNumberFormatter 是可行的方法。
这在法语上失败了。
这是一个很好的解决方案,但是仅使用此代码在使用逗号作为分隔符的国家/地区将无法使用(包括 @PeterK. 提到的法国)。我尝试用解决方案写评论,但它太长了,所以我不得不把它作为一个答案。给你:Modified solution
K
Keith Smiley

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

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

我希望有更好的方法来做到这一点。例如,我如何知道解析是否失败。
@RyanHoffman 不幸的是, NSString doubleValue 合同从未提供过任何这些。如果字符串不是以数字的有效表示开头,那么你得到的只是 0.0,上溢/下溢时 HUGE_VAL 和 -HUGE_VAL。如果您想知道解析失败的原因,我相信您需要研究 NSNumberFormatter,它旨在以更健壮的方式处理数字/字符串转换。
由于可能很难找到该答案,因此这里是:(swiftString as NSString).doubleValue
M
Millie Smith

这是一个扩展方法,它允许您在 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
    }
}

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


r
rintaro

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

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

我喜欢这种方法,而不是强制转换为 NSString
T
Toom

在 Swift 2.0 中,最好的方法是避免像 Objective-C 开发人员那样思考。因此,您不应该“将字符串转换为 Double”,而应该“从字符串初始化 Double”。此处的 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 开发人员那样思考,而是因为这个初始化程序在早期不可用。
确实你是对的。这仅在 Swift 2.0 中可用,我会提到它 :) 但就像在其他语言中一样,您用另一个类型初始化一个类型,并且不应该期望一个类型能够转换为另一个类型。长时间的争论,但作为一名 Obj-C 开发人员,我在使用 Swift 编码时有很多不好的做法,这就是其中之一 :)
T
TomCobo

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

斯威夫特 3

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

J
Jorge Luis Jiménez

在 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


C
CW0007007

尝试这个:

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

笔记

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


bridgeToObjectiveC() 已在 Xcode 6 Beta 5 中删除。Smiley 先生的方法是我目前发现的最快的方法
i
itsji10dra

这是基于@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
}

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

斯威夫特 4

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

V
Vishal Vaghasiya

斯威夫特 4.0

尝试这个

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

A
Abhirajsinh Thakore

斯威夫特:4和5

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

String -> Int -> Double: let strNumber = "314" if let intFromString = Int(strNumber){ let dobleFromInt = Double(intFromString) print(dobleFromInt) } String -> NSString -> Double let strNumber1 = "314" let NSstringFromString = NSString(string: strNumber1) 让 doubleFromNSString = NSstringFromString.doubleValue print(doubleFromNSString)

根据您对代码的需要,随意使用它。


第一种方法对我不起作用,但是第二种方法效果很好。
@Saurabh,您能否详细说明一下您的字符串是什么。?
M
Mamazur

请在操场上查看!

let sString = "236.86"

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

顺便说一句,在我的 Xcode 中

.toDouble() - 不存在

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


F
Firo

正如已经指出的,实现这一点的最佳方法是直接铸造:

(myString as NSString).doubleValue

以此为基础,您可以制作一个漂亮的原生 Swift String 扩展:

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

这允许您直接使用:

myString.doubleValue

它将为您执行铸造。如果 Apple 确实将 doubleValue 添加到本机字符串,您只需删除扩展名,其余代码将自动编译好!


Apple 的方法将被称为 .toDouble... 与 toInt 相同。这是一个奇怪的遗漏。可能会在未来的 Swift 版本中得到纠正。我最喜欢扩展方法,保持代码干净。
返回值应该是 Double 而不是 Float
D
Daxesh Nagar

1.

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

2.

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

可能对你有帮助。


i
imike

带有可选语言环境的扩展

斯威夫特 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
        }
    }
}

我添加了 Swift 3.1 示例。您所说的“......并为语言环境添加一个示例”是什么意思? 🙂
a
aturan23

斯威夫特 3

要清楚,现在有一个默认方法:

public init?(_ text: String)` of `Double` class.

它可以用于所有课程。

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

, ETC。


j
jr00n

或者你可以这样做:

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

S
Stas

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

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

它验证字符串,如果不能转换为双精度则失败。

例子:

import StringEx

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

A
Aitor Lucas

斯威夫特 4

extension String {
    func toDouble() -> Double {
        let nsString = self as NSString
        return nsString.doubleValue
    }
}

B
Bijan

什么也有效:

// 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 中已弃用
P
Paul B

在某些情况下使用 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

自动检测语言环境没有问题。请注意,字符串“Mon salaire est 9 999,99€”中法语语言环境中的 groupingSeparator 不是空格,尽管它可能会完全呈现为空格(这里不是)。这就是为什么下面的代码在没有 !$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)

a
aturan23

在 Swift 2.0 中使用此代码

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

S
Stoull

如果字符串包含其他字符,例如:"27.8 °C""52.523553 kM""Total: 349.0"

这适用于 Swift 4:

let anyString = "52.523553 kM"
let digitsCharacterSet = CharacterSet.init(charactersIn: "0123456789.")
let doubleResult = Double(anyString.components(separatedBy:digitsCharacterSet.inverted).joined())

注意! 这不适用于包含多个 . 的字符串,例如 "27.8 °C 3.5 kM"


i
itsji10dra

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

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

然后你就可以编写你的代码:

myDouble = myString.doubleValue

M
Murat Akdeniz

我的问题是逗号,所以我这样解决:

extension String {
    var doubleValue: Double {
        return Double((self.replacingOccurrences(of: ",", with: ".") as NSString).doubleValue)
    }
}

i
itsji10dra
var stringValue = "55"

var convertToDouble = Double((stringValue as NSString).doubleValue)

A
ANIL.MUNDURU

我们可以使用由 myString.doubleValue 获得的 CDouble 值


错误:“字符串”没有名为“doubleValue”的成员
@Matthew 是的,没有名为“double value”的成员,所以我们可以使用 CDouble Value
en.wikipedia.org/wiki/… 在此处参考复数并阅读 Swift 参考文档。没有任何 CDouble...您是在谈论 Swift 还是其他语言?我的意思是没有扩展的快速?