Swift-文字列をDoubleに変換する方法


241

私はBMIプログラムを迅速な言語で記述しようとしています。そして、私はこの問題を得ました:StringをDoubleに変換する方法?

Objective-Cでは、次のようにすることができます。

double myDouble = [myString doubleValue];

しかし、どうすればこれをSwift言語で実現できますか?


1
これはSwift 2で変更されました。新しいDouble()失敗可能イニシャライザを使用した新しい回答を参照してください:stackoverflow.com/a/32850058/276626
Paul Solt

回答:


205

Swift 4.2+文字列からダブルへ

String型と数値型(Double、Float、Int)の間で変換するには、新しい型初期化子を使用する必要があります。正しい値を持つオプションのタイプ(Double?)を返します。文字列が数値でない場合は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以外の異なるロケールの数値をサポートしていないことです。1000,00のような他のフォーマットを使用すると、最後の2桁が切り捨てられます。少なくとも、これはObjective-Cで起こっていることです。-Appleのドキュメントに従って:(これらはロケールに対応していません)次の便利なメソッドはすべて、最初の空白文字(whitespaceSet)をスキップし、末尾の文字を無視します。これらはロケールに対応していません。NSScannerまたはNSNumberFormatterは、数値のより強力でロケールに対応した解析に使用できます。
mskw 2017年

@mskw通貨を解析するためのNumberFormatterの使用に関する詳細を追加しました。私のコードでは、if / let構文を使用して変換を試みる複数のフォーマッターの試行をまとめて、通貨形式の数値($ 1,000.00)と形式化された数値(1,000)の両方を解析できるようにします。ユーザーが数字を入力する方法は常にわからないため、if / letステートメントのグループで両方をサポートできることが理想的です。
Paul Solt

276

スウィフト2アップデート があり、あなたがより多くの慣用的かつ安全な方法でこれを行うことができ、新たなfailable初期化子は、この手段があること。多くの答えが指摘しているとして、それ以外の数値に0を返すので、NSStringのの二重の値は非常に安全ではありません(あるdoubleValue"foo""0"あります同じ。)

let myDouble = Double(myString)

これはオプションを返すため、0を返した"foo"場所を渡すような場合doubleValue、失敗した初期化子はを返しnilます。、、またはを使用してguardif-letmapOptional<Double>

元の投稿: 承認された回答が提案するようにNSStringコンストラクタを使用する必要はありません。次のように単純にブリッジできます:

(swiftString as NSString).doubleValue

5
オハイ、ジャーセン。このアプローチの欠点の1つは、文字列が有効なdoubleでなくても失敗しないことです。代わりに、0.0を取得します。すなわち、結果が- [NSString doubleValue]あるDoubleに並置として迅速にDouble?Optional<Double>)。これは、ドキュメントで "...正規表現" [-+]?[0-9] + "のみに一致する文字列を受け入れる" toInt()を返しInt?、それを記述するSwift String関数とは異なります。 "
デリックハサウェイ2015年

はい、使用した.toInt()方が良いでしょう。しかし、これはまったく同じことをする方法に対するより多くの答えでしたが、Swiftでした。
ジャーセン2015

このアプローチはFoundationに密接に結合し、Apple以外のSwiftの実装では機能しない可能性があります。
Erik Kerber、2015

5
Swift 2.0では、Double.init?(_ text: String)失敗する可能性のあるイニシャライザが利用可能になりました。
ミクセル2015

3
これは最善の方法ではありません。小数点記号によっては失敗します。「。」で結構です。ただし、「、」は使用できません。NSNumberFormatterは、現在のローカリゼーションで使用される小数点記号をサポートします。
JulianKról2016年

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クラスを拡張するアイデアが気に入っています。しかし、NSStringFormatterを呼び出すことの複雑さの利点はわかりません。NSStringへのキャストと.doubleValueの使用は、少なくとも直接的で、読みやすく、覚えやすいためです。
クリアライト2014年

