1

約 200,000 ~ 300,000 レコードを保持する CSV ファイルがあります。ほとんどのレコードは、単純な方法で分離して MySQL データベースに挿入できます。

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

で区切られた値

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

次に、int、float、string、text などの適切なデータ型を使用してデータベースに挿入します。

ただし、一部のレコードには、文字列に \n を含むテキスト列があります。$line = expand("\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

例からわかるように、ほとんどのレコードは上記の方法で簡単に分割できます。問題を引き起こす例の 2 番目のレコードです。

改行は \n のみで、ファイルには \r がまったく含まれていません。

4

5 に答える 5

3

もちろん、ここでの他のアドバイスは有効です。特に、独自の CSV パーサーを作成する場合は特に有効ですが、データを取得したいだけの場合は、fgetcsv()関数を使用し、実装の詳細について心配する必要はありません。

于 2008-10-09T13:03:26.777 に答える
1

csvデータがファイルにある場合は、他の人が指摘しているように、fgetcsv()を使用できます。fgetcsvは、埋め込まれた改行を正しく処理します。

ただし、csvデータが文字列内にある場合(例の$ fileDataのように)、str_getcsv()は一度に1行でのみ機能し、ファイル全体をレコードに分割できないため、次のメソッドが役立つ場合があります。

各行の引用符を数えることで、埋め込まれた改行を検出できます。引用符の数が奇数の場合、行が不完全であるため、この行を次の行と連結します。引用符の数が偶数になると、完全なレコードが作成されます。

完全なレコードができたら、引用符で分割します(ここでも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ループを1つか2つ使用して、データを最初から最後まで手動で反復処理するのはどうでしょうか。よりも低速ですが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」を追加します。2 行目は、改行、次に整数が見つかる場所で分割されます。

「0」は、preg_split一致する文字を後続の一致から削除するため、id の前に追加されます。

私が言うように、これは、行を分割するテキストが数字で新しい行を開始しないことが確実な場合にのみ機能します。

于 2008-10-09T12:46:13.003 に答える