【问题标题】:CSV Import Split by Comma - what to do about quotes?CSV Import Split by Comma - 如何处理引号?
【发布时间】:2010-05-14 15:17:05
【问题描述】:

我正在导入一个 CSV 文件,但遇到了问题。数据格式为:

TEST 690, "This is a test 1, 2 and 3" ,$14.95 ,4

我需要能够通过 , 不在引号内...

【问题讨论】:

  • 我会尝试的一件事是,如果可以的话,更改输入文件,使所有内容都包含在引号中,然后在去掉第一个和最后一个引号后,您可以通过"," 爆炸。这样,它不会在引号旁边爆炸逗号。当然,前提是您不想像 Artefacto 建议的那样使用 fgetcsv,并且想用它挑战自己。
  • 我不能把所有的东西都用引号括起来,它是通过另一个系统导出的。
  • 是否只能在第二个字段中使用引号?
  • 根据普遍接受的 CSV 规范,引号是可选的,并且仅用于消除包含引号、逗号或多行的字段的歧义。 en.wikipedia.org/wiki/Comma-separated_values#Basic_rules

标签: php regex csv


【解决方案1】:

参见fgetcsv 函数。

如果您已经有一个字符串,您可以创建一个包装它的流,然后使用fgetcsv。见http://code.google.com/p/phpstringstream/source/browse/trunk/stringstream.php

【讨论】:

  • 我宁愿使用正则表达式,因为这里有特殊功能
  • 不要使用正则表达式。它并不像看起来那么简单。您可能在字符串中有换行符。您可能已经转义了字符。
  • 一旦 CSV 被解析(通过 fgetscsv),您就可以对每个单独的字段进行正则表达式处理,使其符合您的心意。
  • 需要注意的是fgetcsv如果是字符串值的第一个字母,会有吃掉特殊字符的问题,所以有时你只需要解决它。
【解决方案2】:

如果您真的想手动执行此操作,我编写了一个粗略的参考实现,用于将一整行 CSV 文本分解为一个数组。 警告:此代码不处理多行字段!使用此实现,整个 CSV 行必须存在于单行中且没有换行符!

<?php
//-----------------------------------------------------------------------
function csvexplode($str, $delim = ',', $qual = "\"")
// Explode a single CSV string (line) into an array.
{
    $len = strlen($str);  // Store the complete length of the string for easy reference.
    $inside = false;  // Maintain state when we're inside quoted elements.
    $lastWasDelim = false;  // Maintain state if we just started a new element.
    $word = '';  // Accumulator for current element.

    for($i = 0; $i < $len; ++$i)
    {
        // We're outside a quoted element, and the current char is a field delimiter.
        if(!$inside && $str[$i]==$delim)
        {
            $out[] = $word;
            $word = '';
            $lastWasDelim = true;
        } 

        // We're inside a quoted element, the current char is a qualifier, and the next char is a qualifier.
        elseif($inside && $str[$i]==$qual && ($i<$len && $str[$i+1]==$qual))
        {
            $word .= $qual;  // Add one qual into the element,
            ++$i; // Then skip ahead to the next non-qual char.
        }

        // The current char is a qualifier (so we're either entering or leaving a quoted element.)
        elseif ($str[$i] == $qual)
        {
            $inside = !$inside;
        }

        // We're outside a quoted element, the current char is whitespace and the 'last' char was a delimiter.
        elseif( !$inside && ($str[$i]==" ")  && $lastWasDelim)
        {
            // Just skip the char because it's leading whitespace in front of an element.
        }

        // Outside a quoted element, the current char is whitespace, the "next" char is a delimiter.
        elseif(!$inside && ($str[$i]==" ")  )
        {
            // Look ahead for the next non-whitespace char.
            $lookAhead = $i+1;
            while(($lookAhead < $len) && ($str[$lookAhead] == " ")) 
            {
                ++$lookAhead;
            }

            // If the next char is formatting, we're dealing with trailing whitespace.
            if($str[$lookAhead] == $delim || $str[$lookAhead] == $qual) 
            {
                $i = $lookAhead-1;  // Jump the pointer ahead to right before the delimiter or qualifier.
            }

            // Otherwise we're still in the middle of an element, so add the whitespace to the output.
            else
            {
                $word .= $str[$i];  
            }
        }

        // If all else fails, add the character to the current element.
        else
        {
            $word .= $str[$i];
            $lastWasDelim = false;
        }
    }

    $out[] = $word;
    return $out;
}


// Examples:

$csvInput = 'Name,Address,Phone
Alice,123 First Street,"555-555-5555"
Bob,"345 Second Place,   City  ST",666-666-6666
"Charlie ""Chuck"" Doe",   3rd Circle   ,"  777-777-7777"';

// explode() emulates file() in this context.
foreach(explode("\n", $csvInput) as $line)
{
    var_dump(csvexplode($line));
}
?>

我仍然建议使用 PHP 的内置函数。从长远来看,这(希望)会更加可靠。 Artefacto 和 Roadmaster 是对的。您必须对数据执行的任何操作最好在导入数据后 完成。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-15
    • 2020-05-23
    • 1970-01-01
    • 2022-11-21
    • 2015-10-12
    • 1970-01-01
    相关资源
    最近更新 更多