Как прочитать файл в php массив
При необходимости можно разбить содержимое файла построчно, тогда получится массив строк.
$content = file_get_contents('./example.txt'); $lines = explode("\n", $content); print_r($lines); # => Array # => ( # . Здесь вывод данных. Каждая строчка - элемент массива # => )
Вариант с функцией file(). Файл будет прочитан построчно, строки файла будут сохранены в массив
$lines = file('./example.txt', FILE_IGNORE_NEW_LINES); print_r($lines); # => Array # => ( # . Здесь вывод данных. Каждая строчка - элемент массива # => )
Учимся работать с файлами в PHP
В данном уроке мы рассмотрим несколько способов работы с файлами в PHP, а также поговорим о преимуществах и недостатках каждого из них.
Построчное чтение из файла
Для начала давайте научимся читать файлы. Давайте создадим текстовый файл file.txt и запишем в него следующий текст:
В век высоких технологий Без программ не обойтись, Программисты ежедневно Легче делают нам жизнь!
Давайте теперь считаем его с помощью программы на языке php. Создайте файл files.php и запишите в него следующий код:
'; > fclose($file);
Если вы сейчас запустите этот скрипт, то увидите на экране четверостишие из файла.
- Тест на знание основ HTML
- Тест на знание основ PHP
- Тест на знание ООП в PHP
Давайте теперь разберём по шагам нашу программу.
- Функция fopen открывает файл. Первым аргументом указывается путь до файла, вторым аргументом указывается режим для работы с файлом. В нашем случае это ‘r’ – чтение (от read). Ознакомьтесь с другими режимами работы в документации по функции. Данная функция возвращает ресурс — это еще один тип данных, с которым мы ранее не работали. Ресурс в PHP – это ссылка на какой-то внешний ресурс (файл, база данных и т. п.). Работа с ресурсами происходит с помощью специальных функций. Более детально о ресурсах вы можете прочитать тут.
- В цикле for четыре раза вызывается функция fgets. Эта функция считывает строку из файла и перемещает текущий курсор на следующую строку (курсор можете представить себе как обычный курсор в текстовом редакторе, который просто перемещается в момент считывания данных из файла). Если снова вызвать эту функцию, она сделает то же самое для следующей строки. Возвращает эта функция считанную строку (string).
- Ресурс закрывается с помощью функции fclose. Делать это следует всегда, несмотря на то, что PHP по завершении работы программы сделает это сам. Дело в том, что если открыть файл в блокирующем режиме (например, для записи, в режиме ‘w’), то он будет недоступен для других процессов, они будут ждать, пока предыдущий ресурс освободит этот файл.
Как вы понимаете, для чтения не очень удобно использовать цикл for с указанием конкретного числа строк, которые необходимо считать. Для этого можно использовать функцию feof – она возвращает true, если достигнут конец файла, и false – если нет.
На вход в качестве аргумента она принимает ресурс файла. Программа, переписанная с использованием функции feof будет выглядеть следующим образом:
'; > fclose($file);
Цикл будет выполняться до тех пор, пока не достигнут конец файла. При этом функция fgets каждый раз сдвигает курсор на следующую строку. Как только курсор достигнет конца файла, цикл завершится. Таким образом мы не привязаны к числу строк и можем читать файл не задумываясь об этом.
Построчная запись в файл
Теперь запишем данные в файл. Для того чтобы открыть файл для записи используется та же функция fopen, только в качестве режима работы указывается ‘w’ (от write).
А для записи в файл строки используется функция fputs(). Первым аргументом указывается ресурс, а вторым — строка, которую необходимо записать в файл. Давайте в качестве примера напишем программу, которая запишет в файл file2.txt числа от 1 до 100.
fclose($file);
Константа PHP_EOL содержит в себе символ переноса строки. При этом для разных операционных систем (Windows или Unix-подобных) эти символы будут разными.
Дозаписываем в конец файла
При этом если сейчас запустить программу снова, то старые данные в файле file2.txt перезапишутся новыми. Для того, чтобы сохранить содержимое файла и дозаписать данные в конец, нужно использовать режим работы с файлом “a” (от append – присоединять, добавлять).
Если мы запустим этот скипт дважды, то в файле file3.txt будет две строки “abc”.
Читаем файл целиком
В PHP также имеется возможность прочитать весь файл за раз. Для этого используется функция file_get_contents().
Данный код выведет всё то же четверостишие, только без переноса строк.

Так произошло потому, что в самом первом примере мы добавляли тег
после каждой прочитанной строки. Если же мы откроем исходный код страницы, то увидим, что на самом деле переносы строк в исходном коде сохранены.

