Swift - Como converter String para Double


242

Estou tentando escrever um programa de IMC em linguagem rápida. E eu tenho esse problema: como converter uma String para um Double?

No Objective-C, posso fazer assim:

double myDouble = [myString doubleValue];

Mas como posso conseguir isso na linguagem Swift?


1
Isso mudou no Swift 2, veja a nova resposta usando o novo inicializador disponível Double (): stackoverflow.com/a/32850058/276626
— Paul Solt

Respostas:


205

Swift 4.2+ String para Double

Você deve usar os novos inicializadores de tipo para converter entre String e tipos numéricos (Double, Float, Int). Ele retornará um tipo Opcional (Duplo?) Que terá o valor correto ou nulo se a String não for um número.

Nota: A propriedade NSString doubleValue não é recomendada porque retorna 0 se o valor não puder ser convertido (por exemplo: entrada incorreta do usuário).

let lessPrecisePI = Float("3.14")

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

Desembrulhe os valores para usá-los usando 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!)")
}

Você pode converter números e moedas formatados usando a NumberFormatterclasse

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

Formatos de moeda

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")

Leia mais no meu post sobre como converter tipos String para Double (e moeda) .


2
Outra questão é que ele não suporta números em códigos de idioma diferentes, além dos 1.000,00. Se você usar outros formatos como 1 000,00, cortará as 2 casas decimais no final. Pelo menos é o que está acontecendo no Objective-C. - De acordo com a documentação da Apple: (Eles não reconhecem a localidade) Os métodos de conveniência a seguir ignoram os caracteres de espaço inicial (whitespaceSet) e ignoram os caracteres finais. Eles não reconhecem a localidade. O NSScanner ou NSNumberFormatter pode ser usado para uma análise de números mais poderosa e com reconhecimento de localidade.
— mskw

@mskw Adicionei detalhes adicionais sobre o uso do NumberFormatter para analisar a moeda. No meu código, reunirei várias tentativas do formatador de converter usando a sintaxe if / let, para poder analisar números formatados em moeda (US $ 1.000,00) ou números formatados (1.000). Você nem sempre sabe como o usuário digitará números; portanto, poder oferecer suporte a ambos é ideal com um grupo de instruções if / let.
— Paul Solt 30/10

276

Swift 2 Update Há novos initializers failable que permitem que você faça isso de maneira mais idiomática e segura (como muitas respostas têm notado, duplo valor de NSString não é muito seguro porque ele retorna 0 para valores não numéricos. Isto significa que o doubleValuedos "foo"e "0"são o mesmo.)

let myDouble = Double(myString)

Isso retorna um opcional; portanto, em casos como a passagem de "foo"onde doubleValueretornaria 0, o inicializador disponível retornará nil. Você pode usar um guard, if-letou mappara lidar com oOptional<Double>

Postagem original: você não precisa usar o construtor NSString como a resposta aceita propõe. Você pode simplesmente fazer a ponte dessa maneira:

(swiftString as NSString).doubleValue

5
ohai, Jarsen. Uma desvantagem dessa abordagem é que ela não falhará se a sequência não for um duplo válido. Em vez disso, você receberá apenas 0,0. Ou seja, o resultado de - [NSString doubleValue]é Doublerápido, ao contrário de Double?( Optional<Double>). Isso é diferente da função Swift String, toInt()que retorna Int?e declara nos documentos que "... aceita strings que correspondem à expressão regular" [- +]? [0-9] + "only".
— Derrick Hathaway

Sim, usar .toInt()seria melhor. No entanto, essa foi mais uma resposta de como fazer exatamente a mesma coisa, mas em Swift.
— Jarsen

Essa abordagem o une fortemente à Foundation e provavelmente não funcionará em nenhuma implementação do Swift que não seja da Apple.
— Erik Kerber

5
No Swift 2.0, existe Double.init?(_ text: String)um inicializador disponível agora disponível.
— mixel

3
Esta não é a melhor abordagem. Falhará dependendo do separador decimal. Vai ficar tudo bem com '.' mas não com ','. NSNumberFormatter oferecerá suporte ao separador decimal usado na localização atual.
— Julian Król

75

Para uma sensação um pouco mais rápida, o uso NSFormatter()evita a conversão para NSStringe retorna nilquando a sequência não contém um Doublevalor (por exemplo, "teste" não retornará 0.0).

let double = NSNumberFormatter().numberFromString(myString)?.doubleValue

Como alternativa, estendendo o Stringtipo de Swift :

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

e use-o como toInt():

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

Isso retorna um opcional Double?que precisa ser desembrulhado.

Com desembrulhamento forçado:

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

ou com uma declaração if let:

if let myDouble = myDouble {
    println("The value is \(myDouble)") // prints: The value is 4.2
}

Atualização: para localização, é muito fácil aplicar localizações ao NSFormatter da seguinte maneira:

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

Por fim, você pode usar NSNumberFormatterCurrencyStyleo formatador se estiver trabalhando com moedas em que a sequência contém o símbolo da moeda.


