Próbuję napisać program BMI w szybkim języku. Mam problem: jak przekonwertować ciąg znaków na podwójne?
W Objective-C mogę zrobić tak:
double myDouble = [myString doubleValue];
Ale jak mogę to osiągnąć w języku Swift?
Próbuję napisać program BMI w szybkim języku. Mam problem: jak przekonwertować ciąg znaków na podwójne?
W Objective-C mogę zrobić tak:
double myDouble = [myString doubleValue];
Ale jak mogę to osiągnąć w języku Swift?
Odpowiedzi:
Swift 4.2+ String to Double
Powinieneś użyć inicjatorów nowego typu do konwersji między typami String i numerycznymi (Double, Float, Int). Zwróci typ opcjonalny (Double?), Który będzie miał poprawną wartość lub zero, jeśli ciąg nie był liczbą.
Uwaga: Właściwość NSString doubleValue nie jest zalecana, ponieważ zwraca 0, jeśli wartość nie może zostać przekonwertowana (tj. Złe dane wprowadzone przez użytkownika).
let lessPrecisePI = Float("3.14")
let morePrecisePI = Double("3.1415926536")
let invalidNumber = Float("alphabet") // nil, not a valid number
Rozpakuj wartości, aby użyć ich za pomocą 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!)")
}
Możesz konwertować sformatowane liczby i walutę za pomocą NumberFormatter
klasy.
let formatter = NumberFormatter()
formatter.locale = Locale.current // USA: Locale(identifier: "en_US")
formatter.numberStyle = .decimal
let number = formatter.number(from: "9,999.99")
Formaty walutowe
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")
Przeczytaj więcej na moim blogu o konwertowaniu ciągów znaków na podwójne (i walutę) .
Swift 2 Update
Są nowe inicjalizatory failable które pozwalają to zrobić w bardziej idiomatyczne i bezpieczny sposób (jak wiele odpowiedzi zauważyli, podwójna wartość NSString nie jest bardzo bezpieczne, ponieważ zwraca 0 dla wartości spoza numerycznych. Oznacza to, że doubleValue
od "foo"
i "0"
są to samo.)
let myDouble = Double(myString)
Zwraca to wartość opcjonalną, więc w przypadkach takich jak przekazywanie w "foo"
miejsce, w którym doubleValue
zwróciłoby 0, zwracany jest niezawodny intializer nil
. Można użyć guard
, if-let
lub map
do obsługiOptional<Double>
Oryginalny post: Nie musisz używać konstruktora NSString, jak proponuje zaakceptowana odpowiedź. Możesz po prostu to przełączyć w następujący sposób:
(swiftString as NSString).doubleValue
- [NSString doubleValue]
jest Double
szybki w zastosowaniu do Double?
( Optional<Double>
). Różni się to od funkcji Swift String, toInt()
która zwraca Int?
i stwierdza w dokumentach, że „... akceptuje ciągi pasujące do wyrażenia regularnego„ [- +]? [0-9] + ”tylko.”
.toInt()
byłoby lepsze. Była to jednak raczej odpowiedź na to, jak zrobić dokładnie to samo, ale w Swift.
Double.init?(_ text: String)
teraz awaryjny inicjator.
Aby uzyskać nieco bardziej szybkie uczucie, użycie NSFormatter()
unika rzutowania na NSString
i zwraca, nil
gdy ciąg nie zawiera Double
wartości (np. „Test” nie zwróci 0.0
).
let double = NSNumberFormatter().numberFromString(myString)?.doubleValue
Alternatywnie, rozszerzając String
typ Swift :
extension String {
func toDouble() -> Double? {
return NumberFormatter().number(from: self)?.doubleValue
}
}
i używaj go w następujący sposób toInt()
:
var myString = "4.2"
var myDouble = myString.toDouble()
Zwraca to opcję opcjonalną, Double?
którą należy rozpakować.
Albo z wymuszonym rozpakowaniem:
println("The value is \(myDouble!)") // prints: The value is 4.2
lub z instrukcją if let:
if let myDouble = myDouble {
println("The value is \(myDouble)") // prints: The value is 4.2
}
Aktualizacja: W celu lokalizacji bardzo łatwo jest zastosować ustawienia narodowe do NSFormatter w następujący sposób:
let formatter = NSNumberFormatter()
formatter.locale = NSLocale(localeIdentifier: "fr_FR")
let double = formatter.numberFromString("100,25")
Na koniec możesz użyć NSNumberFormatterCurrencyStyle
formatyzatora, jeśli pracujesz z walutami, w których łańcuch zawiera symbol waluty.
Inną opcją jest przekonwertowanie tego na NSString
i użycie tego:
let string = NSString(string: mySwiftString)
string.doubleValue
(swiftString as NSString).doubleValue
Oto metoda rozszerzenia, która pozwala po prostu wywołać doubleValue () na łańcuchu Swift i uzyskać podwójne odwrócenie (przykładowe wyjście jest pierwsze)
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
Oto metoda rozszerzenia:
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
}
}
Brak sprawdzania błędów, ale możesz go dodać, jeśli potrzebujesz.
Począwszy od wersji Swift 1.1, można bezpośrednio przejść String
do const char *
parametru.
import Foundation
let str = "123.4567"
let num = atof(str) // -> 123.4567
atof("123.4567fubar") // -> 123.4567
Jeśli nie lubisz przestarzałych atof
:
strtod("765.4321", nil) // -> 765.4321
Jedno zastrzeżenie: zachowanie konwersji jest inne niż NSString.doubleValue
.
atof
i strtod
zaakceptuj 0x
poprzedzony ciąg szesnastkowy:
atof("0xffp-2") // -> 63.75
atof("12.3456e+2") // -> 1,234.56
atof("nan") // -> (not a number)
atof("inf") // -> (+infinity)
Jeśli wolisz .doubleValue
zachowanie, nadal możemy korzystać z CFString
mostkowania:
let str = "0xff"
atof(str) // -> 255.0
strtod(str, nil) // -> 255.0
CFStringGetDoubleValue(str) // -> 0.0
(str as NSString).doubleValue // -> 0.0
NSString
.
W Swift 2.0 najlepszym sposobem jest unikanie myślenia jak programista Objective-C. Dlatego nie powinieneś „konwertować ciągu na podwójny”, ale „inicjować podwójny z ciągu”. Dokument Apple tutaj: https://developer.apple.com/library/ios//documentation/Swift/Reference/Swift_Double_Structure/index.html#//apple_ref/swift/structctr/Double/s:FSdcFMSdFSSGSqSd_
Jest to opcjonalny init, więc możesz użyć zerowego operatora koalescencyjnego (??), aby ustawić wartość domyślną. Przykład:
let myDouble = Double("1.1") ?? 0.0
Double.init?(_ text: String)
stają się dostępne w Swift 2.0. Powinieneś o tym wspomnieć. Inne odpowiedzi tutaj nie są dlatego, że ludzie myślą jak programiści Objective-C, ale dlatego, że ten inicjator nie był wcześniej dostępny.
W SWIFT 3 możesz użyć:
if let myDouble = NumberFormatter().number(from: yourString)?.doubleValue {
print("My double: \(myDouble)")
}
Uwaga: - Jeśli ciąg znaków zawiera znaki inne niż cyfry lub separatory grupy lub liczby dziesiętne odpowiednie dla danego regionu, analiza nie powiedzie się. - Wszelkie początkowe lub końcowe znaki separatora spacji w ciągu są ignorowane. Na przykład ciągi „5”, „5” i „5” dają liczbę 5.
Zaczerpnięte z dokumentacji: https://developer.apple.com/reference/foundation/numberformatter/1408845-number
Opiera się to na odpowiedzi @Ryu
Jego rozwiązanie jest świetne, dopóki jesteś w kraju, w którym kropki są używane jako separatory. Domyślnie NSNumberFormatter
używa ustawień regionalnych urządzeń. Dlatego nie powiedzie się to we wszystkich krajach, w których przecinek jest używany jako domyślny separator (w tym Francja, jak wspomniano @PeterK.), Jeśli liczba używa kropek jako separatorów (co zwykle ma miejsce). Aby ustawić ustawienia regionalne tego NSNumberFormatter na US, a zatem użyj kropek, ponieważ separatory zastępują linię
return NSNumberFormatter().numberFromString(self)?.doubleValue
z
let numberFormatter = NSNumberFormatter()
numberFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
return numberFormatter.numberFromString(self)?.doubleValue
Dlatego pełny kod staje się
extension String {
func toDouble() -> Double? {
let numberFormatter = NSNumberFormatter()
numberFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
return numberFormatter.numberFromString(self)?.doubleValue
}
}
Aby z tego skorzystać, wystarczy zadzwonić "Your text goes here".toDouble()
Zwróci to opcjonalne Double?
Jak wspomniano w @Ryu, możesz albo wymusić rozpakowanie:
println("The value is \(myDouble!)") // prints: The value is 4.2
lub użyj if let
oświadczenia:
if let myDouble = myDouble {
println("The value is \(myDouble)") // prints: The value is 4.2
}
NumberFormatter().number(from: myString)?.doubleValue
Spróbuj tego
let str:String = "111.11"
let tempString = (str as NSString).doubleValue
print("String:-",tempString)
Szybki: 4 i 5
Można to zrobić na dwa sposoby:
String -> Int -> Double:
let strNumber = "314"
if let intFromString = Int(strNumber){
let dobleFromInt = Double(intFromString)
print(dobleFromInt)
}
String -> NSString -> Double
let strNumber1 = "314"
let NSstringFromString = NSString(string: strNumber1)
let doubleFromNSString = NSstringFromString.doubleValue
print(doubleFromNSString)
Używaj go w dowolny sposób, zgodnie z potrzebą kodu.
1.
let strswift = "12"
let double = (strswift as NSString).doubleValue
2)
var strswift= "10.6"
var double : Double = NSString(string: strswift).doubleValue
Może ci to pomóc.
Jak już wspomniano, najlepszym sposobem na osiągnięcie tego jest rzutowanie bezpośrednie:
(myString as NSString).doubleValue
Opierając się na tym, możesz stworzyć eleganckie natywne rozszerzenie Swift String:
extension String {
var doubleValue: Double {
return (self as NSString).doubleValue
}
}
Pozwala to na bezpośrednie użycie:
myString.doubleValue
Który wykona dla ciebie casting. Jeśli Apple doda doubleValue
natywny ciąg, wystarczy usunąć rozszerzenie, a reszta kodu automatycznie się skompiluje!
SWIFT 3
Aby wyczyścić, obecnie istnieje domyślna metoda:
public init?(_ text: String)
z Double
klasą.
Może być stosowany do wszystkich klas.
let c = Double("-1.0")
let f = Double("0x1c.6")
let i = Double("inf")
itp.
Rozszerzenie z opcjonalnymi ustawieniami narodowymi
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
}
}
}
Możesz użyć StringEx . Rozciąga String
się na konwersje ciągów na liczby, w tym toDouble()
.
extension String {
func toDouble() -> Double?
}
Sprawdza ciąg i kończy się niepowodzeniem, jeśli nie można go przekonwertować na podwójny.
Przykład:
import StringEx
let str = "123.45678"
if let num = str.toDouble() {
println("Number: \(num)")
} else {
println("Invalid string")
}
Szybki 4
extension String {
func toDouble() -> Double {
let nsString = self as NSString
return nsString.doubleValue
}
}
Co również działa:
// 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.
Użyj tego kodu w Swift 2.0
let strWithFloat = "78.65"
let floatFromString = Double(strWithFloat)
Używanie Scanner
w niektórych przypadkach jest bardzo wygodnym sposobem wydobywania liczb z łańcucha. I jest prawie tak potężny, jak w NumberFormatter
przypadku dekodowania i radzenia sobie z różnymi formatami liczb i lokalizacjami. Może wyodrębniać liczby i waluty z różnymi separatorami dziesiętnymi i grupowymi.
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)
}
}
}
Istnieją jednak problemy z separatorami, które można by traktować jako ograniczniki słów.
// 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
Nie ma problemu z automatycznym wykryciem ustawień regionalnych. Zauważ, że groupingSeparator w języku francuskim w łańcuchu „Mon salaire est 9 999,99 €” nie jest spacją, chociaż może renderować się dokładnie jako spacja (tutaj nie ma). Właśnie dlatego poniższy kod działa dobrze bez !$0.isWhitespace
odfiltrowywania znaków.
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)
moim problemem był przecinek, więc rozwiązałem go w ten sposób:
extension String {
var doubleValue: Double {
return Double((self.replacingOccurrences(of: ",", with: ".") as NSString).doubleValue)
}
}
var stringValue = "55"
var convertToDouble = Double((stringValue as NSString).doubleValue)
możemy użyć wartości CDouble, która zostanie uzyskana przez myString.doubleValue