问题描述
我正在尝试解析一些混乱的 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',25')); // float(3527.25)
\var_dump($formatter->parse('3thisisnotanumber')); // float(3)
我想问题可能在于 LENIENT_PARSE
属性设置为 true,但将其设置为 false ($formatter->setAttribute(\NumberFormatter::LENIENT_PARSE,0)
) 没有效果;非数字字符串仍然可以很好地解析,只要它们以数字开头。
由于行数太多,每行可能有多达 10 列需要验证,因此我认为每个文件有超过一百万次验证——因此,我宁愿避免 {{1基于}} 的解决方案,因为一百万次正则表达式匹配调用会非常昂贵。
有什么方法可以告诉 preg_match()
类您希望它不要宽大处理,并且仅在 整个 时才将字符串视为可解析的字符串是数字?
解决方法
你可以去掉所有的分隔符并检查剩下的是否是数值。
function customIsNumeric(string $value): bool
{
return is_numeric(str_replace(['.',','],'',$value));
}
可进行现场测试 here。
,您可以在解析之前使用 is_numeric() 来检查它是否只是数字。但是 NumberFormatter 并没有做你在这里寻找的东西。