替换CSV文件中的换行符

3

我遇到了一个问题,需要将CSV字符串转换为数组。

INV;165;1;0;1 Username;0;10000;"Here is multiline-text.

with line-breaks:

";20 Offen;0,00
INV;166;1;0;1 Username2;0;10000;"Here is another multiline-text.

with line-breaks:

";20 Offen;0,00

我尝试使用str_getcsv将字段分割,但问题是,分隔符仅出现在一个字段中,而函数也在拆分多行字段。

我的解决方案是首先通过preg_replace转换换行符,但我不是很了解它。这是仅替换由; "和"; 包围的换行符的正则表达式:

/(;")(.*)(\n)(.*)(";)/

这个模式实际上只匹配第一个换行符。

有人能给我提示如何完成这个任务吗?

提前感谢你。

这是原始的CSV文件:

CMXINV;165;1;0;1 Felix Hirschberg;0;10000;Herr;;Max;Muster;Company;;Street;123;City;DE;(0 40) 6 25 6;;(0 40) 6 25 6;mail@muster.de;;;;;;;;0;20121217;20121217;1 Sofort ohne Abzug;EUR;1 Agentur;0 ;0,00;;"Vielen Dank für Ihren Auftrag.

Vereinbarungsgemäß berechnen wir Ihnen:

";"Mit besten Grüßen


Invoice Man";;0;0;0;0;;20 Offen;0,00;;0 ;0,00;0,00;;EXW;;;;;;;;;;;;;;;;2;;Project: Test-Project;;0,000;0,00;1,000;0,00;0,00;0;0;0;0;0
CMXINV;165;2;0;1 Felix Hirschberg;0;10000;Herr;;Max;Muster;Company;;Street;123;City;DE;(0 40) 6 25 6;;(0 40) 6 25 6;mail@muster.de;;;;;;;;0;20121217;20121217;1 Sofort ohne Abzug;EUR;1 Agentur;0 ;0,00;;"Vielen Dank für Ihren Auftrag.

Vereinbarungsgemäß berechnen wir Ihnen:

";"Mit besten Grüßen


Invoice Man";;0;0;0;0;;20 Offen;0,00;;0 ;0,00;0,00;;EXW;;;;;;;;;;;;;;;;0;1;"- job1 (1h)
- job2 (1h)
- job3 (0,75h)
- job4 (1h)
- job5 (0,5h)";HR;3,25;100,00;1,00;0,00;325,00;1;0;0;0;0
MESSAGE;S;210053;INVOICE_GET hat 1 Datensätze zurückgegeben
MESSAGE;S;204020;Datenübertragung erfolgreich. Es wurden 1 Datensätze verarbeitet.

这是一个有效的CSV文件吗?类似这样的文件能够通过Excel打开吗?如果不能,为什么还要费心呢 - 只需创建(或允许创建)一个可在任何软件中使用的有效格式即可。如果您的内容需要换行符,请考虑使用XML而不是CSV作为跨多个应用程序进行通信的数据格式。 - feeela
问题是,CSV文件是由一个我无法修改的API提供的。是的,这些文件可以通过Excel打开。 - Felix Hirschberg
我猜你需要一个多行标识符,以便使你的正则表达式在多行文本/代码上工作。另请参阅:http://php.net/reference.pcre.pattern.modifiers.php - feeela
你能粘贴原始的 CVS 文件吗? - alinsoar
@alinsoar 在我的原始帖子中粘贴了。 - Felix Hirschberg
在您的原始帖子中,您发布了混合了评论的CVS文件。这会让我猜测CVS是什么,因此我的解决方案可能无法在原始文件上运行。 - alinsoar
3个回答

2
您可以尝试这个方法:
/;"(([^"]*)([\r\n])+([^"]*))+"/im

这将匹配;"分隔符内每个换行符之前和之后的文本。第二次匹配将是前面的文本,第四次匹配将是后面的文本。

请注意,我省略了最后一个';',以确保如果多行值是在行末,则仍然可以匹配。


谢谢!那已经是一个不错的方法,但我有一个问题,只有CSV列中的最后一个 \n 被替换了。我有一些带有多个换行符的字段。 - Felix Hirschberg
preg_replace在处理嵌套模式时表现不佳。也许可以尝试使用preg_match_all,并循环遍历所有的结果? - Gareth Cornish

1
根据 PHP 手册中用户的评论,fgetcsv()str_getcsv() 都应该正确处理换行符。
你可能应该利用这些实现(它们应该已经解决了你可能遇到的任何问题)。

编辑:自己编写解析器

或者您可以根据评论编写自己的解析器:

// Browse file one character after another
while (false !== ($c = fgetc($fp))) {
    // We are not inside the value, newline = new row
    if( ($c == "\n") || ($c == "\r")){
       // Newline, add to result
       continue;
    }

    // Whitespace? continue, do nothing
    if( ctype_space( $c)){
        continue;
    }

    // Okay, now we can use switch
    switch( $c){
        case ',':
            // Add empty value
            break;

        // Escaped value
        case '"':
        case "'":
            $escapeChar = $c;
            $prevChar = '';
            $value = '';

            while( false !== ($c = fgetc($fp))){
                // We just hit and end of escaped sequence, check escaped val by \
                if( ($c == $escapeChar) && ($c != '\\') ){
                   break;
                }

                // If we got \ and prev value is \ = "blah blah \\"
                // Prevent escape escape character of being guessed incorrectly
                if( ($c == '\\') && ($prevChar == '\\')){
                    $prevChar = '';
                } else {
                    $prevChar = $c;
                }

                $value .= $c;
            }

            // $value is your value
            break;

        // Normal, non escaped value:
        default:
            $value = '';
            while( false !== ($c = fgetc($fp))){
                if( ($c == ',') || ($c == '\n') || ($c == '\r')){
                    break;
                }
                $value .= $c;
            }

            // $value = your field value
            break;
     }
}

我在处理那些函数时遇到了麻烦,因为有些字段是被包含的,而有些则不是。 - Felix Hirschberg
@FelixHirschberg 我已经为你添加了解析器。 - Vyktor

0
如果您的CSV输入在文件中,您可以直接使用fgetcsv(),它可以很好地处理多行条目。
如果CSV输入是一个字符串,您可以使用特殊的php://temp I/O流将其有效地传递给fgetcsv()
$fp = fopen( 'php://temp', 'w+' );
fputs( $fp, $csv );
rewind( $fp );
$data = fgetcsv( $fp, 0, ';', '"' );
fclose( $fp );

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