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.