Запись в файл данных целиком
Для того, чтобы записать в файл большой объем данных за раз — сразу несколько строк, можно воспользоваться функцией file_put_contents.
Если снова запустить этот код, файл перезапишется. Для того, чтобы дополнить файл у этой функции есть третий аргумент — режим работы с файлом. Для дозаписи в конец файла следует использовать константу FILE_APPEND.
Какой способ выбрать
Данные способы отличаются в первую очередь тем, что когда мы считываем файл целиком, или записываем в него много данных за раз, нам приходится хранить в оперативной памяти больше данных, чем если бы мы работали по отдельности с каждой строкой. Если данных не слишком много, об этом можно не беспокоиться. Однако, если вы читаете файл размером 10 Гб, а на компьютере, на котором выполняется скрипт, всего 4Гб оперативной памяти, то его получится считать только построчно. При попытке загрузить его целиком программа упадёт с ошибкой о нехватке памяти.
Как прочитать большой файл средствами PHP (не грохнув при этом сервак)
PHP разработчикам не так уж часто приходится следить за расходом памяти в своих приложениях. Сам движок PHP неплохо подчищает мусор за нами, да и модель веб-сервера с контекстом исполнения, «умирающим» после выполнения каждого запроса, позволяет даже самому плохому коду не создавать больших долгих проблем.
Однако, в некоторых ситуациях, мы можем столкнуться с проблемами нехватки оперативной памяти — например, пытаясь запустить композер на маленьком VPS, или при открытии большого файла на сервере не богатом ресурсами.

