使用 PHP 解析 csv 文件中转义的逗号

Parse escaped comma in a csv file using PHP

提问人:Nikhil Mohan 提问时间:10/23/2023 最后编辑:Nikhil Mohan 更新时间:10/25/2023 访问量:87

问:

我正在尝试解析 csv 文件。但是在尝试解析以下行时,我遇到了一个转义逗号的问题。

<?php
$str = "19018216307,Public,\,k]'=system1-system2,20230914143505.5,1-050000,No";
$data = str_getcsv($str);
?>

输出:

<?php
Array
(
    [0] => 19018216307
    [1] => Public
    [2] => \
    [3] => k]'=system1-system2
    [4] => 20230914143505.5
    [5] => 1-050000
    [6] => No
)
?>

让我们考虑列值 \,k]'=system1-system2。它应该被解析为 ,k]'=system1-system2。但是在处理 CSV 文件时,PHP 将其视为 2 列,结果类似于 \k]'=@system1-system2

预期输出:

<?php
Array
(
    [0] => 19018216307
    [1] => Public
    [2] => ,k]'=system1-system2
    [3] => 20230914143505.5
    [4] => 1-050000
    [5] => No
);
?>

注意:CSV 文件是由外部网站生成的原始数据。所以我不能对csv文件内容做任何事情。(例如:将列值放在双引号中)

提前致谢!

php csv fgetcsv php-8.1

评论

0赞 Nikhil Mohan 10/23/2023
@CBroe 是的,我想是的。我真的做了以下解决方法。$str = str_replace('\,', '|逗号|', $str);$data = str_getcsv($str);$data = array_map(function($v) { return str_replace('|comma|', ',', $v); }, $data);但我只想确定,是否有更好的方法来解决这种情况?
3赞 CBroe 10/23/2023
这不是 CSV 中转义的工作方式;该外部网站正在向您发送垃圾。您将无法直接使用它,您必须先以某种方式“修复”您的输入数据。str_getcsv
0赞 CBroe 10/23/2023
您的示例数据甚至与显示的输出不匹配 - 如何变成......?system1\,system2system1-system2
0赞 Nikhil Mohan 10/23/2023
@CBroe 对不起,这是我的错误。刚刚纠正了它!
0赞 Álvaro González 10/23/2023
实现的转义字符仅用于转义外壳字符(默认使用双引号),但您的数据似乎使用它来转义分隔符(默认为逗号)。如前所述,这是一种非常奇特的 CSV 方言,因此您将无法使用内置功能。str_getcsv()

答:

0赞 Casimir et Hippolyte 10/25/2023 #1

奇怪的“csv 格式”的解决方法:

$str = "19018216307,Public,\,k]'=system1-system2,20230914143505.5,1-050000,No";

$pattern = <<<'REGEX'
~(?nxx)
    (?# modifiers:
        - inline n: parenthesis act as a non-capturing group
        - inline xx: white-spaces are ignored even in character classes
        - global A: all the matches have to be contiguous
    )

    # pattern
    ( (?!\A) , \K | \A ) # not at the start with a commas or at the start without
    [^ , \\ ]* ( \\ . [^ , \\ ]* )* # field content (all that isn't a comma nor
                                    # a backslash, except escaped characters) 
                                           
    # final check
    ( \z (*:END) )? # define a marker if the end of the string is reached
~A
REGEX;

if (preg_match_all($pattern, $str, $m) && isset($m['MARK'])) {
    $result = array_map(fn($s) => strtr($s, ['\\\\' => '\\', '\\' => '']), $m[0]);
    print_r($result);
}

演示

如果格式允许在字段中使用换行符,请添加修饰符 s.(即 在模式的末尾或开始时)~As(?nxxs)

评论

0赞 Nikhil Mohan 10/25/2023
我对此感到担忧,我尝试了您的代码。但是,如果 csv 文件中有多行,则它不起作用。
0赞 Casimir et Hippolyte 10/25/2023
@NikhilMohan:此代码是为一行设计的(如示例中所示,因为您使用了该函数)。要使它适用于整个文件,您可以这样使用它: 3v4l.org/M7tJP#v8.2.11str_getcsv