Есть ли у кого-нибудь скрипт пошагового построчного чтения файла, который например, сможет прочитать с 20й строчки - N колличество строк?
Можно либо прочитать файл целиком и разбить по строкам на массив, либо читать от начала, определяя количество строк, а от нужной считать символы.
Мне кажется что это ресурсоемкие операции в случае если текстовый файл большого размера. Поэтому хочется найти решение которое будет читать именно то что нужно (как писал выше). Поправьте если ошибаюсь по поводу ресурсов.
Написал простенький класс, реализующий требуемый функционал: Код (PHP): <?php final class FileReader { protected $handler = null; protected $fbuffer = ""; /** * Конструктор класса, открывающий файл для работы * * @param string $filename */ public function __construct($filename) { if(!($this->handler = fopen($filename, "rb"))) throw new Exception("Cannot open the file"); } /** * Построчное чтение всего файла с учетом сдвига * * @return string */ public function ReadAll() { if(!$this->handler) throw new Exception("Invalid file pointer"); while(!feof($this->handler)) $this->fbuffer .= fgets($this->handler); return $this->fbuffer; } /** * Установить строку, с которой производить чтение файла * * @param int $line */ public function SetOffset($line) { if(!$this->handler) throw new Exception("Invalid file pointer"); while(!feof($this->handler) && $line--) { fgets($this->handler); } } }; /** * Пример использования */ $stream = new FileReader("lines.txt"); // Укажем, что читать надо с 20-ой строки $stream->SetOffset(20); // Получаем содержимое $stream->ReadAll(); /** * Количество прочитанных строк можно узнать так: * * echo count(explode("\n", $stream->ReadAll())); */ ?>
Класс! Попробую сегодня. Все что содержится между началом строки и ее концом (символом перевода строки). Файл CSV, XML тут не подойдет.
Спасибо Apple, доработал - получилось то что нужно, гиговый файл в 2млн строк возвращает 5 строк начиная с 1млн - моментально! PHP: final class FileReader { protected $handler = null; protected $fbuffer = array(); /** * Конструктор класса, открывающий файл для работы * * @param string $filename */ public function __construct($filename) { if(!($this->handler = fopen($filename, "rb"))) throw new Exception("Cannot open the file"); } /** * Построчное чтение $count_line строк файла с учетом сдвига * * @param int $count_line * * @return string */ public function Read($count_line = 10) { if(!$this->handler) throw new Exception("Invalid file pointer"); while(!feof($this->handler)) { $this->fbuffer[] = fgets($this->handler); $count_line--; if($count_line == 0) break; } return $this->fbuffer; } /** * Установить строку, с которой производить чтение файла * * @param int $line */ public function SetOffset($line = 0) { if(!$this->handler) throw new Exception("Invalid file pointer"); while(!feof($this->handler) && $line--) { fgets($this->handler); } } }; // Пример использования $file = "file.txt"; $line = 1000000; $count_line = 5; $stream = new FileReader($file); // Укажем, что читать надо с $line строки $stream->SetOffset($line); // Получаем содержимое $count_line строк $result = $stream->Read($count_line); print_r("<pre>"); print_r($result); print_r("</pre>");
Ну так -) Для нас символ перевода строки - это новая строка, для машины обычный символ. Машина не различает строки, мы не можем ей сказать "выбери с 4 по 6 строку". Нужно читать файл, считая строки, до тех пор, пока не будет достигнута последняя необходимая строка. Что и делает код, приведенный Apple. Работает он быстро от того, что прочтенные данные до момента "выборки" никуда не сохранятся и файл читается по частям, а не целиком.
Но скрипт обходит весь файл, хоть и быстро но обходит, ещё есть fseek, который перемещает указатель чтения, может с помощью него можно сделать скрипт побыстре, но пока у меня не получается. Что скажете насчет fseek?
После "выборки" нужных строк - нет. Чтобы его использовать, нужно знать, куда смещать. Можно выбирать по 1000 символов и считать переводы строк, только это будет медленнее, чем во встроенной функции fgets.
Он сдвигает указатель на указанное количество байт. Вам известно, сколько байт каждая строка и равно ли их количество? Я полагаю, что нет.
И всё было бы замечательно, если бы не мой случай)) Дан файлик, в котором есть перевод строки, а есть символ экранирования (\) и сразу следующий за ним символ перевода строки. Как можно доработать класс, дабы он отличал перевод строки от экранированного перевода строки, который надо воспринимать как есть (как просто очередной символ и идти далее), а не как конец строки? хм....
Driver86 проверяй последний прочитанный символ строки. Если \ значит не считай следующую за строчку и добавляй ее к текущей.
посмотрите команду tail в *nix, елси вам последние строки надо читать. Еще есть готовая утилита ftptail (где-то в сети можно найти), написанная на перле - позволяет дергать последние н строк из файла, находящегося на удаленном сервере. По фтп)))
Apple, спасибо, отличный класс а подскажи как сделать, чтобы можно было на получить определенные строки? допустим есть массив array('23242', '444', '2342', '777') и мне нужны именно эти строки из файла подскажите плиз
Код (PHP): function getLinesById($filename, Array $ids) { $out=array(); $cnt=1; if ( ($fh = fopen($filename,'r')) !== false) { $max = max($ids); while ( ($line = fgets($fh)) !== false ) { if (in_array($cnt,$ids)) { $out[$cnt]=$line; } $cnt++; if ($cnt>$max) break; //выходим, т.к. дальше нам строки ненужны }//while fclose($fh); } return $out; } echo '<pre>'; print_r( getLinesById('test.php', array(7,16,1001) ) );
спасибо большое а как выдать в массиве в том же формате как и на входе было? сейчас выдаются по возрастанию
Код (Text): array(53) { [26400]=> string(0) "" [23044]=> string(0) "" [28199]=> string(0) "" [47969]=> string(0) "" [48254]=> string(0) "" [36139]=> string(0) "" [42957]=> string(0) "" [15682]=> string(0) "" иногда не вытягивает данные
пример файла. и подробности. невытягивает какието определенные номера, или ошибка повторяется только изредка?