Swift - Cómo convertir String a Double


242

Estoy tratando de escribir un programa de IMC en lenguaje rápido. Y tengo este problema: ¿cómo convertir una cadena en un doble?

En Objective-C, puedo hacer así:

double myDouble = [myString doubleValue];

Pero, ¿cómo puedo lograr esto en lenguaje Swift?


1
Esto ha cambiado en Swift 2, vea la nueva respuesta utilizando el nuevo inicializador disponible Double (): stackoverflow.com/a/32850058/276626
Paul Solt

Respuestas:


205

Swift 4.2+ String to Double

Debería usar los nuevos inicializadores de tipo para convertir entre String y tipos numéricos (Double, Float, Int). Devolverá un tipo Opcional (¿Doble?) Que tendrá el valor correcto o nulo si la Cadena no era un número.

Nota: La propiedad NSString doubleValue no se recomienda porque devuelve 0 si el valor no se puede convertir (es decir: entrada incorrecta del usuario).

let lessPrecisePI = Float("3.14")

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

Desenvuelva los valores para usarlos 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!)")
}

Puede convertir números y monedas con formato utilizando la NumberFormatterclase.

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 moneda

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

Lea más en mi publicación de blog sobre cómo convertir String a tipos dobles (y moneda) .


2
Otro problema es que no admite números en una configuración regional diferente a 1,000.00, si usa otros formatos como 1 000,00, cortaría los 2 decimales al final. Al menos esto es lo que está sucediendo para Objective-C. - Según la documentación de Apple: (no son compatibles con la configuración regional) Los siguientes métodos de conveniencia omiten todos los caracteres de espacio inicial (whitespaceSet) e ignoran los caracteres finales. No son locales. NSScanner o NSNumberFormatter se pueden usar para un análisis de números más potente y compatible con la configuración regional.
mskw

@mskw He agregado detalles adicionales sobre el uso de NumberFormatter para analizar la moneda. En mi código, encadenaré varios intentos de formateador para convertir utilizando la sintaxis if / let para poder analizar tanto los números con formato de moneda ($ 1,000.00) como los números con formato (1,000). No siempre sabe cómo el usuario ingresará los números, por lo que poder admitir ambos es ideal con un grupo de declaraciones if / let.
Paul Solt

276

Swift 2 Actualización Hay nuevas inicializadores failable que le permiten hacer esto de una manera más idiomática y seguros (como han dicho en muchas respuestas, el valor doble de NSString no es muy seguro, ya que devuelve 0 para valores no numéricos. Esto significa que el doubleValuede "foo"y "0"están lo mismo.)

let myDouble = Double(myString)

Esto devuelve un opcional, por lo que en casos como pasar "foo" donde doubleValuehabría devuelto 0, volverá el intializer fallable nil. Puede utilizar una guard, if-leto mappara manejar laOptional<Double>

Publicación original: no necesita usar el constructor NSString como propone la respuesta aceptada. Simplemente puede puentearlo así:

(swiftString as NSString).doubleValue

55
ohai, Jarsen. Una desventaja de este enfoque es que no fallará si la cadena no es un doble válido. En cambio, solo obtendrás 0.0. Es decir, el resultado de - [NSString doubleValue]está Doubleen rápido como se aplicó a Double?( Optional<Double>). Esto es diferente de la función Swift String toInt()que devuelve Int?y dice en los documentos que "... acepta cadenas que coinciden con la expresión regular" [- +]? [0-9] + "solamente".
Derrick Hathaway

Sí, usar .toInt()sería mejor. Sin embargo, esto fue más una respuesta a cómo hacer exactamente lo mismo, pero en Swift.
Jarsen

Este enfoque lo une estrechamente a Foundation, y probablemente no funcionará en ninguna implementación de Swift que no sea de Apple.
Erik Kerber

55
En Swift 2.0 hay Double.init?(_ text: String)un inicializador disponible ahora disponible.
mixel

3
Este no es el mejor enfoque. Fallará dependiendo del separador decimal. Estará bien con '.' pero no con ','. NSNumberFormatter admitirá el separador decimal utilizado en la localización actual.
Julian Król

75

Para un poco más de sensación de rapidez, el uso NSFormatter()evita la conversión a NSString, y regresa nilcuando la cadena no contiene un Doublevalor (por ejemplo, "prueba" no volverá 0.0).

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

Alternativamente, extendiendo el Stringtipo de Swift :

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

y úsalo como toInt():

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

Esto devuelve un opcional Double?que debe desenvolverse.

Ya sea con desenvoltura forzada:

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

o con una declaración if let:

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

Actualización: para la localización, es muy fácil aplicar configuraciones regionales a NSFormatter de la siguiente manera:

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

