我该如何改变NumberFormatter::parseCurrency()方法的行为以接受空格和不间断空格?

11

我正在尝试解析本地化货币字符串以获取货币和浮点值。

一切都进行得很顺利,现在我们遇到了一些问题。似乎NumberFormatter :: parseCurrency使用了一个额外的不可见字符:

测试代码:

<?php
$formatter = new NumberFormatter("de_DE", NumberFormatter::CURRENCY);
var_dump(array(
    $formatter->parseCurrency("88,22 €", $curr), // taken from output of $formatter->format(88.22)
    $formatter->parseCurrency("88,22 €", $curr), // input with keyboard
    $formatter->parseCurrency("88,22 \xE2\x82\xAc", $curr), // just a test
    $formatter->format(88.22),
    "88,22 €" // keyboard input
));

输出:

array(5) {
  [0]=> float(88,22)
  [1]=> bool(false)
  [2]=> bool(false)
  [3]=> string(10) "88,22 €" // this as input works
  [4]=> string(9) "88,22 €" // this not...
}

正如您所看到的,输出3和4的字符串长度存在差异。

我在启用了mbstring的Ubuntu上的PHP 5.3和Mac OS X上的Zend服务器上的PHP 5.4中获得了相同的结果。

主要问题是来自我的表单(ZF1应用程序)的输入值与索引4的输出相等......

有什么建议吗?提前感谢。

编辑1:

工作值的十六进制转储:

00000000  38 38 2c 32 32 c2 a0 e2  82 ac 0a                 |88,22......|
0000000b

无法工作值的十六进制转储:

00000000  38 38 2c 32 32 20 e2 82  ac 0a                    |88,22 ....|
0000000a

编辑2:

这似乎是使用的空格符有问题。c2a0是“NO-BREAK SPACE”,(可能?)NumberFormatter :: parseCurrency()需要它,但0x20是默认空格符(输入表单中输入的)。目前的解决方法是使用NO-BREAK SPACE替换空格符,使用$value = str_replace("\x20", "\xC2\xA0", $value);

编辑3:

在另一个系统(Mac OS X与Zend Server 5.6一起使用,启用了mbstring,PHP 5.3.14),一切都按预期工作:

array(5) {
  [0]=> float(88,22)
  [1]=> float(88,22)
  [2]=> float(88,22)
  [3]=> string(9) "88,22 €"
  [4]=> string(9) "88,22 €"
}

编辑4:

使用空格和不间断空格配置所需的主要区别在于ICU版本:

可用版本:

intl

Internationalization support => enabled
version => 1.1.0
ICU version => 3.8.1

Directive => Local Value => Master Value
intl.default_locale => no value => no value
intl.error_level => 0 => 0

不工作的版本:

intl

Internationalization support => enabled
version => 1.1.0
ICU version => 4.8.1.1
ICU Data version => 4.8.1

Directive => Local Value => Master Value
intl.default_locale => no value => no value
intl.error_level => 0 => 0

1
只是一个想法:格式化程序中的 € 符号是否为 UTF-8 编码(0x20AC),而键盘上的符号则为 Latin-1(0x80)?据我所知,strlen() 函数不知道 Unicode 字符。如果它在 var_dump() 中被内部使用,那么这就可以解释额外的字符了。 - CodeZombie
我的终端应用程序(iTerm2)使用Unicode(UTF-8)作为终端仿真。此外,这个错误/行为是由于从浏览器输入数据通过HTML表单文本输入字段造成的。我添加了十六进制转储输出以供澄清。 - nofreeusername
文件保存为UTF-8格式了吗? - Timo Huovinen
是的,在所有测试的系统上都是这样。 - nofreeusername
1个回答

3

NumberFormatter::parseCurrency 是 ICU 库函数 unum_parseDoubleCurrency 的一个薄包装器(请见源代码)。

ICU 库函数在解析字符串时非常严格,它只能解析其双重功能 unum_formatDoubleCurrency 生成的字符串。解析格式由 Unicode 语言环境数据驱动,该语言环境数据规定货币值和数字值之间使用不间断空格。显然,早期版本的库接受其他空格字符。

简而言之,你不能让 NumberFormatter::parseCurrency 接受空格。但是,Zend_Currency 也应默认输出不间断空格:

$currency = new Zend_Currency(array(
     'currency' => 'EUR',
     'value'    => 88.22,
), 'de_DE');

var_dump(
    strval($currency),             // 88,22 €
    strpos($currency, "\x20"),     // false
    strpos($currency, "\xc2\xa0")  // 5
);

问题是您的应用程序的哪个部分输出了空格,以及您如何处理它。您提到它是表单的一部分,因此也许您可以考虑让表单将货币和值作为单独的字段返回,以便您无需担心解析数字。如果用户自己输入字符串 "88,22 €",可能会遇到比空格问题更多的问题。话虽如此,您提到的解决方法(使用 \xc2\xa0 替换 \x20 )是唯一的方法,如果您想使用 NumberFormatter

谢谢解释!Zend_Currency返回正确的值。但是我的表单允许使用本地格式和货币符号的浮点数进行直接用户输入。使用Zend Framework的完整解决方案是添加到元素中的自定义过滤器,因为这不是NumberFormatter或pecl-intl上的真正问题。 - nofreeusername

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接