Использование NSNumberFormatter для получения десятичного значения из строки международной валюты

Кажется, что NSNumberFormatter не может анализировать строки евро (и, возможно, других) валют в числовой тип. Может кто-нибудь доказать, что я ошибаюсь.

Я пытаюсь использовать следующее, чтобы получить числовую сумму из строки валюты:

NSNumberFormatter *currencyFormatter = [[[NSNumberFormatter alloc] init] autorelease];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSNumber *currencyNumber = [currencyFormatter numberFromString:currencyString];

Это отлично работает для сумм в валюте Великобритании и США. Он без проблем справляется даже с разделителями $ и £ и тысячами.

Однако, когда я использую его с суммами в евро (с форматом региона, установленным на Франция или Германия в приложении настроек), он возвращает пустую строку. Все следующие строки терпят неудачу:

12,34 €
12,34
12.345,67 €
12.345,67

Стоит отметить, что эти строки точно соответствуют тому, что получается из метода stringFromNumber NSNumberFormatter при использовании соответствующей локали.

Установка формата региона на Франция в приложении настроек, а затем установка currencyNumber на 12,34 в следующем коде приводит к тому, что для currencyString устанавливается значение '12, 34 € ':

NSNumberFormatter *currencyFormatter = [[[NSNumberFormatter alloc] init] autorelease];
[currencyFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
NSString *currencyString = [currencyFormatter stringFromNumber:currencyNumber];

Очевидно, было бы довольно легко обойти эту проблему специально для евро, но я надеюсь продавать это приложение в как можно большем количестве стран, и я думаю, что аналогичная ситуация неизбежно произойдет с другими регионами.

Есть ли у кого-нибудь ответ?

ТИА, Дункан


person pieSquared    schedule 24.08.2010    source источник
comment
Я еще не нашел решения для этого, поэтому любой вклад был бы очень признателен. Спасибо, Дункан   -  person pieSquared    schedule 15.09.2010
comment
Я предлагаю заполнить отчет об ошибке.   -  person Nick Weaver    schedule 06.05.2011


Ответы (7)


Это непростой вопрос. Следующие работы выполняются без проблем:

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 используют библиотеку ICU, также используется другими проектами. Я нашел как минимум две других отчет об ошибке в отношении этой конкретной проблемы. Однако похоже, что это ожидаемое поведение. (Я потратил некоторое время, просматривая исходный код ICU, чтобы найти, где это определено, но это лабиринт.)

На данный момент я рекомендую передать -setLenient:YES программе форматирования валюты. Возможно, даже отправьте отчет об ошибке с помощью радара Apple.

person Sedate Alien    schedule 10.10.2010

Вы пытались установить регион как Франция в настройках, а затем пытались получить числовую сумму для строки? Или вы можете вручную установить локальный адрес модуля форматирования номеров с помощью setLocale:. NSNumberFormatter использует любую локаль, которая у него есть, чтобы выяснить, какие числа представляют собой строку. Если вам просто нужно, чтобы пользователь мог ввести строку локализованной валюты для своей области, измените систему на эту локаль, а затем проверьте. Если вам нужно, чтобы пользователь мог использовать несколько форматов, добавьте какую-то систему выбора, чтобы вы знали, что они вставляют.

person theMikeSwan    schedule 25.08.2010
comment
Да, все делается на правильной локали. Мне не нужно поддерживать несколько валют, просто работаю со строками в собственном языковом стандарте пользователя. Итак, в приведенном выше примере локаль установлена ​​на Францию ​​(или Германию и т. Д.). Обновлю вопрос;) - person pieSquared; 26.08.2010

Я подтвердил, что (по крайней мере, на iOS 4.3) использование флага [formatter setLenient: YES] у меня работает с использованием евро. Без установленного флага средство форматирования не возвращает правильные значения для евро.

person Ed Fernandez    schedule 28.10.2011

Его рабочий код на 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"
  • для США - "en_US"
  • для Франции - "fr_FR"

Отформатированное значение также будет возвращено с соответствующим символом валюты.

person Shailesh Tyagi    schedule 04.12.2017

У меня некрасивый ответ с использованием 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);
    }

вывод:
05.05.2011, 12:56: 25.460 ScannerTest [10882: 903] вывод с плавающей запятой: 12,34
2011-05-05 12:56: 25.473 ScannerTest [10882: 903] вывод с плавающей запятой: 12,34
05.05.2011, 12:56: 25.474 ScannerTest [10882: 903] вывод с плавающей запятой: 12345,67
2011-05-05 12:56: 25.475 ScannerTest [10882: 903] вывод с плавающей запятой: 12345.67

person Grady Player    schedule 05.05.2011

При настройках по умолчанию (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 будет равна нулю. Чтобы получить желаемый результат, используйте неразрывные пробелы в обоих местах строки (как разделитель тысяч и разделитель символов валюты):

amount = @"10\u00a0000,00\u00a0€";

Тогда преобразование произойдет, как ожидалось.

Конечно, если вы хотите использовать мягкие преобразования, это самый простой способ и, вероятно, будет лучшим вариантом для обработки человеческого ввода.

С другой стороны, при обработке данных, сгенерированных машиной, мягкая интерпретация строк может быть нежелательной, так как вы потеряете возможность проверять правильность входных данных.

person Monolo    schedule 13.05.2013

В 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 - это вводимое число.

person Gabriel Garrett    schedule 26.10.2014