прочитать текстовый файл построчно php
Работа с файловой системой
Как и большинство языков программирования, PHP поддерживает работу с файлами, которые являются одним из способов хранения информации.
Чтение и запись файлов
Открытие и закрытие файлов
‘r’ : файл открывается только для чтения. Если файла не существует, возвращает false
‘r+’ : файл открывается только для чтения с возможностью записи. Если файла не существует, возвращает false
‘a’ : файл открывается для записи. Если такой файл уже существует, то данные записываются в конец файла, а старые данные остаются. Если файл не существует, то он создается
‘a+’ : файл открывается для чтения и записи. Если файл уже существует, то данные дозаписываются в конец файла. Если файла нет, то он создается
Результатом функции fopen будет дескриптор файла. Этот дескриптор используется для операций с файлом и для его закрытия.
Конструкция or die(«текст ошибки») позволяет прекратить работу скрипта и вывесте некоторое сообщение об ошибке, если функция fopen не смогла открыть файл.
Чтение файла
Чтение файла полностью
Если нам надо прочитать файл полностью, то мы можем облегчить себе жизнь, применив функцию file_get_contents() :
При этом нам не надо открывать явно файл, получать дескриптор, а затем закрывать файл.
Поблочное считывание
Также можно провести поблочное считывание, то есть считывать определенное количество байт из файла с помощью функции fread() :
Функция fread() принимает два параметра: дескриптор считываемого файла и количество считываемых байтов. При считывании блока указатель в файле становится в конец этого блока. И также с помощью функции feof() можно отследить завершение файла.
Запись файла
Аналогично работает другая функция fputs() :
Работа с указателем файла
При открытии файла для чтения или записи в режиме ‘w’, указатель в файле помещается в начало. При считывании данных PHP перемещает указатель в файле в конец блока считанных данных. Однако мы также вручную можем управлять указателем в файле и устанавливать его в произвольное место. Для этого надо использовать функцию fseek, которая имеет следующее формальное определение:
SEEK_SET : значение по умолчанию, устанавливает смещение в offset байт относительно начала файла
SEEK_CUR : устанавливает смещение в offset байт относительно начала текущей позиции в файле
SEEK_END : устанавливает смещение в offset байт от конца файла
Чтение файла в PHP. Выбираем оптимальный вариант
Приветствую вас, друзья! 🙂
Думаю, что, если не все, то, уж точно большинство из вас сталкивались на практике с необходимостью чтения информации из txt файлов на уровне серверных скриптов. У меня, по крайней мере, таких случаев было несколько, о последнем из которых я вам сегодня и расскажу.
Ничего в этом сложного нет, но иногда глаза разбегаются от обилия вариантов, предоставляемых средствами серверных языков. Если говорить конкретно о PHP, на котором я сейчас программирую, то с помощью его функций можно считывать содержимое файлов и построчно, и целиком в строку, и в массив, причём для последнего варианта существует ещё несколько способов… Вот такие пироги 🙂
К сожалению только, данные методы работают с различной скоростью для файлов разной структуры, и о скорости их работы нет ни единого слова в официальной документации; об этом можно судить лишь на практике, перебирая все возможные варианты.
Поэтому, сегодня я продемонстрирую вам работу различных функций PHP для чтения файлов, чтобы, когда вам нужно будет создать PHP парсер файла для решения реальных задач, вы знали, из чего выбирать. А также подскажу, как именно в «боевых условиях» сделать правильный выбор.
Создаём PHP парсер файла — начальные условия
Перед тем, как мы начнём, пару слов о задаче, для которой я создавал парсер файла на PHP, а затем выбирал из реализованных вариантов оптимальный.
Однажды у меня на работе возникла проблема, которая заключалась в том, что в БД хранились телефоны пользователей в неверном формате. Сам баг я, естественно, без проблем пофиксил.
Но, что делать с неверной информацией, которая на тот момент уже хранились в базе данных? Естественно, её нужно было заменить на корректную.
Для этого мне был предоставлен текстовый файл с идентификаторами пользователей и их телефонами, которые нужно было перенести в БД.
Должен сказать, он получился весьма увесистым: 352 Кбайта и 8223 строки текста, в каждой из которых содержался идентификатор пользователя и его телефон в формате id_пользователя:номер_телефона.
Словом, вся задача заключалась в построчном чтении файла PHP средствами, выделения из строки идентификатора и телефона с последующим обновлением значения телефона у пользователя в БД, найденного по айдишнику.
Мой проект был реализован на PHP фреймворке Yii, следовательно в дальнейших примерах кода вы встретите элементы его API для работы с БД, в частности, поэтому не пугайтесь 🙂
После анализа имеющихся в языке конструкций, а также опыта других разработчиков, по крупицам собранного в Интернете, мне удалось выделить 4 способа, которые я далее вам и продемонстрирую.
Ну, а после я расскажу, по каким критериям и как именно я выбирал среди них оптимальный вариант. И, естественно, поделюсь результатами 🙂
Так что данная статья — отличная тренировка терпеливости 🙂 Суть её будет заключаться в подробном изучении следующего материала вплоть до результатов, которые будут ждать вас в конце. По ходу, кстати, можете поработать ещё и над фантазией, предполагая, как именно будет выбираться идеальный вариант.
Чтение файла в PHP построчно с помощью fgets()
Для того, чтобы прочитать файл построчно, в PHP есть специальная функция fgets(). Чтобы с её помощью считать содержимое всего файла, её нужно вызывать в цикле, проходясь по всем строкам.
В итоге, PHP парсер файла, реализующий данный алгоритм, у меня принял следующий вид:
Немного расшифрую свою писанину, если у кого-то возникнут сложности в понимании.
В самом начале, переменной $filename присваивается значение имени файла, который будет парситься, с полным путём к нему. Далее следуют PHP проверка существования файла и читаем ли он с помощью функций file_exists() и is_readable() соответственно.
Если всё ОК, то открываем файл с помощью функции fopen(), которая вызывается с PHP оператором управления ошибками для того, чтобы отключить вывод ошибок, генерируемых данной функцией. Использовать я его решил, чтобы сгенерировать своё сообщение об ошибке вместо стандартного.
Если файл открыть получилось, то мы проходимся по всем его строкам в цикле, пока файл не закончится, и, если строка не пустая, разделяем её по символу двоеточия функцией explode().
Затем проверяем, что id пользователя и его телефон не пустые, ищем пользователя в БД по айдишнику и, если таковой существует, то обновляем ему номер телефона, убрав из значения номера предварительно символы переноса и начала новой строки.
Ну, и ещё я использовал PHP функции strtolower() и strtoupper() для проверки существования в БД пользователя с идентификаторами, которые могли быть прописаны в различных регистрах, т.к. они в моём случае состояли из символов и цифр.
Далее по коду следуют различные сообщения об ошибках, которые могут возникнуть на разных этапах, а также функция закрытия файла — fclose().
PHP парсинг файла в массив с помощью file()
Данный метод чтения файла в PHP предполагает использование функции file(), которая открывает файл и помещает его содержимое в массив. При этом элементами массива будут являться, как раз, строки считываемого файла, что в моей ситуации отлично подходит.
Код данного варианта PHP парсера файла получился следующий:
Как видите, от предыдущего способа чтения файла в PHP данный отличается только своим началом, где файл открывается и сразу же считывается функцией file() вместо связки fopen() + fgets(), как ранее.
Далее код такой же.
PHP чтение файла в переменную с помощью fread()
Ещё одной функцией PHP для разбора файла является fread(), с помощью которой можно читать различные фрагменты файла указанной длины. Чтобы прочитать файл в PHP целиком, в качестве размера фрагмента я указал размер файла, полученный с помощью функции filesize():
Данный способ чтения файла PHP средствами, на самом деле, очень похож на предыдущий, т.к., несмотря на то, что с помощью PHP данные из файла изначально считываются не в массив, а в строковую переменную, далее она всё равно преобразуется в массив, т.к. с ним проще работать, чем со строкой.
Преобразование строки в массив на PHP проще всего сделать с помощью уже применявшейся сегодня функции explode(), в качестве разделителя в которую был передан символ начала строки.
А дальше всё идёт по накатанной 🙂
Создаём PHP парсер файла на базе file_get_contents()
Ну, и напоследок, я решил реализовать PHP парсинг файла с помощью функции file_get_contents(), которая, как раз и предназначена для чтения файла целиком в строку, т.е. работает, практически, как fread($fp, filesize($filename)).
За тем лишь исключением, что file_get_contents() самостоятельно открывает файл и считывает его, в то время как для использования fread() нужно было предварительно открыть файл через fopen() и получить его указатель для дальнейшего использования.
В целом, код PHP парсера файла на базе file_get_contents() будет практически как и в предыдущем случае:
На этом всё. Пришло время подвести итоги производительности всех перечисленных вариантов и выяснить, какой же PHP парсер файла оказался самым оптимальным для дальнейшего использования.
Какой способ обработки файлов в PHP является оптимальным?
Чтобы выбрать из найденных вариантов самый оптимальный, т.е. самый быстрый, я решил определить время выполнения скрипта PHP в каждом случае. Для этого я воспользовался методикой, описанной в статье по ссылке.
Сами по себе PHP функции чтения файлов достаточно шустрые, поэтому, чтобы добиться хоть каких-то более-менее осязаемых цифр времени их работы, я специально оставил в тестируемых фрагментах операции с базой данных, которые во всех случаях были одни и те же.
Время работы PHP скрипта я также решил для удобства округлять до третьего знака после запятой, т.е. до тысячных долей секунд (хотя, можно было ограничиться и сотыми, на самом деле).
Помню, когда я учился в школе и писал свою научную работу по физике (да, был такой опыт 🙂 ) на её защите перед университетскими преподавателями меня постоянно упрекали за недостаточное количество экспериментов (я делал по 3 опыта для каждого случая). «Светилы науки» называли цифры в 100, ну или, хотя бы, в 10 экспериментов для сравнения различных ситуаций, чтобы можно было делать какое-то их сопоставление и минимизировать вероятность случайного превосходства одного над другим.
Да, досталось мне тогда от них крепко, но их рекомендации я хорошо усвоил, что даже сейчас об этом помню, хотя прошло уже более 10 лет с тех пор. Тем более, что данные рекомендации действительно были основаны на законах математической статистики и теории вероятности.
Ну, на научность своих нынешних экспериментов я в данной статье не претендую, поэтому число в 100 экспериментов я посчитал излишне большим, а процесс их проведения — слишком утомительным занятием.
В итоге, я решил ограничиться 10 экспериментами для каждого варианта PHP парсера файла, чего, как оказалось в итоге, оказалось вполне достаточно, чтобы выделить явного лидера без всякой подтасовки фактов и зацепок за сотые и тысячные доли секунды превосходства.
Результаты вычислений времени работы разработанных мною PHP парсеров файла представлены в следующей таблице и рассортированы по PHP функциям, на базе которых они работают.
Эксперимент | fgets() | file() | fread() | file_get_contents() |
1 | 9,147 | 9,722 | 10,539 | 2,008 |
2 | 8,950 | 9,006 | 9,495 | 1,733 |
3 | 8,821 | 8,845 | 9,207 | 1,642 |
4 | 8,717 | 8,876 | 8,931 | 1,758 |
5 | 9,010 | 9,091 | 8,703 | 1,635 |
6 | 9,110 | 8,640 | 9,712 | 1,633 |
7 | 9,074 | 9,626 | 9,13 | 1,645 |
8 | 8,886 | 9,204 | 9,048 | 1,701 |
9 | 8,667 | 8,918 | 9,438 | 1,713 |
10 | 8,852 | 9,197 | 9,537 | 1,567 |
Среднее | 8,923 | 9,113 | 9,374 | 1,704 |
Как видите, помимо значений времени выполнения скрипта в каждом из 10 экспериментов, я решил подсчитать среднюю температуру по больнице 🙂
А именно, арифметическое среднее время работы каждого PHP парсера файла, чтобы можно было выявить лидера.
И им оказался, как видите, последний вариант, реализованный на базе функции file_get_contents(), который выполняет чтение содержимого файла в строковую переменную с дальнейшим его преобразованием в массив и обработкой в цикле.
Все остальные варианты PHP парсеров файлов работают примерно с одинаковой скоростью.
Почему именно он обогнал своих конкурентов я, если честно, не имею ни малейшего понятия. Могу лишь предположить, что операция чтения файла в строку с помощью file_get_contents() требует меньше ресурсов, чем формирование готового массива строк с помощью file().
А превосходство над fgets() и fread() можно списать на то, что перед их использованием требуется открытие файла с помощью fopen(), на что требуется время.
Да, на самом деле, это и не важно, т.к. цифры говорят сами за себя: благодаря использованию функции file_get_contents() PHP парсер файла на его базе работает в 5 раз быстрее остальных, что и повлияло на моё решение использовать его на практике.
Разбор файла в PHP — выводы
Как я уже и говорил в начале, мои опыты не являются безупречными и опираться исключительно на полученные в их ходе результаты не стоит, т.к., несмотря на быстродействие file_get_contents() в моей ситуации, бывают случаи, когда намного удобнее и эффективнее использовать другие приведённые мною PHP парсеры файлов.
Кроме того, не стоит забывать, что PHP сам по себе является синхронным языком программирования, т.е. все серверные операции происходят последовательно без возможности настройки их параллельного выполнения, в том числе, и на разных ядрах серверного процессора.
Следовательно, на время выполнения операций, прописанных в PHP коде, может влиять целый ряд факторов, среди которых основным является нагруженность ядра в момент работы PHP приложения.
Я это особенно ощутил во время проведения опытов, когда один и тот же PHP парсер файла отработал за 9, затем за 12, а потом снова за 9 секунд на трёх последовательных итерациях из-за банального запуска проводника Windows во время второго случая, который, естественно, тоже требует серверных ресурсов.
Учитывая данные особенности, я проводил эксперименты практически одновременно, друг за другом, при одинаковом комплекте запущенных программ, чтобы не распылять ресурсы серверного железа.
Поэтому в дальнейшем, при проведении подобных экспериментов с PHP конструкциями действуйте аналогичным образом, т.к. это, по сути, единственный способ привести эксперименты к равным условиям.
Если же вы будете работать с асинхронными серверными языками (C#, Java) или технологиями (Node.js, например), то, по возможности, для экспериментов создавайте отдельный поток, который будет работать на выделенном ядре процессора.
Ну, а если найти полностью незадействованное ядро не получится (что при уровне современного ПО не удивительно), то вы хотя бы сможете найти самое слабонагруженное или, хотя бы, со статической нагрузкой, которая не меняется во времени.
Надеюсь, что мои наблюдения и рекомендации будут вам полезны, равно как и мои сегодняшние эксперименты с PHP парсерами файлов.
Подытоживая, хочу сказать, что приведённые в статье фрагменты кода могут использоваться не только для парсинга текстовых файлов в PHP, но и отлично подойдут для других форматов, например, для разбора CSV файлов дампа базы данных MySQL.
Пишите ваши отзывы, как положительные, так и отрицательные в комментариях под статьёй — мне необходимо любое ваше мнение для дальнейшего развития 🙂
Также буду благодарен, если поделитесь данной статьёй со своими друзьями в социальных сетях с помощью кнопочек ниже.
P.S.: если вам нужен сайт либо необходимо внести правки на существующий, но для этого нет времени и желания, могу предложить свои услуги.
Более 5 лет опыта профессиональной разработки сайтов. Работа с PHP, OpenCart, WordPress, Laravel, Yii, MySQL, PostgreSQL, JavaScript, React, Angular и другими технологиями web-разработки.
Опыт разработки проектов различного уровня: лендинги, корпоративные сайты, Интернет-магазины, CRM, порталы. В том числе поддержка и разработка HighLoad проектов. Присылайте ваши заявки на email cccpblogcom@gmail.com.
И с друзьями не забудьте поделиться 😉
Учимся работать с файлами в PHP
В данном уроке мы рассмотрим несколько способов работы с файлами в PHP, а также поговорим о преимуществах и недостатках каждого из них.
Построчное чтение из файла
Для начала давайте научимся читать файлы. Давайте создадим текстовый файл file.txt и запишем в него следующий текст:
Давайте теперь считаем его с помощью программы на языке php. Создайте файл files.php и запишите в него следующий код:
Если вы сейчас запустите этот скрипт, то увидите на экране четверостишие из файла.
Давайте теперь разберём по шагам нашу программу.
Как вы понимаете, для чтения не очень удобно использовать цикл for с указанием конкретного числа строк, которые необходимо считать. Для этого можно использовать функцию feof – она возвращает true, если достигнут конец файла, и false – если нет.
На вход в качестве аргумента она принимает ресурс файла. Программа, переписанная с использованием функции feof будет выглядеть следующим образом:
Цикл будет выполняться до тех пор, пока не достигнут конец файла. При этом функция fgets каждый раз сдвигает курсор на следующую строку. Как только курсор достигнет конца файла, цикл завершится. Таким образом мы не привязаны к числу строк и можем читать файл не задумываясь об этом.
Построчная запись в файл
Теперь запишем данные в файл. Для того чтобы открыть файл для записи используется та же функция fopen, только в качестве режима работы указывается ‘w’ (от write).
А для записи в файл строки используется функция fputs(). Первым аргументом указывается ресурс, а вторым — строка, которую необходимо записать в файл. Давайте в качестве примера напишем программу, которая запишет в файл file2.txt числа от 1 до 100.
Константа PHP_EOL содержит в себе символ переноса строки. При этом для разных операционных систем (Windows или Unix-подобных) эти символы будут разными.
Дозаписываем в конец файла
При этом если сейчас запустить программу снова, то старые данные в файле file2.txt перезапишутся новыми. Для того, чтобы сохранить содержимое файла и дозаписать данные в конец, нужно использовать режим работы с файлом “a” (от append – присоединять, добавлять).
Если мы запустим этот скипт дважды, то в файле file3.txt будет две строки “abc”.
Читаем файл целиком
В PHP также имеется возможность прочитать весь файл за раз. Для этого используется функция file_get_contents().
Данный код выведет всё то же четверостишие, только без переноса строк.
Так произошло потому, что в самом первом примере мы добавляли тег
после каждой прочитанной строки. Если же мы откроем исходный код страницы, то увидим, что на самом деле переносы строк в исходном коде сохранены.
Запись в файл данных целиком
Для того, чтобы записать в файл большой объем данных за раз — сразу несколько строк, можно воспользоваться функцией file_put_contents.
Если снова запустить этот код, файл перезапишется. Для того, чтобы дополнить файл у этой функции есть третий аргумент — режим работы с файлом. Для дозаписи в конец файла следует использовать константу FILE_APPEND.
Какой способ выбрать
Данные способы отличаются в первую очередь тем, что когда мы считываем файл целиком, или записываем в него много данных за раз, нам приходится хранить в оперативной памяти больше данных, чем если бы мы работали по отдельности с каждой строкой. Если данных не слишком много, об этом можно не беспокоиться. Однако, если вы читаете файл размером 10 Гб, а на компьютере, на котором выполняется скрипт, всего 4Гб оперативной памяти, то его получится считать только построчно. При попытке загрузить его целиком программа упадёт с ошибкой о нехватке памяти.
Чтение и запись данных в файле
При изучении данной темы нужно учитывать, что для обращения к файлу одни PHP функции используют дескриптор файла, а другие путь к файлу. Для получения дескриптора нужно открыть файл. Это рассматривалось в предыдущей теме.
Чтение из файла
Функцию fread() можно использовать для получения определённого количества данных из файла.
Если конец файла будет достигнут раньше, чем функция прочитает указанный размер данных, то она вернёт строку меньшего размера. Данную функцию удобно применять для чтения бинарных файлов.
При указании длины строки и при перемещении указателя нужно учитывать, что русские буквы имеют размер не один байт, а больше. Подробнее об этом сказано в теме «Работа со строками». Также не забудте, что в начале файла в кодировке UTF-8 есть несколько байт. В кодировке UTF-8 без BOM эти байты отсутствуют.
Прочитаем перыве 10 байт из файла myfile.txt. Откроем его в режиме ‘r+’. Указатель будет находится в начале.
Если в файле присутствуют русские буквы, то последняя буква может отображаться неправильно. Это происходит потому, что символ имеет размер больше одного байта и одна часть прочитана, а вторая нет.
Функция fgets() возвращает одну строку, начиная от указателя до конца строки.
Если длина не указана, то в старых версиях PHP максимальная длина возвращаемой строки была ограничена. В более новых версиях этого ограничения нет.
Запуск функции в цикле позволяет произвести чтение файла построчно.
Прочитаем одну строку строку из файла myfile.txt. Так как указатель уже перемещён, то будет прочитана не вся строка, а от указателя.
Функция fgetss() также возвращает одну строку, но удаляет из неё HTML теги. Если в коде присутствует PHP скрипт, то он тоже будет удалён.
fgetss (файл, длина, нужные тэги)
Функция file() читает весь файл и помещает данные в массив. Каждая строка помещается в элемент массива. Для работы этой функции не требуется открывать файл. Расположение указателя не учитывается.
file (путь к файлу, флаги, контекст)
Можно указать такие флаги:
Если указывается несколько флагов, то они разделяются оператором «|».
Выведем файл myfile.txt на страницу.
Запись в файл
Функция fwrite() записывает данные в файл.
fwrite (файл, данные, длина)
Если указатель находится в начале или середине файла, функция заменяет символы, находящиеся в файле, на новые. То есть, если записывается 5 символов, то из файла удаляются 5 символов и на их место добавляются новые.
Когда вы записываете русские буквы, то кодировка скрипта должна соответствовать кодировке файла.
Запишем в файл строку «текст из скрипта». В нашем примере дескрептор находится в середине файла и строка запишется туда. Когда нужно добавить данные в конец файла, то его можно открыть в режиме ‘a’. А когда нужно перезаписать файл, его открывают в режиме ‘w’. Мы не будем передвигать указатель, запишем данные туда, где он находится.
Коприрование материалов сайта возможно только с согласия администрации