Использование NSNumberFormatter для получения десятичного значения из строки международной валюты
Кажется, что NSNumberFormatter не может анализировать евро (и, возможно, другие) валютные строки в числовом виде. Может кто-то, пожалуйста, докажите, что я неправ.
Я пытаюсь использовать следующее, чтобы получить числовую сумму из строки валюты:
NSNumberFormatter *currencyFormatter = [[[NSNumberFormatter alloc] init] autorelease];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSNumber *currencyNumber = [currencyFormatter numberFromString:currencyString];
Это отлично подходит для суммы в валюте Великобритании и США. Он даже не имеет проблем с разделителями $и £ и тысяч без проблем.
Однако, когда я использую его с суммой валюты евро (с использованием формата Region, установленного во Франции или Германии в приложении настроек), он возвращает пустую строку. Все следующие строки терпят неудачу:
12,34 €
12,34
12.345,67 €
12.345,67
Стоит отметить, что эти строки соответствуют именно тому, что выходит из метода NSNumberFormatter stringFromNumber при использовании соответствующего языкового стандарта.
Настройка формата региона во Францию в приложении настроек, а затем установка значения currencyNumber в 12.34 в следующем коде, приводит к тому, что currencyString устанавливается в '12, 34 € ':
NSNumberFormatter *currencyFormatter = [[[NSNumberFormatter alloc] init] autorelease];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSString *currencyString = [currencyFormatter stringFromNumber:currencyNumber];
Очевидно, было бы довольно легко взломать эту проблему специально для евро, но я надеюсь продать это приложение как можно большему числу стран, и я думаю, что подобная ситуация будет связана с другими локалями.
Есть ли у кого-нибудь ответ?
TIA, Duncan
Ответы
Ответ 1
Это сложно. Выполнение следующих работ без проблем:
double moneyAmount = 1256.34;
NSLocale *french = [[NSLocale alloc] initWithLocaleIdentifier:@"fr_FR"];
NSNumberFormatter *currencyStyle = [[NSNumberFormatter alloc] init];
[currencyStyle setLocale:french];
[currencyStyle setNumberStyle:NSNumberFormatterCurrencyStyle];
NSNumber *amount = [NSNumber numberWithDouble:moneyAmount];
NSString *amountString = [currencyStyle stringFromNumber:amount];
NSNumber *pAmount = [currencyStyle numberFromString:amountString]; // == 1256.34
Однако вывод метода -currencyGroupingSeparator
возвращает строку \u00a0
, неразрывное пространство, а не ожидаемое ,
.
В классах форматирования (а также в классах регулярных выражений, среди прочих) на OS X/iOS используется библиотека два других bug сообщает с к этой конкретной проблеме. Похоже, что это ожидаемое поведение. (Я провел некоторое время, просачиваясь через источник ICU, чтобы найти, где это определено, но это лабиринт.)
Моя рекомендация в настоящее время заключалась бы в передаче -setLenient:YES
в форматировщик валют. Возможно, даже сообщите об ошибке с помощью радара Apple.
Ответ 2
Вы пытались установить регион во Францию в настройках, а затем пытаетесь получить числовое значение для строки? Или вы можете вручную установить локальный формат форматирования чисел с помощью setLocale:
. NSNumberFormatter использует любую локалью, чтобы выяснить, что такое числа. Если вам просто нужно, чтобы пользователь мог ввести локализованную строку валюты для своей области, измените систему на эту локаль и затем проверьте. Если вам нужно, чтобы пользователь мог разместить несколько форматов, добавьте какую-то систему выбора, чтобы вы знали, что они вкладывают.
Ответ 3
Я подтвердил, что (по крайней мере, на iOS 4.3), используя флаг [formatter setLenient: YES], работал у меня с использованием евро. Если этот флаг не установлен, форматтер не возвращает правильные значения для евро.
Ответ 4
Его рабочий код в Swift..
let formatter = NumberFormatter()
formatter.usesGroupingSeparator = true
formatter.locale = Locale(identifier: "de_DE")
formatter.numberStyle = NumberFormatter.Style.currency
formatter.string(from: NSNumber(floatLiteral: InputAsDoubleValue))!
Идентификаторы:
- для Немецкий -
"de_DE"
- для US -
"en_US"
- для Франция -
"fr_FR"
Отформатированное значение также будет возвращено с соответствующим символом валюты.
Ответ 5
У меня есть уродливый ответ, используя NSScanner
NSArray * testStrings = [NSArray arrayWithObjects:@"12,34 €", @"12,34" ,@"12.345,67 €" ,@"12.345,67", nil];
NSCharacterSet * charset = [[NSCharacterSet decimalDigitCharacterSet] invertedSet];
for(NSString * s in testStrings)
{
s = [s stringByReplacingOccurrencesOfString:@"." withString:@""];
s = [s stringByReplacingOccurrencesOfString:@"," withString:@"."];
NSScanner * scanner = [NSScanner scannerWithString:s];
[scanner setCharactersToBeSkipped:charset];
float f;
[scanner scanFloat:&f];
NSLog(@"float output is %.2f",f);
}
вывод:
2011-05-05 12: 56: 25.460 ScannerTest [10882: 903] выход с плавающей точкой - 12.34
2011-05-05 12: 56: 25.473 ScannerTest [10882: 903] выход с плавающей точкой - 12.34
2011-05-05 12: 56: 25.474 ScannerTest [10882: 903] float output - 12345.67
2011-05-05 12: 56: 25.475 ScannerTest [10882: 903] float output 12345.67
Ответ 6
С настройками по умолчанию (lenient
= NO), он будет преобразовывать строки только в числа, если они отформатированы точно так же, как строки, которые он производит при форматировании чисел в строки.
Итак, если вы хотите использовать неклассические преобразования, вы должны знать, что во многих локалях (все?) это означает, что пробелы во входной строке должны быть неразрывными (\u00a0
). К остроумию:
NSString *amount = @"10 000,00 €";
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterCurrencyStyle;
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier: @"fr_FR"];
NSNumber *number = [currencyFormatter numberFromString: amount];
Это не приведет к ожидаемому результату, так как переменная number
будет равна nil. Чтобы получить желаемый результат, используйте неразрывные пробелы в обоих местах строки (в качестве разделителя тысяч и разделителя символов валюты):
amount = @"10\u00a0000,00\u00a0€";
Тогда преобразование произойдет так, как ожидалось.
Конечно, если вы хотите использовать слабые преобразования, тогда это самый простой способ, и это, вероятно, будет лучшим вариантом для обработки данных.
С другой стороны, при обработке машинных данных мягкая интерпретация строк может быть нежелательной, поскольку вы потеряете возможность проверить правильность входных данных.
Ответ 7
Способ сделать это в Swift:
var locale = NSLocale.currentLocale()
var formatter = NSNumberFormatter()
formatter.locale = NSLocale(localeIdentifier: "en_US")
formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
var moneyDouble = formatter.numberFromString(textField.text)?.doubleValue
Предполагая, что textField.text - это введенное число.