12
実際、NSNumberFormatter()を使用する利点は、文字列にdoubleを構成しない文字が含まれている場合、numberFromString:メソッドがnilを返すことです。NSStringのdoubleValueの問題は、それが関係なく常にdouble(たとえば、「test」の場合は0.0)を返すことです。したがって、すべての種類の文字列を処理していて、文字列が2つの文字だけで、追加の文字がないかどうかをテストする場合は、NSNumberFormatterを使用するのがよいでしょう。
dirkgroten 2015

4
これはフランス語では失敗します。
Peter K.

1
これは素晴らしい解決策ですが、このコードのみを使用すると、区切り文字としてカンマが使用されている国(@PeterK。で言及されているフランスを含む)では機能しません。解決策を使ってコメントを書いてみましたが、長すぎるため、代わりに回答にする必要がありました。ここに行きます:修正された解決策
Jacob R

53

ここでの別のオプションは、これをに変換してNSStringそれを使用することです:

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

13
これを行うより良い方法があったらいいのにと思います。たとえば、解析が失敗したかどうかはどうすればわかりますか。
ライアンホフマン2014年

2
@RyanHoffman残念ながらNSStringのdoubleValueコントラクトはそれを提供したことがありません。文字列が数値の有効な表現(オーバーフロー/アンダーフローでのHUGE_VALおよび-HUGE_VAL)で始まらない場合、取得できるのは0.0だけです。解析が失敗した理由を知りたい場合は、数値/文字列変換をより堅牢な方法で処理するように設計されたNSNumberFormatterを調べる必要があると思います。
Jarsen 2014年

それはその答えを見つけるのは難しいかもしれないので、ここにある:(swiftString as NSString).doubleValue
LearnCocos2D

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

スウィフト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ます。

atof接頭辞付きの16進数文字列をstrtod受け入れ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開発者のように考えることを避けることです。したがって、「文字列を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開発者のように考えるのではなく、この初期化子が以前に利用できなかったためです。
ミクセル2015

確かにあなたは正しい。これはSwift 2.0でのみ利用可能です。ここで触れます:)他の言語と同様に、タイプを別のタイプで初期化し、タイプを別のタイプに変換できると期待すべきではありません。長い議論ですが、Obj-C開発者として、Swiftでコーディングするときに多くの悪い習慣がありますが、これはその1つです:)
Toom

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で削除されました。スマイリー氏の方法は、現時点で私が見つけた中で最も速い
faraquet99

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および5

これを行うには、おそらく2つの方法があります。

  1. 文字列-> Int-> Double:

    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)

コードの必要性に応じて、とにかくそれを使用してください。


最初の方法は私にはうまくいきませんが、2番目の方法はかなりうまくいきます。
Saurabh

@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ネイティブ文字列に追加する場合は、拡張機能を削除するだけで、残りのコードは自動的に正常にコンパイルされます。


Appleのメソッドは.toDouble ...と呼ばれ、toIntと同じです。それは奇妙な省略です。将来のSwiftバージョンで修正される可能性があります。私は拡張メソッドが一番好きで、コードをクリーンに保ちます。
n13 2015

戻り値は、浮動
小数点数で

4

SWIFT 3

明確にするために、今日ではデフォルトの方法があります:

public init?(_ text: String)Doubleクラス。

すべてのクラスで使用できます。

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


2

オプションのロケールを持つ拡張

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

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)

-1

次のように、Stringに拡張機能を追加すると、より読みやすくなります。

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

そして、あなたはあなたのコードを書くことができます:

myDouble = myString.doubleValue

-1

私の問題はカンマだったので、このように解決しました:

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


-6

myString.doubleValueによって取得されるCDouble値を使用できます


エラー:「文字列」には「doubleValue」という名前のメンバーがありません
マシュークニペン

我々はCDouble値を使用できるよう@Matthewはい「ダブル値」と呼ばれる全く部材が存在しない
ANIL.MUNDURU

1
en.wikipedia.org/wiki/… そこで複素数を参照し、Swiftのリファレンスドキュメントを読んでください。CDoubleはありません... Swiftや他の言語について話しているのですか そして、私は拡張なしで迅速を意味しますか?
エイドリアンSluyters 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.