Разберём ещё несколько практических задач из области разработки PHP парсеров, связанных с импортом, сортировкой и экспортом CSV данных. Часто требуется представить результаты парсинга в виде CSV файла, далее такой файл можно импортировать в базу данных сайта. Средства импорта CSV данных часто поддерживаются стандартными средствами различных CMS.
CSV (Comma-Separated Values) — текстовый формат, предназначенный для представления табличных данных. Из названия следует, что данные разделены запятыми, но могут использоваться и другие разделители, например, точка с запятой (DSV формат).
CSV файлы можно открывать разными текстовыми редакторами, а также программой EXCEL. Но бывают проблемы с кодировкой. Например, EXCEL плохо переваривает UTF-8 без BOM. Под EXCEL нужно данные просто в UTF-8 сохранять.
В тестовой задаче нам нужно отсортировать ряд CSV файлов по столбцу email. Файлы содержат списки организаций с различными контактными данными, при этом не у всех фирм есть электронная почта. Так, чтобы облегчить работу со списками удобно скриптом отсортировать списки и наверх поставить компании с известными почтовыми ящиками.
Скрипт index.php и каталог с входными данными следует разместитьть в папке csv-sorter.
Обработанные файлы будут записываться в директорию output.
Импорт и экспорт CSV данных на PHP
Листинг файла index.php
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Сортировка CSV файлов по заполненности полей</title>
</head>
<body>
<h1>Сортировка CSV файлов</h1>
<!--p>Файлы на выходе программы преобразуются в кодировку UTF-8.</p-->
<?php
/**
* Функция преобразования строки в CSV формате в массив
*
* @param string $filename, int $file_size, string $delimiter
*
* @return string
*/
function csv2array($filename='', $file_size, $delimiter=';') {
ini_set('auto_detect_line_endings', true);
if(!file_exists($filename) || !is_readable($filename)) {
return false;
}
$header = null;
$data = array();
if(($handle = fopen($filename, 'r')) !== false) {
while(($row = fgetcsv($handle, $file_size, $delimiter)) !== false) {
if(!$header) {
if($row[0] != 'sep=') {
$header = $row;
}
} else {
if (count($header) > count($row)) {
$difference = count($header) - count($row);
for ($i = 1; $i <= $difference; $i++) {
$row[count($row) + 1] = '';
}
}
}
if($row[0] != 'sep=') {
$data[] = $row;
}
}
fclose($handle);
}
return $data;
}
/**
* Функция сортировки массива по 1-му полю или N полей
*
* @param string|array $keys, string $order [ASC|DESC]
*
* @return int
*/
function sort_arr_ncol($keys, $order = 'ASC') {
$order = ($order == 'DESC') ? -1 : 1;
if(is_array($keys)) { //если сортировка по нескольким полям
return function($a, $b) use ($keys, $order) {
foreach($keys as $k) {
if($a[$k] != $b[$k]) {
return $order * (($a[$k] < $b[$k]) ? 1 : -1);
}
}
return 0;
};
} else { //если сортировка по одному полю
return function($a, $b) use ($keys, $order) {
if ($a[$keys] == $b[$keys]) {
return 0;
}
return $order * (($a[$keys] < $b[$keys]) ? 1 : -1);
};
}
}
/**
* Функция преобразования массива в строку в CSV формате
*
* @param @param string $input, int $file_size, string $delimiter, string $enclosure
*
* @return string
*/
function str_putcsv($input, $file_size, $delimiter = ';', $enclosure = '"') {
// Open a memory "file" for read/write...
$fp = fopen('php://temp', 'r+');
// ... write the $input array to the "file" using fputcsv()...
fputcsv($fp, $input, $delimiter, $enclosure);
// ... rewind the "file" so we can read what we just wrote...
rewind($fp);
// ... read the entire line into a variable...
$data = stream_get_contents($fp);
// ... close the "file"...
fclose($fp);
// ... and return the $data to the caller, with the trailing newline from fgets() removed.
return $data; //rtrim($data, "\r");
}
// Параметры сортировщика
$data_dir_name = "input"; // Каталог с исходными файлами
$res_dir_name = "output"; // Каталог с отсортированными файлами
$key_list_str = 'email'; // Название столбца для сортировки (с нуля)
if(!empty($_REQUEST['action']) && $_REQUEST['action'] = 'run') {
if(!isset($_REQUEST['charset'])) {
$charset = 'no';
} else {
$charset = $_REQUEST['charset'];
}
$k = 0;
$er = 0;
$error_file_names = array();
$all_file_count = 0;
$entries = scandir($data_dir_name);
foreach($entries as $entry) {
if(mb_strpos($entry, '.csv') !== false) { // Обрабатываем только CSV файлы
$filepath_in = $data_dir_name . "/" . $entry;
$file_size = filesize($filepath_in);
$csv_data_arr = csv2array($filepath_in, $file_size);
$header_arr[0] = array_shift($csv_data_arr);
$key_list = array_keys($header_arr[0], $key_list_str);
usort($csv_data_arr, sort_arr_ncol($key_list, 'ASC'));
$csv_data_arr = array_merge($header_arr, $csv_data_arr);
// Формируем строку для CSV файла
$res_csv_file = '';
foreach($csv_data_arr as $key_row => $csv_data_arr_row) {
$res_csv_file .= str_putcsv($csv_data_arr_row, $file_size);
}
if($charset == 'yes') {
$res_csv_file = iconv("WINDOWS-1251", "UTF-8", $res_csv_file);
if($res_csv_file == false) {
$res_csv_file = iconv("WINDOWS-1251", "UTF-8//IGNORE", $res_csv_file);
$error_file_names[] = $entry;
}
}
$filepath_out = $res_dir_name . "/" . $entry;
file_put_contents($filepath_out, $res_csv_file) ? $k++ : $er++;
$all_file_count++;
}
}
echo "<div id=\"stat_block\">Обработано файлов: <span>" . $k . "</span> из <span>" . $all_file_count . "</span>. Ошибок: <span>" . $er . "</span></div>";
echo "<div id=\"error_files\"><p>" . implode("</p><p>", $error_file_names) . "</p></div>";
}
?>
<div><a href="/csv-sorter/">Перезагрузка</a> :: <a href="/csv-sorter/?action=run">Запуск</a>...</div>
</body>
</html>
Алгоритм работы следующий. Считываются файлы из заданного каталога. Далее данные преобразуются из CSV формата в PHP массив — это по сути импорт CSV.
Функция csv2array($filepath_in, $file_size) считывает CSV данные из файла и преобразует их в массив с помощью стандартной функции PHP fgetcsv().
Далее массив сортируется по заданному полю с помощью функции sort_arr_ncol(). Полезная штука в разных задачах для сортировки многомерных массивов по столбцам.
Далее обработанные данные экспортируются обратно в файл. Вначале преобразуем PHP массив в строку в формате CSV при помощи функции str_putcsv() и записываем результат в файл.
Список компаний до сортировки:
Пример файла после сортировки по столбцу email:
Теперь мы с вами умеем сохранять результаты парсинга не только в JSON формате, но и в CSV. А уже с CSV могут работать много разных программ и движков сайтов. Например, часто с помощью CSV файлов экспортируются и импортируются товары в интернет-магазинах.



Это работает, отлично! Но, как это запустить из консоли?
/usr/bin/php /home/public_html/csv-sorter/?action=run
Не получается, с амерсандом вместо знака вопроса, тоже…
Для запуска из консоли этот скрипт не приспособлен. Вам интерфейс получается совсем не нужен. Самый простой вариант дописать в начало скрипта .
Либо разобраться с передачей переменных в консоли и сделать замену переменных в скрипте:
/usr/bin/php/home/public_html/csv-sorter/ run no
То в скрипте нужно определить переменные
$params[‘action’] = $argv[1]; // =run
$paras[‘charset’] = $argv[2]; // =no
Массив $params[] будет вместо $_REQUEST[] в данном случае.
Тоже задаюсь таким вопросом. Получилось разобраться?
Здравствуйте.
А, возможно ли спарсить с удаленного сервера (по FTP) нужные для преобразования CSV/XML файлы, чтобы в итоге получить на выходе один CSV?
В PHP есть функция для скачивания файлов по FTP ftp_get().