如何让 NumberFormatter::parse() 只解析实际的数字字符串?
Posted
技术标签:
【中文标题】如何让 NumberFormatter::parse() 只解析实际的数字字符串?【英文标题】:How to get NumberFormatter::parse() to only parse actual numeric strings? 【发布时间】:2021-02-09 18:24:39 【问题描述】:我正在尝试解析一些混乱的 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()
-based 解决方案,因为一百万个正则表达式匹配调用会非常昂贵。
有没有办法告诉NumberFormatter
类,你希望它不要宽大处理,并且只在整个字符串是数字?
【问题讨论】:
鉴于此,我将尝试parse()
然后format()
使用相同的NumberFormatter
并查看开始和结束结果是否相同。
您是否至少知道哪些其他字符出现在您认为是数字的值中?那些只是点和逗号吗?
@El_Vanja 点、逗号和可能的否定前导连字符。没有别的(没有科学记数法或 Es 或类似的东西)。
【参考方案1】:
你可以去掉所有的分隔符,然后检查剩下的是否是一个数值。
function customIsNumeric(string $value): bool
return is_numeric(str_replace(['.', ','], '', $value));
可进行现场测试here。
【讨论】:
啊,是的,应该可以。你知道它在速度方面与if (preg_match("/[^0-9,.-]/", $value))
相比如何吗?我猜str_replace()
需要创建一个变量的副本并检查它,所以我想知道相对费用是多少(实际上,preg_match()
是否也创建一个副本?)...
我不知道内部工作原理,但是用 100k 数组值对它们进行基准测试(通过microtime
)(仅执行var_dump
s),它们的平均值大致相同。
但是,请考虑到对 100k 值的操作平均只需要 0.2 秒。因此,无论您决定使用哪个功能,它都不会对您的脚本性能产生太大影响。瓶颈将是读取文件/写回文件。【参考方案2】:
您可以在解析之前使用is_numeric() 来检查它是否只是数字。但是 NumberFormatter 并不能满足您的需求。
【讨论】:
正如我在问题中提到的,我不能使用is_numeric()
(至少在没有先操作字符串的情况下不会),因为这也会在所有实际上是数字的字符串。
@JanusBahsJacquet 对此感到抱歉,preg_match('#[^0-9\.,]#',$variable);
这就是我想要避免的。 preg_match()
很慢,并且必须在 100,000 行的文件上每行运行多达 10 次——这是我非常希望能够不用的一百万个正则表达式匹配。以上是关于如何让 NumberFormatter::parse() 只解析实际的数字字符串?的主要内容,如果未能解决你的问题,请参考以下文章