1

我有一个 CSV 文件,其中包含大约 200,000 - 300,000 条记录。大多数记录可以通过一个简单的方法分离并插入到 MySQL 数据库中

$line = explode("\n", $fileData);

然后用分隔的值

$lineValues = explode(',', $line);

然后使用正确的数据类型(即 int、float、string、text 等)插入数据库。

但是,某些记录的文本列在字符串中包含 \n。使用 $line = explode("\n", $fileData); 时会中断 方法。需要插入数据库的每一行数据大约有 216 列。并非每一行都有一个字符串中带有 \n 的记录。但是,每次在该行中找到一个 \n 时,它都包含在一对单引号 (') 之间

每一行都按以下格式设置:

id,data,data,data,text,more data

例子:

1,0,0,0,'Hello World,0
2,0,0,0,'Hello
    World',0
3,0,0,0,'Hi',0
4,0,0,0,,0

从示例中可以看出,大多数记录都可以使用上面显示的方法轻松拆分。它是导致问题的示例中的第二条记录。

新行只有 \n 并且文件中根本不包含 \r 。

4

5 回答 5

3

当然,这里的另一个建议是有效的,特别是如果您打算编写自己的 CSV 解析器,但是,如果您只想获取数据,请使用fgetcsv()函数,而不必担心实现细节。

于 2008-10-09T13:03:26.777 回答
1

如果 csv 数据在文件中,则可以像其他人指出的那样使用 fgetcsv() 。fgetcsv 正确处理嵌入的换行符。

但是,如果您的 csv 数据位于字符串中(例如示例中的 $fileData),则以下方法可能很有用,因为 str_getcsv() 一次只能处理一行,并且不能将整个文件拆分为记录。

您可以通过计算每行中的引号来检测嵌入的换行符。如果有奇数个引号,则说明您的行不完整,因此请将此行与下一行连接起来。一旦你有偶数的报价,你就有一个完整的记录。

一旦你有一个完整的记录,在引号处分割它(再次使用explode())。奇数字段被引用(因此嵌入的逗号不是特殊的),偶数字段不是。

例子:

# Split file into physical lines (records may span lines)
$lines = explode("\n", $fileData);

# Re-assemble records
$records = array ();
$record = '';
$lineSep = '';
foreach ($lines as $line) {
  # Escape @ symbol so we can use it as a marker (as it does not conflict with
  # any special CSV character.)
  $line = str_replace('@', '@a', $line);

  # Escape commas as we don't yet know which ones are separators
  $line = str_replace(',', '@c', $line);

  # Escape quotes in a form that uses no special characters
  $line = str_replace("\\'", '@q', $line);
  $line = str_replace('\\', '@b', $line);

  $record .= $lineSep . $line;
  $lineSep = "\n";

  # Must have an even number of quotes in a complete record!
  if (substr_count($record, "'") % 2 == 0) {
    $records[] = $record;
    $record = '';
    $lineSep = '';
  }
}
if (strlen($record) > 0) {
  $records[] = $record;
}

$rows = array ();

foreach ($records as $record) {
  $chunks_in = explode("'", $record);
  $chunks_out = array ();

  # Decode escaped quotes/backslashes.
  # Decode field-separating commas (unless quoted)
  foreach ($chunks_in as $i => $chunk) {
    # Unescape quotes & backslashes
    $chunk = str_replace('@q', "'", $chunk);
    $chunk = str_replace('@b', '\\', $chunk);
    if ($i % 2 == 0) {
      # Unescape commas
      $chunk = str_replace('@c', ',', $chunk);
    }
    $chunks_out[] = $chunk;
  }

  # Join back together, discarding unescaped quotes
  $record = join('', $chunks_out);

  $chunks_in = explode(',', $record);
  $row = array ();
  foreach ($chunks_in as $chunk) {
    $chunk = str_replace('@c', ',', $chunk);
    $chunk = str_replace('@a', '@', $chunk);
    $row[] = $chunk;
  }
  $rows[] = $row;
}
于 2008-10-09T12:48:02.563 回答
1

如何使用一两个 for 循环从头到尾手动迭代数据?它比 慢explode(),但更容易获得关于报价的一致和可靠的结果。

如果您选择此方法,请记住要考虑转义引号。

于 2008-10-09T12:54:10.010 回答
0

使用fgetcsv它,它会为您处理所有这些。除非有一些压倒一切的原因,否则您需要拥有自己的 CSV 解析器。

于 2008-10-09T13:11:41.767 回答
-1

如果您可以保证以数字开头的每个新行都是有效的新行(即不在文本描述的中间),那么您可以尝试以下操作:

// Replace all new-line then id patterns with new-line 0+id
$line = preg_replace('/\n(\d)/',"\n0$1",$line);

// Split on new-line then id
$linevalues = preg_split("/\n\d/",$data);

第一步识别具有新行后跟数字值的所有行。然后它将“0”添加到该数值。第二行在找到换行符的地方分割,然后是整数。

“0”被添加到 id 的前面,因为preg_split它从后续匹配中删除了它匹配的字符。

正如我所说,这只有在您确定换行的文本不会以数字开始新行时才有效。

于 2008-10-09T12:46:13.003 回答