Finalmente, puede usar NSNumberFormatterCurrencyStyleel formateador si está trabajando con monedas donde la cadena contiene el símbolo de moneda.


Me gusta la idea de extender la clase String para aplicaciones que utilizarán la conversión en varios lugares. Pero no veo el beneficio de la complejidad de invocar NSNumberFormatter, ya que el reparto a NSString y el uso de .doubleValue parece ser al menos tan directo y más fácil de leer y recordar.
clearlight

12
En realidad, la ventaja de usar NSNumberFormatter () es que el método numberFromString: devolverá nil si la cadena contiene caracteres que no constituyen un doble. El problema con doubleValue en NSString es que siempre devolverá un doble (0.0 para "prueba" por ejemplo) independientemente. Entonces, si está tratando con todo tipo de cadenas y desea probar si la cadena es solo un doble, sin caracteres adicionales, usar NSNumberFormatter es el camino a seguir.
dirkgroten

44
Esto falla en francés.
Peter K.

1
Esta es una gran solución, pero usar solo este código no funcionará en países donde las comas se usan como separadores (incluyendo Francia como @PeterK. Mencionado). Intenté escribir un comentario con la solución, pero fue demasiado largo, así que tuve que responderlo. Aquí tienes: Solución modificada
Jacob R

53

Otra opción aquí es convertir esto a an NSStringy usar eso:

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

13
Desearía que hubiera una mejor manera de hacer esto. ¿Cómo puedo saber si el análisis falló, por ejemplo?
Ryan Hoffman

2
@RyanHoffman Lamentablemente, el contrato NSString doubleValue nunca ha proporcionado nada de eso. Todo lo que obtienes es 0.0 si la cadena no comienza con una representación válida de un número, HUGE_VAL y -HUGE_VAL en overflow / underflow. Si desea saber por qué falló el análisis, creo que debe analizar NSNumberFormatter, que fue diseñado para manejar la conversión de números / cadenas de una manera más sólida.
Jarsen

Como puede ser difícil encontrar esa respuesta, aquí está:(swiftString as NSString).doubleValue
LearnCocos2D

24

Aquí hay un método de extensión que le permite simplemente llamar a doubleValue () en una cadena Swift y obtener una doble vuelta (la salida de ejemplo es lo primero)

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

Aquí está el método de extensión:

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

No hay comprobación de errores, pero puede agregarlo si lo necesita.


20

A partir de Swift 1.1, puede pasar directamente Stringal const char *parámetro.

import Foundation

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

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

Si no te gusta obsoleto atof:

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

Una advertencia: el comportamiento de la conversión es diferente de NSString.doubleValue.

atofy strtodaceptar 0xcadena hexadecimal prefijada:

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

Si prefiere el .doubleValuecomportamiento, aún podemos usar el CFStringpuente:

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

Soy fanático de este enfoque en lugar de lanzarlo NSString.
Sam Soffes

10

En Swift 2.0, la mejor manera es evitar pensar como un desarrollador de Objective-C. Por lo tanto, no debe "convertir una cadena en un doble", sino que debe "inicializar un doble de una cadena". Documento de Apple por aquí: https://developer.apple.com/library/ios//documentation/Swift/Reference/Swift_Double_Structure/index.html#//apple_ref/swift/structctr/Double/s:FSdcFMSdFSSGSqSd_

Es un init opcional, por lo que puede usar el operador de fusión nula (??) para establecer un valor predeterminado. Ejemplo:

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

Double.init?(_ text: String)estar disponible en Swift 2.0. Deberías mencionar esto. Otras respuestas aquí no es porque la gente piense como los desarrolladores de Objective-C sino porque este inicializador no estaba disponible antes.
mixel

De hecho tienes razón. Esto solo está disponible en Swift 2.0, lo mencionaré :) Pero al igual que en otros idiomas, inicializa un tipo con otro y no debe esperar que un tipo pueda convertirse en otro. Debate largo, pero como desarrollador de Obj-C, tengo muchas malas prácticas al codificar con Swift y esta es una de ellas :)
Toom

10

En SWIFT 3 , puede usar:

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

Nota: - Si una cadena contiene caracteres que no sean dígitos numéricos o separadores decimales o de grupo apropiados para la localidad, el análisis fallará. - Se ignoran los caracteres separadores de espacio iniciales o finales de una cadena. Por ejemplo, las cadenas "5", "5" y "5" producen el número 5.

Tomado de la documentación: https://developer.apple.com/reference/foundation/numberformatter/1408845-number


10

No he visto la respuesta que estaba buscando. Acabo de publicar aquí el mío en caso de que pueda ayudar a alguien. Esta respuesta es válida solo si no necesita un formato específico .

Swift 3

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

9

Prueba esto:

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

NOTA

Eliminado en Beta 5. ¿Esto ya no funciona?


1
bridgeToObjectiveC () se ha eliminado en Xcode 6 Beta 5. El método de Mr Smiley es el más rápido que he encontrado en este momento
faraquet99

7

Esto se basa en la respuesta de @Ryu

Su solución es excelente siempre que esté en un país donde los puntos se usan como separadores. Por defecto NSNumberFormatterusa la configuración regional de los dispositivos. Por lo tanto, esto fallará en todos los países donde se utiliza una coma como separador predeterminado (incluido Francia como @PeterK. Mencionado) si el número usa puntos como separadores (que normalmente es el caso). Para establecer la configuración regional de este NSNumberFormatter en EE. UU. Y, por lo tanto, usar puntos como separadores reemplaza la línea

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

con

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

Por lo tanto, el código completo se convierte

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

Para usar esto, solo llame "Your text goes here".toDouble()

Esto devolverá un opcional Double?

Como @Ryu mencionó, puedes forzar el desenvolvimiento:

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

o use una if letdeclaración:

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

1
Esta es la respuesta correcta. La versión de 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

prueba esto

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

6

Swift: 4 y 5

Posiblemente hay dos formas de hacer esto:

  1. Cadena -> Int -> Doble:

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

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

Úselo de la manera que desee según la necesidad del código.


El primer método no funciona para mí, sin embargo, el segundo funciona bastante bien.
Saurabh

@Saurabh, ¿Puedes por favor dar más detalles sobre cuál era tu cadena?
Abhirajsinh Thakore

5

Por favor, compruébalo en el patio de recreo!

let sString = "236.86"

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

Por cierto en mi Xcode

.toDouble () - no existe

.doubleValue crea el valor 0.0 a partir de cadenas no 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 

Puede ser esta ayuda para ti.


4

Como ya se señaló, la mejor manera de lograr esto es con el casting directo:

(myString as NSString).doubleValue

A partir de eso, puede hacer una elegante extensión nativa de Swift String:

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

Esto le permite usar directamente:

myString.doubleValue

Que realizará el casting por ti. Si Apple agrega un doubleValuea la cadena nativa, solo necesita eliminar la extensión y el resto de su código se compilará automáticamente.


El método de Apple se llamaría .toDouble ... igual que toInt. Es una omisión extraña. Es probable que se rectifique en una futura versión de Swift. Me gusta más el método de extensión, mantiene limpio el código.
n13

El valor de retorno debería ser un Doble NO un Flotador
Leo Dabus

4

SWIFT 3

Para borrar, hoy en día hay un método predeterminado:

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

Se puede usar para todas las clases.

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


2

Extensión con configuración regional 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
He agregado el ejemplo Swift 3.1. ¿Qué quiere decir con "... y agregar un ejemplo para la configuración regional"? 🙂
imike

1

O podrías hacer:

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

1

Puedes usar StringEx . Se extiende Stringcon conversiones de cadena a número incluidas toDouble().

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

Verifica la cadena y falla si no se puede convertir al doble.

Ejemplo:

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

Lo que también 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' quedó en desuso en iOS 13.0
uplearnedu.com el

0

Use este código en Swift 2.0

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


0

Usar Scanneren algunos casos es una forma muy conveniente de extraer números de una cadena. Y es casi tan poderoso como NumberFormattercuando se trata de decodificar y manejar diferentes formatos de números y configuraciones regionales. Puede extraer números y monedas con diferentes separadores decimales y 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)
        }

    }
}

Sin embargo, hay problemas con los separadores que podrían concebirse como delimitadores de palabras.

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

No hay problema para detectar automáticamente la configuración regional. Tenga en cuenta que el separador de agrupación en francés en la cadena "Mon salaire est 9 999,99 €" no es un espacio, aunque puede representar exactamente como espacio (aquí no lo hace). Es por eso que el siguiente código funciona bien sin !$0.isWhitespaceque se filtren los caracteres.

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

Encuentro más legible agregar una extensión a String de la siguiente manera:

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

y luego podrías escribir tu código:

myDouble = myString.doubleValue

-1

mi problema era una coma, así que lo resuelvo de esta manera:

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 el valor CDouble que será obtenido por myString.doubleValue


Error: 'String' no tiene un miembro llamado 'doubleValue'
Matthew Knippen el

@Matthew Sí, no hay ningún miembro llamado "valor doble", por lo que podemos usar CDouble Value
ANIL.MUNDURU

1
en.wikipedia.org/wiki/... Consulte los números complejos allí y lea la documentación de referencia de Swift. No hay ningún CDouble ... ¿Está hablando de Swift u otro idioma? ¿Y quiero decir rápido sin extensiones?
Adrian Sluyters
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.