- Чтение и запись данных в CSV-файл средствами PHP
- Как это работает?
- Пример записи данных
- Пример чтения данных
- Список возможных режимов для fopen()
- Комментарии
- CSV файл на PHP — создание и чтение
- Формат CSV
- Создание CSV файла в PHP
- Чтение CSV файла в PHP
- Конвертация .lsx , .xlsx файла в .csv
- Как отдать сгенерированный CSV файл для загрузки
Чтение и запись данных в CSV-файл средствами PHP
Обычно, если стоимость создания интернет магазина достаточно высока или имеет большое количество товаров, заказчики просят сделать импорт товаров из их прайс листов. PHP как известно, не умеет работать с Excel-файлами стандартными средствами. Конечно есть множество различных классов и библиотек, которые могут читать и записывать информацию в Excel, но с ними возникает достаточно много проблем и неудобств. Гораздо проще и удобнее работать с CSV-файлами. Предлагаю разобрать простой способ чтения и записи данных в CSV-файл средствами PHP.
Как Вы знаете, CSV — текстовый формат, предназначенный для представления табличных данных. Каждая строка файла — это одна строка таблицы. Значения отдельных колонок разделяются разделительным символом — обычно запятой (,).
Как это работает?
Пример записи данных
Первым делом мы формируем двумерный массив значений $list. Функция fopen()закрепляет именованный ресурс, указанный в аргументе filename, за потоком, а также указываем режим работы с файлом. Со списком режимов работы с файлом Вы можете ознакомиться в конце статьи.
Запись в файл производится построчно, поэтому имея двумерный массив мы вызываем функцию записи fputcsv() в цикле.Функция fputcsv() форматирует строку (переданную в виде массива) в виде CSV и записывает её (заканчивая переводом строки) в указанный файл. Как видите, в этой функции мы указываем файл $fp, в который производим запись, строку $fields, которая содержит значения полей, а также такие параметры, как разделитель полей (в нашем случае это точка с запятой) и ограничитель полей (у нас это двойная кавычка).
После окончания записи, не забываем закрывать дескриптор файла fclose().
Пример чтения данных
С помощью функции fopen() мы пытаемся открыть файл для чтения и, если он есть, построчно заполняем массив данными с помощью функции fgetcsv(). Функция fgetcsv() читает строку из файла и производит разбор данных CSV. Данная функция похожа на функцию fgets(), с той разницей, что она производит анализ строки на наличие записей в формате CSV и возвращает найденные поля в качестве массива. В эту функцию мы передаем такие параметры как корректный файловый указатель на успешно открытый файл, длину строки (обычно указывается длина большая самой длинной строки в файле, в нашем случае мы указываем 0, т.е. длина не ограничена), а также разделитель поля (т.к. в примере для записи мы использовали точку с запятой, то и для чтения будем использовать её).
И опять же, после выполнения чтения из файла не забываем закрывать дескриптор файла fclose().
Список возможных режимов для fopen()
‘r’ — Открывает файл только для чтения; помещает указатель в начало файла.
‘r+’ — Окрывает файл для чтения и записи; помещяет указатель в начало файла.
‘w’ — Открывает файл только для записи; помещает указатель в начало файла и обрезает файл до нулевой длинны. Если файл не существует — пробует его создать.
‘w+’ — Открывает файл для чтения и записи; помещает указатель в начало файла и обрезает файл до нулевой длинны. Если файл не существует — пробует его создать.
‘a’ — Открывает файл только для записи; помещает указатель в конец файла. Если файл не существует — пробует его создать.
‘a+’ — Открывает файл для чтения и записи; помещает указатель в конец файла. Если файл не существует — пробует его создать.
‘x’ — Создаёт и открывает только для записи; помещает указатель в начало файла. Если файл уже существует, вызов fopen() закончится неудачей, вернёт FALSE и выдаст предупреждение уровня E_WARNING. Если файл не существует, пытается его создать.
‘x+’ — Создаёт и открывает для чтения и записи; помещает указатель в начало файла. Если файл уже существует, вызов fopen() закончится неудачей, вернёт FALSE и выдаст предупреждение уровня E_WARNING. Если файл не существует, пытается его создать.
Как видите, мы разобрали лишь теорию, но если вынести чтение из файла и запись в файл отдельными функциями, правильно организовать обработку ошибок, то можно реализовать неплохой функционал по импорту/экспорту товаров в свой интернет магазин с возможностью добавления новых товаров и обновления имеющихся. Самое главное, не забывайте про кодировку импортируемых/экспортируемых данных, а также правильно выбирайте разделитель поля, т.к. бывают ситуации, когда выбранный разделитель может встретиться просто в тексте, который Вы пытаетесь сохранить в CSV-файл, в результате чего все данные в такой строке могут отображаться некорректно.
Комментарии
Здесь еще никто не оставлял комментарии.
CSV файл на PHP — создание и чтение
CSV очень удобный формат с точки зрения генерации, поскольку он очень просто устроен. В этой заметке разберемся как устроены файлы с расширением .csv , как их создавать и разбирать (парсить) в PHP. Делается это очень просто. Ниже приведены простые PHP функции для создания и парсинга csv файлов. Никаких библиотек — для CSV это лишнее!
Формат CSV
Чтобы понимать суть вещей, нужно разобраться в спецификации CSV файлов, как устроен формат. Давайте коротко.
CSV (Comma-Separated Values — значения, разделённые запятыми) — текстовый формат, предназначенный для представления табличных данных.
- Каждая строка файла — это одна строка таблицы.
- Разделителем значений колонок является символ: , (запятая). Для русского языка используется ; (точка с запятой), потому что в русском запятая используется в дробных числах.
- Значения, содержащие зарезервированные символы: » , ; \r\n или \n или \r (двойная кавычка, запятая, точка с запятой, новая строка) обрамляются двойными кавычками » .
- Если в значении встречаются двойные кавычки » , то они должны выглядеть как двое кавычек подряд «» .
- Строка файла может разделяться символами: \r\n или \n .
Это все что нужно знать, чтобы работать с CSV!
1965;Пиксель;E240 – формальдегид (опасный консервант)!;"красный, зелёный, битый";3000,00 1965;Мышка;"А правильней использовать ""Ёлочки""";;4900,00 "Н/д";Кнопка;Сочетания клавиш;"MUST USE! Ctrl, Alt, Shift";4799,00
1997,Ford,E350,"ac, abs, moon",3000.00 1999,Chevy,"Venture «Extended Edition»","",4900.00 1996,Jeep,Grand Cherokee,"MUST SELL! air, moon roof, loaded",4799.00
Wiki-справка
Большинство программ под CSV понимают более общий формат DSV (delimiter-separated values — значения разделённые разделителем), допускающий использование иных символов в качестве разделителя. В частности, в русской и других локалях запятая по умолчанию зарезервирована под десятичный разделитель. Поэтому как разделитель используется точка с запятой или табуляция (формат TSV).
Сегодня под CSV понимают набор значений, разделенных какими угодно разделителями, в какой угодно кодировке с какими угодно окончаниями строк. Это значительно затрудняет перенос данных из одних программ в другие, несмотря на всю простоту формата.
Создание CSV файла в PHP
Для создания CSV файла, достаточно создать текстовый файл, используя в нужных местах необходимые разделители столбцов и строк.
Важным моментом, является кодировка файла. Для корректного отображения кириллицы следует использовать кодировку cp1251 (windows-1251).
Разделитель колонок
Для русского языка символом-разделителем является ; (точка с запятой). Для англ. , (запятая).
Строки содержащие спец символы: » , ; \r\n или \n или \r должны быть в двойных кавычках «строка» .
Двойные кавычки внутри строки, нужно «очистить» поставив перед кавычкой еще одну такую же кавычку: строка «csv» превратиться в «строка «»csv»»» . Обрамление в кавычки нужно, чтобы можно было внутри значений колонок использовать разделители ; , , и не переживать что что-то сломается при чтении файла.
Разделитель строк
Для разделения строк в csv файлах можно использовать \r\n (возврат каретки и перенос строки, CR LF). В этом случае, если нужен перенос строки внутри значения колонки, то там используется просто \n .
Также, для разделения строки, может использоваться просто \n (перенос строки, LF). В этом случае, перенос строки внутри значения колонки должен обозначаться как \r (возврат каретки CR).
Функция для создания CSV файла
/** * Создает CSV файл из переданных в массиве данных. * * @param array $create_data Массив данных из которых нужно созать CSV файл. * @param string $file Путь до файла 'path/to/test.csv'. Если не указать, то просто вернет результат. * @param string $col_delimiter Разделитель колонок. Default: `;`. * @param string $row_delimiter Разделитель рядов. Default: `\r\n`. * * @return false|string CSV строку или false, если не удалось создать файл. * * @version 2 */ function kama_create_csv_file( $create_data, $file = null, $col_delimiter = ';', $row_delimiter = "\r\n" ) < if( ! is_array( $create_data ) )< return false; >if( $file && ! is_dir( dirname( $file ) ) ) < return false; >// строка, которая будет записана в csv файл $CSV_str = ''; // перебираем все данные foreach( $create_data as $row ) < $cols = array(); foreach( $row as $col_val )< // строки должны быть в кавычках "" // кавычки " внутри строк нужно предварить такой же кавычкой " if( $col_val && preg_match('/[",;\r\n]/', $col_val) )< // поправим перенос строки if( $row_delimiter === "\r\n" )< $col_val = str_replace( [ "\r\n", "\r" ], [ '\n', '' ], $col_val ); >elseif( $row_delimiter === "\n" ) < $col_val = str_replace( [ "\n", "\r\r" ], '\r', $col_val ); >$col_val = str_replace( '"', '""', $col_val ); // предваряем " $col_val = '"'. $col_val .'"'; // обрамляем в " > $cols[] = $col_val; // добавляем колонку в данные > $CSV_str .= implode( $col_delimiter, $cols ) . $row_delimiter; // добавляем строку в данные > $CSV_str = rtrim( $CSV_str, $row_delimiter ); // задаем кодировку windows-1251 для строки if( $file ) < $CSV_str = iconv( "UTF-8", "cp1251", $CSV_str ); // создаем csv файл и записываем в него строку $done = file_put_contents( $file, $CSV_str ); return $done ? $CSV_str : false; >return $CSV_str; >
Теперь, чтобы сгенерировать CSV файл нужно использовать эту функцию так:
$create_data = array( array( 'Заголовок 1', 'Заголовок 2', 'Заголовок 3', ), array( 'строка 2 "столбец 1"', '4799,01', 'строка 2 "столбец 3"', ), array( '"Ёлочки"', 4900.01, 'красный, зелёный', ) ); echo kama_create_csv_file( $create_data, THEME_PATH .'csv_file.csv' ); /* Получим Заголовок 1;Заголовок 2;Заголовок 3 "строка 2 ""столбец 1""";"4799,00";"строка 2 ""столбец 3""" """Ёлочки""";4900.01;"красный, зелёный" */
Чтение CSV файла в PHP
Когда нужно получить данные из CSV файла, т.е. разобрать его и получить данные в переменную, можно использовать встороенную в PHP функцию str_getcsv() .
Есть еще функция fgetcsv() , но она оказалась капризной и не всегда работает как нужно (может перепутать переносы строк).
Вариант на базе функции str_getcsv() :
/** * Читает CSV файл и возвращает данные в виде массива. * * @param string $file_path Путь до csv файла. * @param array $file_encodings * @param string $col_delimiter Разделитель колонки (по умолчанию автоопределине) * @param string $row_delimiter Разделитель строки (по умолчанию автоопределине) * * @version 6 */ function kama_parse_csv_file( $file_path, $file_encodings = ['cp1251','UTF-8'], $col_delimiter = '', $row_delimiter = '' ) < if( ! file_exists( $file_path ) )< return false; >$cont = trim( file_get_contents( $file_path ) ); $encoded_cont = mb_convert_encoding( $cont, 'UTF-8', mb_detect_encoding( $cont, $file_encodings ) ); unset( $cont ); // определим разделитель if( ! $row_delimiter ) < $row_delimiter = "\r\n"; if( false === strpos($encoded_cont, "\r\n") ) $row_delimiter = "\n"; >$lines = explode( $row_delimiter, trim($encoded_cont) ); $lines = array_filter( $lines ); $lines = array_map( 'trim', $lines ); // авто-определим разделитель из двух возможных: ';' или ','. // для расчета берем не больше 30 строк if( ! $col_delimiter ) < $lines10 = array_slice( $lines, 0, 30 ); // если в строке нет одного из разделителей, то значит другой точно он. foreach( $lines10 as $line )< if( ! strpos( $line, ',') ) $col_delimiter = ';'; if( ! strpos( $line, ';') ) $col_delimiter = ','; if( $col_delimiter ) break; >// если первый способ не дал результатов, то погружаемся в задачу и считаем кол разделителей в каждой строке. // где больше одинаковых количеств найденного разделителя, тот и разделитель. if( ! $col_delimiter )< $delim_counts = array( ';'=>array(), ','=>array() ); foreach( $lines10 as $line ) < $delim_counts[','][] = substr_count( $line, ',' ); $delim_counts[';'][] = substr_count( $line, ';' ); >$delim_counts = array_map( 'array_filter', $delim_counts ); // уберем нули // кол-во одинаковых значений массива - это потенциальный разделитель $delim_counts = array_map( 'array_count_values', $delim_counts ); $delim_counts = array_map( 'max', $delim_counts ); // берем только макс. значения вхождений if( $delim_counts[';'] === $delim_counts[','] ) return array('Не удалось определить разделитель колонок.'); $col_delimiter = array_search( max($delim_counts), $delim_counts ); > > $data = []; foreach( $lines as $key => $line ) < $data[] = str_getcsv( $line, $col_delimiter ); // linedata unset( $lines[$key] ); >return $data; >
$data = kama_parse_csv_file( '/path/to/file.csv' ); print_r( $data );
Конвертация .lsx , .xlsx файла в .csv
Чтобы перевести Excel файл в CSV, нужно открыть его в Excel и сохранить в формате .csv :
Если такую конвертацию нужно сделать программно, смотрите в сторону онлайн конвертеров с API или готовых библиотек.
Сталкивались с такой задачей и знаете более универсальный способ? Прошу поделиться в комментариях.
Я столкнулся и потратил несколько часов перебирая всякий не универсальный «мусор» из гугла — то одно не работает, то другое, то библиотеку нужно ставить «хитро-мудрую». Так и появилась эта заметка.
Как отдать сгенерированный CSV файл для загрузки
Для этого нужно установить заголовки: