用其他东西替换或删除新行,但只能在 CSV 文件上使用 PHP 的单引号或双引号之间

Posted

技术标签:

【中文标题】用其他东西替换或删除新行,但只能在 CSV 文件上使用 PHP 的单引号或双引号之间【英文标题】:Replacing or Removing a new line with something else but only between single or double quotes using PHP on a CSV file 【发布时间】:2010-09-16 06:28:15 【问题描述】:

我有一个 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。

【问题讨论】:

【参考方案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 会从后续匹配中删除它匹配的字符。

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

【讨论】:

【参考方案2】:

如果 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;

【讨论】:

【参考方案3】:

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

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

【讨论】:

【参考方案4】:

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

【讨论】:

【参考方案5】:

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

【讨论】:

我不熟悉 fgetcsv() 函数。这是我第一次负责获取大约 300MB 的 csv 文件,并将它们插入 MySQL 数据库。前几个文件很简单,因为它们没有嵌入的新行。

以上是关于用其他东西替换或删除新行,但只能在 CSV 文件上使用 PHP 的单引号或双引号之间的主要内容,如果未能解决你的问题,请参考以下文章

在 CSV UNIX 中删除双引号之间的 \n

在 csv 文件单元格中打印新行

在 wordpress 表中插入新行但删除所有其他行

phpMyAdmin CSV 上传替换数据不起作用

如何通过将csv文件与python中的其他csv文件进行比较来删除和替换csv文件中的列?

拆分csv文件,但忽略c#中引号之间的新行