如何让 NumberFormatter::parse() 只解析实际的数字字符串?
How to get NumberFormatter::parse() to only parse actual numeric strings?
我正在尝试解析一些混乱的 CSV 文件中的一些字符串(每个文件大约 100,000 行)。一些列在一些行中被挤压在一起,我正试图让它们恢复到它们正确的列中。所需的部分逻辑是查找给定列中的子字符串是否为数字。
非数字字符串可以是任何内容,包括恰好以数字开头的字符串;数字字符串通常以欧洲方式书写,点用于千位分隔符,逗号用于小数,因此如果不经过一堆字符串替换,is_numeric()
将无法实现:
\var_dump(is_numeric('3.527,25')); // bool(FALSE)
我认为 - 天真地,它发生了 - 正确的做法是使用 NumberFormatter::parse()
,但似乎该函数实际上并没有检查作为一个整体给出的字符串是否可解析为完全是数字字符串 – 相反,它只是从头开始,当它到达数字字符串中不允许的字符时,将切断其余部分。
基本上,我正在寻找的是可以产生以下结果的东西:
$formatter = new \NumberFormatter('de-DE', \NumberFormatter::DECIMAL);
\var_dump($formatter->parse('3.527,25')); // float(3527.25)
\var_dump($formatter->parse('3thisisnotanumber')); // bool(FALSE)
但我能得到的只有这个:
$formatter = new \NumberFormatter('de-DE', \NumberFormatter::DECIMAL);
\var_dump($formatter->parse('3.527,25')); // float(3527.25)
\var_dump($formatter->parse('3thisisnotanumber')); // float(3)
我认为问题可能在于 LENIENT_PARSE
属性设置为 true,但将其设置为 false ($formatter->setAttribute(\NumberFormatter::LENIENT_PARSE, 0)
) 没有任何效果;只要非数字字符串以数字开头,它们仍然可以很好地解析。
由于有这么多行,每行可能有多达十列需要验证,我正在查看每个文件超过一百万次验证——出于这个原因,我宁愿避免 preg_match()
基于解决方案,因为一百万个正则表达式匹配调用将非常昂贵。
有没有什么方法可以告诉 NumberFormatter
class 你希望它 不要 宽大一点,只有在以下情况下才将字符串视为可解析的整个 字符串是数字吗?
您可以在解析前使用is_numeric()检查它是否只是数字。但是 NumberFormatter 不会做你在这里寻找的东西。
您可以去除所有分隔符并检查剩下的是否是数值。
function customIsNumeric(string $value): bool
{
return is_numeric(str_replace(['.', ','], '', $value));
}
可以进行现场测试 here。
我正在尝试解析一些混乱的 CSV 文件中的一些字符串(每个文件大约 100,000 行)。一些列在一些行中被挤压在一起,我正试图让它们恢复到它们正确的列中。所需的部分逻辑是查找给定列中的子字符串是否为数字。
非数字字符串可以是任何内容,包括恰好以数字开头的字符串;数字字符串通常以欧洲方式书写,点用于千位分隔符,逗号用于小数,因此如果不经过一堆字符串替换,is_numeric()
将无法实现:
\var_dump(is_numeric('3.527,25')); // bool(FALSE)
我认为 - 天真地,它发生了 - 正确的做法是使用 NumberFormatter::parse()
,但似乎该函数实际上并没有检查作为一个整体给出的字符串是否可解析为完全是数字字符串 – 相反,它只是从头开始,当它到达数字字符串中不允许的字符时,将切断其余部分。
基本上,我正在寻找的是可以产生以下结果的东西:
$formatter = new \NumberFormatter('de-DE', \NumberFormatter::DECIMAL);
\var_dump($formatter->parse('3.527,25')); // float(3527.25)
\var_dump($formatter->parse('3thisisnotanumber')); // bool(FALSE)
但我能得到的只有这个:
$formatter = new \NumberFormatter('de-DE', \NumberFormatter::DECIMAL);
\var_dump($formatter->parse('3.527,25')); // float(3527.25)
\var_dump($formatter->parse('3thisisnotanumber')); // float(3)
我认为问题可能在于 LENIENT_PARSE
属性设置为 true,但将其设置为 false ($formatter->setAttribute(\NumberFormatter::LENIENT_PARSE, 0)
) 没有任何效果;只要非数字字符串以数字开头,它们仍然可以很好地解析。
由于有这么多行,每行可能有多达十列需要验证,我正在查看每个文件超过一百万次验证——出于这个原因,我宁愿避免 preg_match()
基于解决方案,因为一百万个正则表达式匹配调用将非常昂贵。
有没有什么方法可以告诉 NumberFormatter
class 你希望它 不要 宽大一点,只有在以下情况下才将字符串视为可解析的整个 字符串是数字吗?
您可以在解析前使用is_numeric()检查它是否只是数字。但是 NumberFormatter 不会做你在这里寻找的东西。
您可以去除所有分隔符并检查剩下的是否是数值。
function customIsNumeric(string $value): bool
{
return is_numeric(str_replace(['.', ','], '', $value));
}
可以进行现场测试 here。