Я намагаюся швидко написати програму BMI. І у мене з’явилася ця проблема: як перетворити String в Double?
У Objective-C я можу так:
double myDouble = [myString doubleValue];
Але як я можу досягти цього швидкою мовою?
Я намагаюся швидко написати програму BMI. І у мене з’явилася ця проблема: як перетворити String в Double?
У Objective-C я можу так:
double myDouble = [myString doubleValue];
Але як я можу досягти цього швидкою мовою?
Відповіді:
Swift 4.2+ Рядок удвічі
Ви повинні використовувати ініціалізатори нового типу для перетворення між рядковими та числовими типами (Double, Float, Int). Він поверне необов'язковий тип (Double?), Який матиме правильне значення або нуль, якщо String не був числом.
Примітка: Властивість doubleValue NSString не рекомендується, оскільки вона повертає 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")
Детальніше читайте в моєму блозі про перетворення рядків у подвійні типи (та валюти) .
Swift 2 Update
Є нові failable ініціалізаторів , які дозволяють зробити це більш ідіоматичний і безпечним способом (як і багато відповідей відзначили, подвійне значення NSString є не дуже безпечно , тому що вона повертає значення 0 для значень , які не є цифр. Це означає , що doubleValue
з "foo"
і "0"
є так само.)
let myDouble = Double(myString)
Це повертається необов’язково, тож у випадках, коли передача, "foo"
де doubleValue
повернулося б 0, недоступний ініціалізатор повернеться nil
. Ви можете використовувати guard
, if-let
або map
для оброблятиOptional<Double>
Оригінальна публікація: Вам не потрібно використовувати конструктор NSString, як пропонує прийнята відповідь. Ви можете просто провести його таким чином:
(swiftString as NSString).doubleValue
- [NSString doubleValue]
знаходиться Double
в стриже як з'єднаний з Double?
( Optional<Double>
). Це відрізняється від функції Swift String, toInt()
яка повертається Int?
і в документах заявляє, що вона "... приймає рядки, які відповідають регулярному виразу" [- +]? [0-9] + "."
.toInt()
було б краще. Однак це була більше відповідь на те, як зробити саме те саме, але в Swift.
Double.init?(_ text: String)
тепер доступний ініціалізатор, що доступний.
Для трохи більше відчуття Swift, використовуючи NSFormatter()
ухиляється від трансляції NSString
та повертається, nil
коли рядок не містить Double
значення (наприклад, "test" не повернеться 0.0
).
let double = NSNumberFormatter().numberFromString(myString)?.doubleValue
Крім того, розширення String
типу Swift :
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
на форматері, якщо ви працюєте з валютами, де рядок містить символ валюти.
Інший варіант тут - перетворити це на NSString
і використовувати це:
let string = NSString(string: mySwiftString)
string.doubleValue
(swiftString as NSString).doubleValue
Ось метод розширення, який дозволяє просто викликати doubleValue () в рядку Swift і отримувати подвійний зворотній зв'язок (приклад виводу приходить першим)
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
}
}
Немає перевірки помилок, але ви можете додати її, якщо вона вам потрібна.
З 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
.
atof
і 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
.
У Swift 2.0 найкращий спосіб уникнути думки, як розробник Objective-C. Таким чином, ви не повинні "перетворювати рядок у подвійну", але ви повинні "ініціалізувати подвійний з рядка". Документ Apple тут: https://developer.apple.com/library/ios//documentation/Swift/Reference/Swift_Double_Structure/index.html#//apple_ref/swift/structctr/Double/s:FSdcFMSdFSSGSqSd_
Це необов'язковий init, тому ви можете використовувати оператор злиття нуля (??), щоб встановити значення за замовчуванням. Приклад:
let myDouble = Double("1.1") ?? 0.0
Double.init?(_ text: String)
стають доступними у Swift 2.0. Ви повинні згадати про це. Інші відповіді тут не тому, що люди думають, як розробники Objective-C, а тому, що цей ініціалізатор раніше не був доступний.
У 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- номер
Спробуйте це:
var myDouble = myString.bridgeToObjectiveC().doubleValue
println(myDouble)
ПРИМІТКА
Видалено в бета-версії 5. Це більше не працює?
Це спирається на відповідь @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
}
NumberFormatter().number(from: myString)?.doubleValue
спробуйте це
let str:String = "111.11"
let tempString = (str as NSString).doubleValue
print("String:-",tempString)
Швидкий: 4 і 5
Можливо, це два способи:
Рядок -> Int -> Double:
let strNumber = "314"
if let intFromString = Int(strNumber){
let dobleFromInt = Double(intFromString)
print(dobleFromInt)
}
Рядок -> NSString -> Подвійний
let strNumber1 = "314"
let NSstringFromString = NSString(string: strNumber1)
let doubleFromNSString = NSstringFromString.doubleValue
print(doubleFromNSString)
Використовуйте його як завгодно відповідно до необхідності коду.
Будь ласка, перевірте це на дитячому майданчику!
let sString = "236.86"
var dNumber = NSNumberFormatter().numberFromString(sString)
var nDouble = dNumber!
var eNumber = Double(nDouble) * 3.7
До речі, у моєму Xcode
.toDouble () - не існує
.doubleValue створити значення 0,0 з не числових рядків ...
1.
let strswift = "12"
let double = (strswift as NSString).doubleValue
2.
var strswift= "10.6"
var double : Double = NSString(string: strswift).doubleValue
Можливо, це допоможе вам.
Як уже зазначалося, найкращий спосіб досягти цього - прямий кастинг:
(myString as NSString).doubleValue
Виходячи з цього, ви можете зробити гладке розширене Swift String:
extension String {
var doubleValue: Double {
return (self as NSString).doubleValue
}
}
Це дозволяє безпосередньо використовувати:
myString.doubleValue
Який виконає кастинг для вас. Якщо Apple все-таки додасть doubleValue
до рідного рядка, вам просто потрібно видалити розширення, а решта вашого коду автоматично складеться штрафом!
Розширення з додатковою локальною формою
Швидкий 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
}
}
}
Ви можете використовувати 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")
}
Швидкий 4
extension String {
func toDouble() -> Double {
let nsString = self as NSString
return nsString.doubleValue
}
}
Що також працює:
// 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.
Використовуйте цей код у Swift 2.0
let strWithFloat = "78.65"
let floatFromString = Double(strWithFloat)
Використання 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
Немає проблем з автоматичним виявленням локальної точки. Зауважте, що groupingSeparator у французькій мові у рядку "Mon salaire est 9 999,99 €" не є пробілом, хоча він може відображатися саме як простір (тут його немає). Ось чому код нижче працює добре, не !$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)
моя проблема була комою, тому я вирішую її так:
extension String {
var doubleValue: Double {
return Double((self.replacingOccurrences(of: ",", with: ".") as NSString).doubleValue)
}
}
var stringValue = "55"
var convertToDouble = Double((stringValue as NSString).doubleValue)
ми можемо використовувати значення CDouble, яке буде отримано myString.doubleValue