Gosto da idéia de estender a classe String para aplicativos que usarão a conversão em vários locais. Mas não vejo o benefício da complexidade de chamar NSNumberFormatter, já que a conversão para NSString e o uso de .doubleValue parecem ser pelo menos tão diretos e mais fáceis de ler e lembrar.
— Clearlight 03/11/19

12
Na verdade, a vantagem de usar NSNumberFormatter () é que o método numberFromString: retornará nulo se a string contiver caracteres que não compõem um duplo. O problema com o doubleValue no NSString é que ele sempre retornará um duplo (0,0 para "teste" por exemplo) independentemente. Portanto, se você está lidando com todos os tipos de cadeias e deseja testar se a cadeia é apenas um duplo, sem caracteres adicionais, usar o NSNumberFormatter é o caminho a seguir.
— dirkgroten

4
Isso falha em francês.
— Peter K.

1
Essa é uma ótima solução, mas, usando apenas esse código, não funcionará em países onde vírgulas são usadas como separadores (incluindo a França como o @PeterK. Mencionado). Tentei escrever um comentário com a solução, mas era muito longo, então tive que fazer uma resposta. Aqui está: Solução modificada
— Jacob R

53

Outra opção aqui é converter isso em um NSStringe usar o seguinte:

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

13
Eu gostaria que houvesse uma maneira melhor de fazer isso. Como posso saber se a análise falhou, por exemplo.
— Ryan Hoffman

2
@RyanHoffman Infelizmente, o contrato NSString doubleValue nunca forneceu nada disso. Tudo o que você obtém é 0,0 se a sequência não começar com uma representação válida de um número, HUGE_VAL e -HUGE_VAL no estouro / estouro. Se você quiser saber por que a análise falhou, acredito que você precisa examinar o NSNumberFormatter, que foi projetado para lidar com a conversão de número / string de uma maneira mais robusta.
— Jarsen 10/10

Como pode ser difícil encontrar essa resposta, aqui está:(swiftString as NSString).doubleValue
— LearnCocos2D

24

Aqui está um método de extensão que permite que você simplesmente chame doubleValue () em uma string Swift e receba um retorno duplo (a saída de exemplo vem primeiro)

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

Aqui está o método de extensão:

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
    }
}

Nenhuma verificação de erro, mas você pode adicioná-lo se precisar.


20

A partir do Swift 1.1, você pode passar diretamente Stringpara o const char *parâmetro

import Foundation

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

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

Se você não gostar do uso suspenso atof:

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

Uma ressalva: o comportamento da conversão é diferente de NSString.doubleValue.

atofe strtodaceite a 0xsequência hexadecimal prefixada:

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

Se você preferir .doubleValuecomportamento, ainda podemos usar a CFStringponte:

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

Sou fã dessa abordagem em vez de usar o recurso NSString.
— Sam Soffes

10

No Swift 2.0, a melhor maneira é evitar pensar como um desenvolvedor de Objective-C. Portanto, você não deve "converter uma String em um Double", mas "inicializar um Double a partir de uma String". Documento da Apple aqui: https://developer.apple.com/library/ios//documentation/Swift/Reference/Swift_Double_Structure/index.html#//apple_ref/swift/structctr/Double/s:FSdcFMSdFSSGSqSd_

É um init opcional para que você possa usar o operador coalescente nulo (??) para definir um valor padrão. Exemplo:

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

Double.init?(_ text: String)tornar-se disponível no Swift 2.0. Você deveria mencionar isso. Outras respostas aqui não são porque as pessoas pensam como desenvolvedores do Objective-C, mas porque esse inicializador não estava disponível anteriormente.
— mixel

Na verdade você está certo. Isso está disponível apenas no Swift 2.0, vou mencionar :) Mas, como em outros idiomas, você inicializa um tipo com outro e não deve esperar que um tipo possa ser convertido em outro. Debate longo, mas como um desenvolvedor de Obj-C Eu tenho muitas más práticas de codificação quando com Swift e este é um deles :)
— Toom

10

No SWIFT 3 , você pode usar:

if let myDouble = NumberFormatter().number(from: yourString)?.doubleValue {
   print("My double: \(myDouble)")
}

Nota: - Se uma sequência contiver caracteres que não sejam dígitos numéricos ou grupos apropriados para localidade ou separadores decimais, a análise falhará. - Qualquer caractere separador de espaço à esquerda ou à direita em uma sequência é ignorado. Por exemplo, as sequências "5", "5" e "5" produzem o número 5.

Retirado da documentação: https://developer.apple.com/reference/foundation/numberformatter/1408845-number


10

Não vi a resposta que estava procurando. Acabei de publicar aqui o meu, para o caso de ajudar alguém. Esta resposta é válida apenas se você não precisar de um formato específico .

Swift 3

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

9

Tente o seguinte:

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

NOTA

Removido no Beta 5. Isso não funciona mais?


1
bridgeToObjectiveC () foi removido no Xcode 6 Beta método 5. do Sr. Smiley é o mais rápido que eu encontrei no momento
— faraquet99

