用其他东西替换或删除新行,但只能在 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 的单引号或双引号之间的主要内容,如果未能解决你的问题,请参考以下文章