首页 文章

使用CSV文件上的PHP替换或删除新行,但仅在单引号或双引号之间

提问于
浏览
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 \ n .

5 回答

  • 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;
    }
    
  • 0

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

  • 1

    如何手动迭代数据,从开始到结束,使用for循环还是两个?它比 explode() 慢,但更容易获得有关报价的一致和可靠的结果 .

    如果您选择此方法,请记住将转义的引号考虑在内 .

  • 0

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

    // 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从后续匹配中删除它匹配的字符 .

    正如我所说的,只有当你确定打破一行的文本不会开始带有数字的新行时,这才有效 .

  • 3

    使用 fgetcsv 和'll take care of all of that for you. Unless there'这是您需要拥有自己的CSV解析器的一些首要原因 .

相关问题