Подтвердить что ты не робот

Использование 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

4b9b3361

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