Последняя проблема и будет рассмотрена в этом уроке.
Мерила Успеха
При проведении любых оптимизаций кода, мы всегда должны замерять результаты его выполнения до и после, для того чтобы оценивать эффективность(или пагубность) наших оптимизаций.
Обычно измеряют загрузку CPU и использование оперативной памяти. Часто бывает, что экономия одного, ведёт к увеличенным затратам другого и наоборот.
В асинхронной модели приложения(мультипроцессорные и многопоточные) всегда очень важно следить как за процессором, так и за памятью. В классических приложениях контроль ресурсов становится проблемой лишь при приближении к лимитам сервера.
Измерять использование CPU внутри PHP плохая идея. Лучше использовать какую-либо утилиту, как top из Ubuntu или macOS. Если вы у вас Windows, то можно использовать Linux Subsystem, чтобы иметь доступ к top.
В этом уроке мы будем измерять использование памяти. Мы посмотрим, как память расходуется в традиционных скриптах, а затем применим парочку фишек для оптимизации и сравним результаты. Надеюсь, к концу статьи, читатель получит базовое понимание основных принципов оптимизации расхода памяти при чтении больших объемов данных.
Будем замерять память так:
// formatBytes is taken from the php.net documentation memory_get_peak_usage(); function formatBytes($bytes, $precision = 2) < $units = array("b", "kb", "mb", "gb", "tb"); $bytes = max($bytes, 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); $bytes /= (1
Эту функцию мы будем использовать в конце каждого скрипта, и сравнивать полученные значения.
Какие есть варианты?
Существует много разных подходов для эффективного чтения данных, но всех их условно можно разделить на две группы: мы либо считываем и сразу же обрабатываем считанную порцию данных(без предварительной загрузки всех данных в память), либо вовсе преобразуем данные в поток, не заморачиваясь над его содержимым.
Давайте представим, что для первого варианта мы хотим читать файл и отдельно обрабатывать каждые 10000 строк. Нужно будет держать по крайней мере 10000 строк в памяти и передавать их в очередь(в какой бы форме она не была реализована).
Для второго сценария, предположим, мы хотим сжать содержимое очень большого ответа API. Нам не важно, что за данные там содержатся, важно вернуть их в сжатой форме.
В обоих случаях нужно считать большие объемы информации. В первом, нам известен формат данных, во втором, формат значения не имеет. Рассмотрим оба варианта.
Чтение Файла Строка За Строкой
Есть много функций для работы с файлами. Давайте напишем с их помощью свой ридер:
// from memory.php function formatBytes($bytes, $precision = 2) < $units = array("b", "kb", "mb", "gb", "tb"); $bytes = max($bytes, 0); $pow = floor(($bytes ? log($bytes) : 0) / log(1024)); $pow = min($pow, count($units) - 1); $bytes /= (1 print formatBytes(memory_get_peak_usage());
// from reading-files-line-by-line-1.php function readTheFile($path) < $lines = []; $handle = fopen($path, "r"); while(!feof($handle)) < $lines[] = trim(fgets($handle)); >fclose($handle); return $lines; > readTheFile("shakespeare.txt"); require "memory.php";
Тут мы считываем файл с работами Шекспира. Размер файла около 5.5MB и пиковое использование памяти 12.8MB.
А теперь, давайте воспользуемся генератором:
// from reading-files-line-by-line-2.php function readTheFile($path) < $handle = fopen($path, "r"); while(!feof($handle)) < yield trim(fgets($handle)); >fclose($handle); > readTheFile("shakespeare.txt"); require "memory.php";
Файл тот же, а пиковое использование памяти упало до 393KB! Но пока мы не выполняем со считываемыми данными никаких операций, это не имеет практической пользы. Для примера, мы можем разбивать документ на части, если встретим две пустые строки:
// from reading-files-line-by-line-3.php $iterator = readTheFile("shakespeare.txt"); $buffer = ""; foreach ($iterator as $iteration) < preg_match("/\n/", $buffer, $matches); if (count($matches)) < print "."; $buffer = ""; >else < $buffer .= $iteration . PHP_EOL; >> require "memory.php";
Хотя мы разбили документ на 1,216 кусков, мы использовали лишь 459KB памяти. Всё это, благодаря особенности генераторов — объем памяти для их работы равен размеру самой большой итерируемой части. В данном случае, самая большая часть состоит из 101,985 символов.
Генераторы могут применяться и в других ситуациях, но данный пример хорошо демонстрирует производительность при чтении больших файлов. Возможно, генераторы один из лучших вариантов для обработки данных.
Пайпинг между файлами
В ситуациях, когда обработка данных не требуется, мы можем пробрасывать данные из одного файла в другой. Это называется пайпингом( pipe — труба, возможно потому что мы не видим что происходит внутри трубы, но видим что входит и выходит и неё). Это можно сделать с помощью потоковых методов. Но сперва, давайте напишем классический скрипт, который тупо передает данные из одного файла в другой:
// from piping-files-1.php file_put_contents( "piping-files-1.txt", file_get_contents("shakespeare.txt") ); require "memory.php";
Неудивительно, что этот скрипт использует намного больше памяти, чем занимает копируемый файл. Это связано с тем, что он должен читать и хранить содержимое файла в памяти до тех пор пока файл не будет скопирован полностью. Для маленьких файлов в этом нет ничего страшного, но не для больших.
Давайте попробуем стримить(или пайпить) файлы, один в другой:
// from piping-files-2.php $handle1 = fopen("shakespeare.txt", "r"); $handle2 = fopen("piping-files-2.txt", "w"); stream_copy_to_stream($handle1, $handle2); fclose($handle1); fclose($handle2); require "memory.php";
Код довольно странный. Мы открываем оба файла, первый на чтение, второй на запись. Затем мы копируем первый во второй, после чего закрываем оба файла. Возможно будет сюрпризом, но мы потратили всего 393KB.
Что-то знакомое. Не похоже ли это на генератор, читающий каждую строчку? Это так, потому что второй аргумент fgets определяет как много байт каждой строки нужно считывать(по умолчанию -1, т.е до конца строки). Необязательный, третий аругмент stream_copy_to_stream делает то же самое. stream_copy_to_stream читает первый поток по одной строке и пишет во второй.
Пайпинг этого текста не особо полезен для нас. Давайте придумаем реальный пример. Предположим, что мы хотим получить картинку из нашего CDN и передать её в файл или в stdout . Мы могли бы сделать это так:
// from piping-files-3.php file_put_contents( "piping-files-3.jpeg", file_get_contents( "https://github.com/assertchris/uploads/raw/master/rick.jpg" ) ); // . or write this straight to stdout, if we don't need the memory info require "memory.php";
Для того чтобы осуществить задуманное этим способом потребовалось 581KB. Теперь попробуем сделать то же самое с помощью потоков.
// from piping-files-4.php $handle1 = fopen( "https://github.com/assertchris/uploads/raw/master/rick.jpg", "r" ); $handle2 = fopen( "piping-files-4.jpeg", "w" ); // . or write this straight to stdout, if we don't need the memory info stream_copy_to_stream($handle1, $handle2); fclose($handle1); fclose($handle2); require "memory.php";
Потратили немного меньше памяти(400KB) при одинаковом результате. А если б нам не нужно было сохранять картинку в памяти, мы могли бы сразу застримить её в stdout :
$handle1 = fopen( "https://github.com/assertchris/uploads/raw/master/rick.jpg", "r" ); $handle2 = fopen( "php://stdout", "w" ); stream_copy_to_stream($handle1, $handle2); fclose($handle1); fclose($handle2); // require "memory.php";
Другие потоки
Существуют и другие потоки, в/из которых можно стримить:
- php://stdin — только чтение
- php://stderr — только запись
- php://input — только чтение(дает доступ к голому телу запроса)
- php://output — только запись(позволяет писать в буфер вывода)
- php://memory and php://temp — чтение и запись. Тут можно хранить временные данные, отличие в том что php://temp будет хранить данные в файловой системе при их разрастании, а php://memory будет писать всё в оперативную память до последнего.
Фильтры
Есть еще одна фишка, которую мы можем использовать — это фильтры. Промежуточный вариант, который дает нам немного контроля над потоком, без необходимости детально погружаться в его содержимое. Допустим, мы хотим сжать файл. Можно применить zip extension:
// from filters-1.php $zip = new ZipArchive(); $filename = "filters-1.zip"; $zip->open($filename, ZipArchive::CREATE); $zip->addFromString("shakespeare.txt", file_get_contents("shakespeare.txt")); $zip->close(); require "memory.php";
Хороший код, но он потребляет почти 11MB. С фильтрами, получится лучше:
// from filters-2.php $handle1 = fopen( "php://filter/zlib.deflate/resource=shakespeare.txt", "r" ); $handle2 = fopen( "filters-2.deflated", "w" ); stream_copy_to_stream($handle1, $handle2); fclose($handle1); fclose($handle2); require "memory.php";
Здесь мы используем php://filter/zlib.deflate который считывает и сжимает входящие данные. Мы можем пайпить сжатые данные в файл, или куда-нибудь еще. Этот код использовал лишь 896KB.
Я знаю что это не совсем тот же формат, что и zip архив. Но задумайтесь, если у нас есть возможность выбрать иной формат сжатия, затратив в 12 раз меньше памяти, стоит ли это делать?
Чтобы распаковать данные, применим другой zip фильтр.
// from filters-2.php file_get_contents( "php://filter/zlib.inflate/resource=filters-2.deflated" );
Вот парочка статей, для тех кому хотелось бы поглубже погрузиться в тему потоков: “Understanding Streams in PHP” и“Using PHP Streams Effectively”.
Кастомизация потоков
fopen и file_get_contents имеют ряд предустановленных опций, но мы можем менять их как душе угодно. Чтобы сделать это, нужно создать новый контекст потока:
// from creating-contexts-1.php $data = join("&", [ "twitter=assertchris", ]); $headers = join("\r\n", [ "Content-type: application/x-www-form-urlencoded", "Content-length: " . strlen($data), ]); $options = [ "http" => [ "method" => "POST", "header"=> $headers, "content" => $data, ], ]; $context = stream_content_create($options); $handle = fopen("http://example.com/register", "r", false, $context); $response = stream_get_contents($handle); fclose($handle);
В этом примере мы пытаемся сделать POST запрос к API. Прописываем несколько заголовков, и обращаемся к API по файловому дескриптору. Существует много других опций для кастомизации, так что не будет лишним ознакомиться с документацией по этому вопросу.
Создание своих протоколов и фильтров
Перед тем как закончить, давайте поговорим о создании кастомных протоколов. Если посмотреть в документацию, то можно увидеть пример:
Protocol
Написание своей реализации такого тянет на отдельную статью. Но если все же озадачиться и сделать это, то можно будет легко зарегистрировать свою обертку для стримов:
if (in_array("highlight-names", stream_get_wrappers())) < stream_wrapper_unregister("highlight-names"); >stream_wrapper_register("highlight-names", "HighlightNamesProtocol"); $highlighted = file_get_contents("highlight-names://story.txt");
Аналогичным образом, можно создать и кастомные фильтры потока. Пример класса фильтра из доков:
Filter
И его также легко зарегистрировать:
$handle = fopen("story.txt", "w+"); stream_filter_append($handle, "highlight-names", STREAM_FILTER_READ);
Свойство filtername в новом классе фильтра должно быть равно highlight-names . Также можно использовать инлайновый фильтр php://filter/highligh-names/resource=story.txt . Создавать фильтры гораздо легче чем протоколы. Но протоколы, имеют более гибконастраеваемые возможности и функциональность. К примеру, дной из причин для которой фильтры не годятся, а требуются протоколы — это операции с директориями, где фильтр будет нужен для обработки каждой порции данных.
Настоятельно рекомендую поэкспериментировать с созданием собственных протоколов и фильтров. Если получится применить фильтр к функции stream_copy_to_stream , то вы получите колоссальную экономию памяти при работе с большими объемами данных. Представьте что у вас будет фильтр для ресайзинга изображений или фильтр для шифрования, а может и еще что покруче.
Итог
Хотя это не самая частая проблема, с которой мы мучаемся, очень легко накосячить при работе с большими файлами. В асинхронных приложениях, вообще очень просто положить весь сервер, если не контролировать использование памяти в своих скриптах
Надеюсь, что этот урок подарил вам несколько новых идей(или освежил их в памяти) и теперь вы сможете работать с большими файлами гораздо эффективнее. Познакомившись с генераторами и потоками( и перестав использовать функции по типу file_get_contents ) можно избавить наши приложения от целого класса ошибок. That seems like a good thing to aim for!
- Высокая производительность
- PHP
- Серверная оптимизация
fgets
Указатель на файл должен быть корректным и указывать на файл, успешно открытый функциями fopen() или fsockopen() (и всё ещё не закрытый функцией fclose() ).
Чтение заканчивается при достижении length - 1 байт, либо если встретилась новая строка (которая включается в возвращаемый результат) или конец файла (в зависимости от того, что наступит раньше). Если длина не указана, чтение из потока будет продолжаться до тех пор, пока не достигнет конца строки.
Возвращаемые значения
Возвращает строку размером в length - 1 байт, прочитанную из дескриптора файла, на который указывает параметр stream . Если данных для чтения больше нет, то возвращает false .
В случае возникновения ошибки возвращает false .
Примеры
Пример #1 Построчное чтение файла
$fp = @ fopen ( "/tmp/inputfile.txt" , "r" );
if ( $fp ) while (( $buffer = fgets ( $fp , 4096 )) !== false ) echo $buffer ;
>
if (! feof ( $fp )) echo "Ошибка: fgets() неожиданно потерпел неудачу\n" ;
>
fclose ( $fp );
>
?>?php
Примечания
Замечание: Если у вас возникают проблемы с распознаванием PHP концов строк при чтении или создании файлов на Macintosh-совместимом компьютере, включение опции auto_detect_line_endings может помочь решить проблему.
Замечание:
Программисты, привыкшие к семантике 'C' функции fgets() , должны принимать во внимание разницу в том, каким образом возвращается признак достижения конца файла ( EOF ).
Смотрите также
- fgetss() - Читает строку из файла и удаляет HTML-теги
- fread() - Бинарно-безопасное чтение файла
- fgetc() - Считывает символ из файла
- stream_get_line() - Получает строку из потокового ресурса до указанного разделителя
- fopen() - Открывает файл или URL
- popen() - Открывает файловый указатель процесса
- fsockopen() - Открывает соединение с интернет-сокетом или доменным сокетом Unix
- stream_set_timeout() - Установить значение времени ожидания для потока
User Contributed Notes 38 notes
8 years ago
A better example, to illustrate the differences in speed for large files, between fgets and stream_get_line.
This example simulates situations where you are reading potentially very long lines, of an uncertain length (but with a maximum buffer size), from an input source.
As Dade pointed out, the previous example I provided was much to easy to pick apart, and did not adequately highlight the issue I was trying to address.
Note that specifying a definitive end-character for fgets (ie: newline), generally decreases the speed difference reasonably significantly.
#!/usr/bin/php
$plaintext = file_get_contents ( 'http://loripsum.net/api/60/verylong/plaintext' ); # Should be around 90k characters
$plaintext = str_replace ( "\n" , " " , $plaintext ); # Get rid of newlines
$fp = fopen ( "/tmp/SourceFile.txt" , "w" );
for( $i = 0 ; $i < 100000 ; $i ++) fputs ( $fp , substr ( $plaintext , 0 , rand ( 4096 , 65534 )) . "\n" );
>
fclose ( $fp );
$fp = fopen ( "/tmp/SourceFile.txt" , "r" );
$start = microtime ( true );
while( $line = fgets ( $fp , 65535 )) 1 ;
>
$end = microtime ( true );
fclose ( $fp );
$delta1 =( $end - $start );
$fp = fopen ( "/tmp/SourceFile.txt" , "r" );
$start = microtime ( true );
while( $line = stream_get_line ( $fp , 65535 )) 1 ;
>
$end = microtime ( true );
fclose ( $fp );
$delta2 =( $end - $start );
$pdiff = $delta1 / $delta2 ;
print "stream_get_line is " . ( $pdiff > 1 ? "faster" : "slower" ) . " than fgets - pdiff is $pdiff \n" ;
?>
$ ./testcase.php
stream_get_line is faster than fgets - pdiff is 1.760398041785
Note that, in a vast majority of situations in which php is employed, tiny differences in speed between system calls are of negligible importance.
16 years ago
There's an error in the documentation:
The file pointer must be valid, and must point to a file successfully opened by fopen() or fsockopen() (and not yet closed by fclose()).
You should also add "popen" and "pclose" to the documentation. I'm a new PHP developer and went to verify that I could use "fgets" on commands that I used with "popen".
3 years ago
if you for some reason need to get lines from a string instead of a file pointer, try
function string_gets ( string $source , int $offset = 0 , string $delimiter = "\n" ): ? string
$len = strlen ( $source );
if ( $len < $offset ) // out of bounds.. maybe i should throw an exception
return null ;
>
if ( $len === $offset ) // end of string..
return null ;
>
$delimiter_pos = strpos ( $source , $delimiter , $offset );
if ( $delimiter_pos === false ) // last line.
return substr ( $source , $offset );
>
return substr ( $source , $offset , ( $delimiter_pos - $offset ) + strlen ( $delimiter ));
>
?>
(i had a ~16GB string in-memory i needed to process line-by-line, but i would get memory-allocation-crash (on a 32GB ram system) if i tried explode("\n",$str); , so came up with this.. interestingly, fgets() seems to be faster than doing it in-ram-in-php, though. php 7.3.7)
17 years ago
I think that the quickest way of read a (long) file with the rows in reverse order is
$myfile = 'myfile.txt' ;
$command = "tac $myfile > /tmp/myfilereversed.txt" ;
passthru ( $command );
$ic = 0 ;
$ic_max = 100 ; // stops after this number of rows
$handle = fopen ( "/tmp/myfilereversed.txt" , "r" );
while (! feof ( $handle ) && ++ $ic $buffer = fgets ( $handle , 4096 );
echo $buffer . "
" ;
>
fclose ( $handle );
?>
It echos the rows while it is reading the file so it is good for long files like logs.
14 years ago
One thing I discovered with fgets, at least with PHP 5.1.6, is that you may have to use an IF statement to avoid your code running rampant (and possibly hanging the server). This can cause problems if you do not have root access on the server on which you are working.
This is the code I have implemented ($F1 is an array):
if( $fh = fopen ( "filename" , "r" )) <
while (! feof ( $fh )) <
$F1 [] = fgets ( $fh , 9999 );
>
fclose ( $fh );
>
?>
I have noticed that without the IF statement, fgets seems to ignore when $fh is undefined (i.e., "filename" does not exist). If that happens, it will keep attempting to read from a nonexistent filehandle until the process can be administratively killed or the server hangs, whichever comes first.
16 years ago
I'm using this function to modify the header of a large postscript document on copy. Works extremely quickly so far.
function write($filename) $fh = fopen($this->sourceps,'r');
$fw = fopen($filename,'w');
while (!feof($fh)) $buffer = fgets($fh);
fwrite($fw,$buffer);
if (!$setupfound && ereg("^%%BeginSetup",$buffer)) $setupfound++;
if (array_key_exists("$filename",$this->output)) foreach ($this->output[$filename] as $function => $value) fwrite($fw,$value);
>
>
stream_copy_to_stream($fh,$fw);
>
>
fclose($fw);
fclose($fh);
>
5 years ago
Error in the example number 1 of this page.
change this line:
$buffer = fgets($fd, 4096);
into:
$buffer = fgets($handle, 4096);
16 years ago
fscanf($file, "%s\n") isn't really a good substitution for fgets(), since it will stop parsing at the first whitespace and not at the end of line!
(See the fscanf page for details on this)
17 years ago
For sockets, If you dont want fgets, fgetc etc. to block if theres no data there. set socket_set_blocking(handle,false); and socket_set_blocking(handle,true); to set it back again.
19 years ago
Sometimes the strings you want to read from a file are not separated by an end of line character. the C style getline() function solves this. Here is my version:
function getline ( $fp , $delim )
$result = "" ;
while( ! feof ( $fp ) )
$tmp = fgetc ( $fp );
if( $tmp == $delim )
return $result ;
$result .= $tmp ;
>
return $result ;
>
// Example:
$fp = fopen ( "/path/to/file.ext" , 'r' );
while( ! feof ( $fp ) )
$str = getline ( $fp , '|' );
// Do something with $str
>
fclose ( $fp );
?>
17 years ago
It appears that fgets() will return FALSE on EOF (before feof has a chance to read it), so this code will throw an exception:
while (!feof($fh)) $line = fgets($fh);
if ($line === false) throw new Exception("File read error");
>
>
15 years ago
The file pointer that fgets() uses can also be created with the proc_open() function and used with the stdout pipe created from the executed process.
17 years ago
I would have expected the same behaviour from these bits of code:-
/*This times out correctly*/
while (! feof ( $fp )) echo fgets ( $fp );
>
/*This times out before eof*/
while ( $line = fgets ( $fp )) echo $line ;
>
/*A reasonable fix is to set a long timeout*/
stream_set_timeout ( $fp , 180 );
while ( $line = fgets ( $fp )) echo $line ;
>
?>
7 years ago
I had loads of trouble while reading with fgets on a WAMP (Windows server). On local the file went unto a
tag without a hitch, but when I moved the code to a LAMP production server, every \r\n created two fgets and I got free empty lines.
I tried deleting with $string=str_replace("\r\n","\n",$string); but it had no effect whatsoever. The solution was to do an fread() and explode the contents by PHP_EOL and do a foreach($lines as $line) so every line did not get duplicated.
Here is the example code:
$file=fopen("test.txt,"r");
$text=fread($file,filesize("test.txt"));
$lines=explode(PHP_EOL,$text);
foreach($lines as $line)
// Do something
>
5 years ago
if((!isset( $_GET [ 'filepath' ]) || ! file_exists ( $_GET [ 'filepath' ])) || !isset( $_GET [ 'sel' ]))
exit( "" );
echo "List
" ;
$fullPath = "D:\\OSPanel\\domains\\" . $_GET [ "filepath" ];
$f = fopen ( $fullPath , "r" );
$arr ;
for( $i = 0 ; $str = fgets ( $f ); $i ++) $tempArr0 = explode ( '-' , $str );
$arr [ $i ][ 0 ] = trim ( $tempArr0 [ 0 ]);
$arr [ $i ][ 1 ] = trim ( $tempArr0 [ 1 ]);
>
if( $_GET [ "sel" ] == "var1" ) sort ( $arr );
echo "
" ;
for( $i = 0 ; $i < count ( $arr ); $i ++)
echo " ; " ;
echo "
" ;
>
else if( $_GET [ "sel" ] == "var2" ) for( $i = 0 ; $i < count ( $arr ); $i ++)
echo "
" ;
>
else if( $_GET [ "sel" ] == "var3" ) for( $i = 0 ; $i < count ( $arr ); $i ++)
echo "
: " ;
>
13 years ago
Some people try to call feof before fgets, and then ignoring the return value of fgets. This method leads to processing value FALSE when reaching the end of file.
Bad example:
$f = fopen ( "fgetstest.php" , "r" );
$ln = 0 ;
while (! feof ( $f )) $line = fgets ( $f );
++ $ln ;
printf ( "%2d: " , $ln );
if ( $line === FALSE ) print ( "FALSE\n" );
else print ( $line );
>
fclose ( $f );
?>
Good example:
$f = fopen ( "fgetstest.php" , "r" );
$ln = 0 ;
while ( $line = fgets ( $f )) ++ $ln ;
printf ( "%2d: " , $ln );
if ( $line === FALSE ) print ( "FALSE\n" );
else print ( $line );
>
fclose ( $f );
?>
15 years ago
For large files, consider using stream_get_line rather than fgets - it can make a significant difference.
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=stream_get_line($fp,65535,"\n")) < 1; >fclose($fp);'
real 0m1.482s
user 0m1.616s
sys 0m0.152s
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=fgets($fp,65535)) < 1; >fclose($fp);'
real 0m7.281s
user 0m7.392s
sys 0m0.136s
18 years ago
Saku's example may also be used like this:
@ $pointer = fopen ( " $DOCUMENT_ROOT /foo.txt" , "r" ); // the @ suppresses errors so you have to test the pointer for existence
if ( $pointer ) while (! feof ( $pointer )) $preTEXT = fgets ( $pointer , 999 );
// $TEXT .= $preTEXT; this is better for a string
$ATEXT [ $I ] = $preTEXT ; // maybe better as an array
$I ++;
>
fclose ( $pointer );
>
?>
10 years ago
Regarding Leigh Purdie's comment (from 4 years ago) about stream_get_line being better for large files, I decided to test this in case it was optimized since then and I found out that Leigh's comment is just completely incorrect
fgets actually has a small amount of better performance, but the test Leigh did was not set up to produce good results
The suggested test was:
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=stream_get_line($fp,65535,"\n")) < 1; >fclose($fp);'
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=fgets($fp,65535)) < 1; >fclose($fp);'
The reason this is invalid is because the buffer size of 65535 is completely unnecessary
piping the output of "yes 'this is a test line'" in to PHP makes each line 19 characters plus the delimiter
so while I don't know why stream_get_line performs better with an oversize buffer, if both buffer sizes are correct, or default, they have a negligable performance difference - although notably, stream_get_line is consistent - however if you're thinking of switching, make sure to be aware of the difference between the two functions, that stream_get_line does NOT append the delimiter, and fgets DOES append the delimiter
Here are the results on one of my servers:
Buffer size 65535
stream_get_line: 0.340s
fgets: 2.392s
Buffer size of 1024
stream_get_line: 0m0.348s
fgets: 0.404s
Buffer size of 8192 (the default for both)
stream_get_line: 0.348s
fgets: 0.552s
Buffer size of 100:
stream_get_line: 0.332s
fgets: 0.368s
17 years ago
Macintosh line endings mentioned in docs refer to Mac OS Classic. You don't need this setting for interoperability with unixish OS X.
19 years ago
If you need to read an entire file into a string, use file_get_contents(). fgets() is most useful when you need to process the lines of a file separately.
17 years ago
An easy way to authenticate Windows Domain users from scripts running on a non-Windows or non-Domain box - pass the submitted username and password to an IMAP service on a Windows machine.
$server = 'imapserver' ;
$user = 'user' ;
$pass = 'pass' ;
if ( authIMAP ( $user , $pass , $server )) echo "yay" ;
> else echo "nay" ;
>
function authIMAP ( $user , $pass , $server ) $connection = fsockopen ( $server , 143 , $errno , $errstr , 30 );
if(! $connection ) return false ;
$output = fgets ( $connection , 128 ); // banner
fputs ( $connection , "1 login $user $pass \r\n" );
$output = fgets ( $connection , 128 );
fputs ( $connection , "2 logout\r\n" );
fclose ( $connection );
if ( substr ( $output , 0 , 4 ) == '1 OK' ) return true ;
19 years ago
Note that - afaik - fgets reads a line until it reaches a line feed (\\n). Carriage returns (\\r) aren't processed as line endings.
However, nl2br insterts a
tag before carriage returns as well.
This is useful (but not nice - I must admit) when you want to store a more lines in one.
function write_lines ( $text ) $file = fopen ( 'data.txt' , 'a' );
fwrite ( $file , str_replace ( "\n" , ' ' , $text ). "\n" );
fclose ( $file );
>
function read_all () $file = fopen ( 'data.txt' , 'r' );
while (! feof ( $file )) $line = fgets ( $file );
echo 'Section
nl2br' .( $line ). '
' ;
>
fclose ( $file );
>
?>
Try it.
19 years ago
If you need to simulate an un-buffered fgets so that stdin doesnt hang there waiting for some input (i.e. it reads only if there is data available) use this :
function fgets_u ( $pStdn )
$pArr = array( $pStdn );
if ( false === ( $num_changed_streams = stream_select ( $pArr , $write = NULL , $except = NULL , 0 ))) print( "\$ 001 Socket Error : UNABLE TO WATCH STDIN.\n" );
return FALSE ;
> elseif ( $num_changed_streams > 0 ) return trim ( fgets ( $pStdn , 1024 ));
>
17 years ago
fgets is SLOW for scanning through large files. If you don't have PHP 5, use fscanf($file, "%s\n") instead.
17 years ago
When working with VERY large files, php tends to fall over sideways and die.
Here is a neat way to pull chunks out of a file very fast and won't stop in mid line, but rater at end of last known line. It pulled a 30+ million line 900meg file through in ~ 24 seconds.
NOTE:
$buf just hold current chunk of data to work with. If you try "$buf . huge.file";
//Open file (DON'T USE a+ pointer will be wrong!)
$fp = fopen($file, 'r');
//Read 16meg chunks
$read = 16777216;
//\n Marker
$part = 0;
while(!feof($fp)) $rbuf = fread($fp, $read);
for($i=$read;$i > 0 || $n == chr(10);$i--) $n=substr($rbuf, $i, 1);
if($n == chr(10))break;
//If we are at the end of the file, just grab the rest and stop loop
elseif(feof($fp)) $i = $read;
$buf = substr($rbuf, 0, $i+1);
break;
>
>
//This is the buffer we want to do stuff with, maybe thow to a function?
$buf = substr($rbuf, 0, $i+1);
//Point marker back to last \n point
$part = ftell($fp)-($read-($i+1));
fseek($fp, $part);
>
fclose($fp);
11 years ago
WARNING! fgets() and I presume any read() call to a file handle, e.g.
while(!feof(STDIN)) $line = fgets(STDIN);
. do something useful with $line.
>
. will result in a timeout after a default time of 60 seconds on my install. This behavior is non standard (not POSIX like) and seems to me to be a bug, or if not a major caveat which should be documented more clearly.
After the timeout fgets() will return FALSE (=== FALSE), however, you can check to see if the stream (file handle) has really closed by checking feof($stream), e.g.
while(!feof(STDIN)) $line = fgets(STDIN);
. do something useful with $line.
>
6 years ago
One easy sample, how to use "fgets":
This sample shows how to read the Information from an .txt file:
$z = fopen ( "protocol.txt" , "r" ); //r = read
if( $pointer != false ) //Is it possible to open the File?!
//Repeats all Information from protocol.txt
while(! feof ( $pointer )) //the loop runs till the Pointer is at the End of the File
$row = fgets ( $pointer ); // $row reads the Information from the row of the File
echo "
" . $row . "
" ;
>
fclose ( $pointer ); //File must be closed
>
else
echo "
It was not possible to open the File!
" ;
>
?>
7 years ago
Even if this is not really related to PHP and its internals take care when using fgets for reading input from CLI on Linux systems as it may behave unexpected because of the limitations of arguments length on these systems. For example doing rtrim(fgets(STDIN), "\n") on a user input larger than 4095 characters will cut the the input string to 4095 characters. This shortcoming can be solved using "stty -icanon" before the script run, followed by a "stty icanon" after the script is run.
4 years ago
It's strange no one mentions "0" in this context.
Since "0" is considered to be false, a line with a single "0" can be treated as EOF if using the while assign idiom.
while ($line = fgets(STDIN, 2)) >
This may surprisingly break if a line starts with ")"
8 years ago
For anyone who wants a proper non-blocking fgets for sockets, there is a tiny snippet that does just that (performance should be horrible compared to fgets though):
function read_line_nb ( $handle )
static $buffer = '' ;
static $lastOffset = 0 ;
$buffer .= fread ( $handle , 0x1000 );
if ( preg_match ( '#\\R#' , $buffer , $m , PREG_OFFSET_CAPTURE , $lastOffset )) $line = substr ( $buffer , 0 , $m [ 0 ][ 1 ] + strlen ( $m [ 0 ][ 0 ]));
$buffer = substr ( $buffer , $m [ 0 ][ 1 ] + strlen ( $m [ 0 ][ 0 ]));
return $line ;
>
$lastOffset = strlen ( $buffer );
return false ;
>
?>
9 years ago
This goes out to Leigh Purdie (5 years ago) and also Dade Brandon (4 months ago)
So i say Leigh posting and though omg i need to change all my fgets to stream_get_line. Then i ran the tests as shown in Leigh Purdie comment His results:
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=stream_get_line($fp,65535,"\n")) < 1; >fclose($fp);'
real 0m1.482s
user 0m1.616s
sys 0m0.152s
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=fgets($fp,65535)) < 1; >fclose($fp);'
real 0m7.281s
user 0m7.392s
sys 0m0.136s
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=stream_get_line($fp,65535,"\n")) < 1; >fclose($fp);'
real 0m0.341s
user 0m0.352s
sys 0m0.148s
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=fgets($fp,65535)) < 1; >fclose($fp);'
real 0m4.283s
user 0m4.128s
sys 0m0.448s
My results do show the same issue his results show. But first off PHP has at least gotten about 2-5 times faster then when the tests were first run (or better hardware).
Now to relate to Dade Brandon who states if you use a correct buffer size the perfomance is neck and neck.
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=stream_get_line($fp,21,"\n")) < 1; >fclose($fp);'
real 0m0.336s
user 0m0.412s
sys 0m0.076s
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=fgets($fp,21)) < 1; >fclose($fp);'
real 0m0.312s
user 0m0.364s
sys 0m0.192s
As you can see very close and fgets just coming just a little bit ahead. I suspect that fgets is reading backwards on the buffer or loads everything into its self then trys to figure it out where as a correct set buffer does the trick. Dade Brandon states that fgets lets you know how the line was delimited. stream_get_line lets you choose what you wanna call the delimiter using its 3rd option.
fgets has one more option that is important, you dont have to set the length of the line. So in a case where you may not know the length of the line maybe in handling Http protocol or something else like log lines you can simply leave it off and still get great performance.
$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=fgets($fp)) < 1; >fclose($fp);'
real 0m0.261s
user 0m0.260s
sys 0m0.232s
This is better then with a buffer set.
