【问题标题】:Using NSNumberFormatter to get a decimal value from an international currency string使用 NSNumberFormatter 从国际货币字符串中获取十进制值
【发布时间】:2011-04-03 10:00:40
【问题描述】:

似乎 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

值得注意的是,当使用相应的语言环境时,这些字符串与 NSNumberFormatter 的 stringFromNumber 方法输出的内容完全匹配。

在设置应用中将 Region Format 设置为 France,然后在以下代码中将 currencyNumber 设置为 12.34,导致 currencyString 设置为 '12,34 €':

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

显然,专门针对欧元解决此问题相当容易,但我希望在尽可能多的国家/地区销售此应用程序,并且我认为其他地区肯定会发生类似情况。

有人有答案吗?

TIA,邓肯

【问题讨论】:

  • 我还没有找到解决方案,所以任何输入都将不胜感激。谢谢,邓肯
  • 我建议提交错误报告。

标签: iphone objective-c cocoa currency nsnumberformatter


【解决方案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,即non-breaking space,而不是预期的,

OS X / iOS 上的格式化程序类(以及正则表达式类等)使用ICU library,其他项目也使用。我发现至少有two 其他bug 报告关于这个特定问题。不过,这似乎是expected 的行为。 (我花了一些时间筛选 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"
      • 美国 - "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] 浮点输出为 12345.67
        2011-05-05 12:56:25.475 ScannerTest[10882:903] 浮点输出为 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 变量将为零。要获得所需的结果,请在字符串的两个位置使用不间断空格(作为千位分隔符和货币符号分隔符):

          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 是输入的数字。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-12-19
              • 1970-01-01
              • 1970-01-01
              • 2012-12-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多