Чтение CSV-файла с неэкранированными вложениями

Я читаю файл CSV, но некоторые значения не экранированы, поэтому PHP читает его неправильно. Вот пример плохой строки:

« 635 », « », « ОБРИ Р. ФИЛЛИПС (1920–) - пастель с изображением коттеджей в речной долине с крутыми склонами, возможно, в Северном Уэльсе, подпись и дата 2000 г., рама, 66 см на 48 см. еще один сельский пейзаж, озаглавленный оборотная сторона «Время сбора урожая, Сомерсет», подписанное и датированное 87 годом, в рамке, 69 на 49 см. (2) NB — Обри Филлипс — художник из Вустершира, который учился в Школе искусств Стоурбриджа.», «40», «60», «WAT ","Картины, гравюры и акварели",

Вы можете видеть, что Harvest Time, Somerset заключен в кавычки, заставляя PHP думать, что это новое значение.

Когда я делаю print_r() для каждой строки, ломаные строки выглядят так:

Array
(
    [0] =>  635
    [1] =>  
    [2] => AUBREY R. PHILLIPS (1920- ) - Pastel depicting cottages in a steep sided river valley, possibly North Wales, signed and dated 2000, framed, 66cm by 48cm. another of a rural landscape, titled verso Harvest Time
    [3] => Somerset" signed and dated '87
    [4] => framed
    [5] => 69cm by 49cm. (2)  NB - Aubrey Phillips is a Worcestershire artist who studied at the Stourbridge School of Art."
    [6] => 40
    [7] => 60
    [8] => WAT
    [9] => Paintings, prints and watercolours
    [10] => 
)

Что, очевидно, неверно, так как теперь он содержит гораздо больше элементов массива, чем другие правильные строки.

Вот PHP, который я использую:

$i = 1;
if (($file = fopen($this->request->data['file']['tmp_name'], "r")) !== FALSE) {
    while (($row = fgetcsv($file, 0, ',', '"')) !== FALSE) {
        if ($i == 1){
            $header = $row;
        }else{
            if (count($header) == count($row)){
                $lots[] = array_combine($header, $row);
            }else{
                $error_rows[] = $row;
            }

        }
        $i++;
    }
    fclose($file);
}

Строки с неправильным количеством значений помещаются в $error_rows, а остальные помещаются в большой массив $lots.

Что я могу сделать, чтобы обойти это? Спасибо.


person 472084    schedule 16.03.2012    source источник
comment
Следует опубликовать свой код о том, как вы анализируете CSV. Там могут быть ошибки, но они не могут помочь или сказать вам об этом без вашего кода.   -  person Churk    schedule 16.03.2012


Ответы (4)


Если вы знаете, что вы всегда будете получать записи 0 и 1, и что последние 5 записей в массиве всегда верны, так что это просто описательная запись, которая «испорчена» из-за неэкранированных символов включения, тогда вы можете извлечь первые 2 и последние 5 с использованием array_slice(), implode() остаток обратно в одну строку (восстановление потерянных кавычек) и перестроить массив правильно.

$testData = '" 635"," ","AUBREY R. PHILLIPS (1920- ) - Pastel depicting cottages in a steep sided river valley, possibly North Wales, signed and dated 2000, framed, 66cm by 48cm. another of a rural landscape, titled verso "Harvest Time, Somerset" signed and dated \'87, framed, 69cm by 49cm. (2) NB - Aubrey Phillips is a Worcestershire artist who studied at the Stourbridge School of Art.","40","60","WAT","Paintings, prints and watercolours",';

$result = str_getcsv($testData, ',', '"');

$hdr = array_slice($result,0,2);
$bdy = array_slice($result,2,-5);
$bdy = trim(implode('"',$bdy),'"');
$ftr = array_slice($result,-5);

$fixedResult = array_merge($hdr,array($bdy),$ftr);
var_dump($fixedResult);

результат:

array
  0 => string ' 635' (length=4)
  1 => string ' ' (length=1)
  2 => string 'AUBREY R. PHILLIPS (1920- ) - Pastel depicting cottages in a steep sided river valley, possibly North Wales, signed and dated 2000, framed, 66cm by 48cm. another of a rural landscape, titled verso Harvest Time" Somerset" signed and dated '87" framed" 69cm by 49cm. (2) NB - Aubrey Phillips is a Worcestershire artist who studied at the Stourbridge School of Art.' (length=362)
  3 => string '40' (length=2)
  4 => string '60' (length=2)
  5 => string 'WAT' (length=3)
  6 => string 'Paintings, prints and watercolours' (length=34)
  7 => string '' (length=0)

Не идеально, но, возможно, достаточно хорошо

Альтернативой является заставить любого, кто генерирует csv, правильно выйти из своих корпусов.

person Mark Baker    schedule 16.03.2012
comment
Это кажется немного хакерским, но если это единственный способ. Надеюсь, я не получу поврежденные данные в других полях, лол! Одна небольшая проблема, ваша содержит titled verso Harvest Time" Somerset" signed and dated, когда оригинал titled verso "Harvest Time, Somerset" signed and dated - person 472084; 16.03.2012

Если вы можете избежать «в тексте следующим образом: \»

и в fgetcsv используйте escape-символ

fgetcsv($file, 0, ',', '"','\');
person ab_dev86    schedule 16.03.2012

Это далеко не так, так что не принимайте меня всерьез.

Я заметил в тексте закономерность, что все "," которые вы хотите игнорировать, имеют пробел после него. Найдите и замените ',' на 'FUU' или что-то уникальное.

Теперь проанализируйте файл csv. Он может получить правильный формат. Вам нужно только заменить «FUU» обратно на «,»

:)

person heldt    schedule 16.03.2012

Вероятно, вы читаете содержимое CSV-файла в виде массива строк, а затем разделяете каждую строку запятой. Это не удается, поскольку некоторые поля также содержат запятые. Один прием, который может вам помочь, заключается в поиске ",", который указывает на разделитель полей, который маловероятен (но, к сожалению, не невозможен) в поле.

<?php
  $csv = file_get_contents("yourfile.csv");
  $lines = split("\r\n", $csv);
  echo "<pre>";
  foreach($lines as $line)
  {
    $line = str_replace("\",\"", "\"@@@\"", $line);
    $fields = split("@@@", $line);
    print_r($fields);
  }
  echo "</pre>";
?>
person Alexander van Oostenrijk    schedule 16.03.2012