7

Isso se baseia na resposta de @Ryu

A solução dele é ótima, desde que você esteja em um país onde pontos são usados ​​como separadores. Por padrão, NSNumberFormatterusa o código de idioma do dispositivo. Portanto, isso falhará em todos os países onde uma vírgula é usada como separador padrão (incluindo a França como @PeterK. Mencionado) se o número usar pontos como separadores (o que normalmente é o caso). Para definir o código de idioma deste NSNumberFormatter como US e, assim, usar pontos como separadores, substitua a linha

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

com

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

Portanto, o código completo se torna

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

Para usar isso, basta ligar "Your text goes here".toDouble()

Isso retornará um opcional Double?

Como o @Ryu mencionou, você pode forçar o desembrulho:

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

ou use uma if letdeclaração:

if let myDouble = myDouble {
    println("The value is \(myDouble)") // prints: The value is 4.2
}

1
Esta é a resposta certa. A versão Swift 3 será 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

Swift 4.0

tente isso

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

6

Swift: 4 e 5

Existem duas maneiras possíveis de fazer isso:

  1. String -> Int -> Duplo:

    let strNumber = "314"
    if let intFromString = Int(strNumber){
        let dobleFromInt = Double(intFromString)
        print(dobleFromInt)
    }
  2. String -> NSString -> Duplo

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

Use-o como quiser, de acordo com a necessidade do código.


O primeiro método não funciona para mim, no entanto, o segundo funciona muito bem.
— Saurabh

@Saurabh, você pode, por favor, elaborar mais sobre Qual foi a sua string?
— Abhirajsinh Thakore 13/11/19

5

Por favor, verifique no playground!

let sString = "236.86"

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

A propósito, no meu Xcode

.toDouble () - não existe

.doubleValue cria valor 0.0 a partir de cadeias não numéricas ...


4

1

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

2)

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

Pode ser essa ajuda para você.


4

Como já apontado, a melhor maneira de conseguir isso é com o casting direto:

(myString as NSString).doubleValue

Com base nisso, é possível criar uma extensão nativa do Swift String:

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

Isso permite que você use diretamente:

myString.doubleValue

Qual irá realizar o casting para você. Se a Apple adicionar um doubleValueao String nativo, você só precisará remover a extensão e o restante do seu código será compilado automaticamente!


O método da Apple seria chamado .toDouble ... o mesmo que toInt. É uma omissão estranha. Provavelmente será corrigido em uma versão futura do Swift. Eu gosto do método de extensão da melhor forma, mantém o código limpo.
— n13 23/06/15

O valor de retorno deve ser uma dupla não um Float
— Leo Dabus

4

SWIFT 3

Para limpar, hoje em dia existe um método padrão:

public init?(_ text: String)de Doubleclasse.

Pode ser usado para todas as classes.

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


2

Extensão com localidade opcional

Swift 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
    }
}

Swift 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
Eu adicionei o exemplo do Swift 3.1. O que você quer dizer com "... e adicionar um exemplo para localidade"? Im
— imike

1

Ou você pode fazer:

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

1

Você pode usar StringEx . Ele se estende Stringa conversões de string para número, inclusive toDouble().

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

Ele verifica a string e falha se não puder ser convertido para o dobro.

Exemplo:

import StringEx

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

1

Swift 4

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

0

O que também funciona:

// 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.

NOTA: 'scanDouble' foi descontinuado no iOS 13.0
— uplearnedu.com

0

Use esse código no Swift 2.0

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


0

O uso Scannerem alguns casos é uma maneira muito conveniente de extrair números de uma string. E é quase tão poderoso quanto NumberFormatterquando se trata de decodificar e lidar com diferentes formatos e localizações de números. Pode extrair números e moedas com diferentes separadores decimais e de grupo.

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)
        }

    }
}

No entanto, existem problemas com separadores que podem ser concebidos como delimitadores de palavras.

// 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

Não há problema em detectar automaticamente a localidade. Observe que agruparSeparator no código de idioma francês na cadeia "Mon salaire est 9 999,99 €" não é um espaço, embora possa render exatamente como espaço (aqui não é). É por isso que o código abaixo funciona bem sem que os !$0.isWhitespacecaracteres sejam filtrados.

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

Acho mais legível adicionar uma extensão a String da seguinte maneira:

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

e então você poderia escrever seu código:

myDouble = myString.doubleValue

-1

meu problema era vírgula, então eu resolvo desta maneira:

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

-3
var stringValue = "55"

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

-6

podemos usar o valor CDouble, que será obtido por myString.doubleValue


Erro: 'String' não possui um membro chamado 'doubleValue'
— Matthew Knippen

@Matthew Sim não há nenhum membro do chamado "double valor", então podemos usar cdouble Valor
— ANIL.MUNDURU

1
pt.wikipedia.org/wiki/… Consulte números complexos e leia a documentação de referência do Swift. Não há nenhum CDouble ... Você está falando do Swift ou de outro idioma? E eu quero dizer rápido, sem extensões?
— Adrian Sluyters
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.