string fgets ( int $handle [, int $length ] )
从 handle 指向的文件中读取一行并返回长度最多为 length - 1 字节的字符串。碰到换行符(包括在返回值中)、EOF 或者已经读取了 length - 1 字节后停止(看先碰到那一种情况)。如果没有指定 length ,则默认为 1K,或者说 1024 字节。
bool feof ( resource $handle )
果传递的文件指针无效可能会陷入无限循环中,因为 EOF 不会返回 TRUE。
先上代码,正确的写法:
指针位置文件:line_number_mark.txt,需要读取N行的大文件:onlyQQFilteredByJack.txt
<?php
$num = 0;
$insert_num = 10; //insert amount every time.
if(file_exists('line_number_mark.txt'))
{
$num = trim(file_get_contents($rootPath . 'line_number_mark.txt'));
}
echo "pointer_num=".$num."\n";
$fp = fopen('./onlyQQFilteredByJack.txt', 'r');
fseek($fp, $num);
$i = 0;
while($i < $insert_num)
{
if(!feof($fp))
{
$data = fgets($fp);
$value = trim($data);
if(!empty($value))
{
$i++;
echo $value."\n";
}
}else
{
echo "out of the file's bindery,exit the while loop..\n";
$position = ftell($fp);
$flag = 1;//set flag means it's already end
break;//Break 退出循环
}
}
if($flag)
file_put_contents($rootPath . 'line_number_mark.txt', $position);
else
file_put_contents($rootPath . 'line_number_mark.txt', ftell($fp));
?>
指针的越位,错误的写法:
<?php
$num = 0;
$insert_num = 10; //insert amount every time.
if(file_exists($rootPath . 'line_number_mark.txt'))
{
$num = trim(file_get_contents($rootPath . 'line_number_mark.txt'));
}
echo "num=".$num."\n";
$fp = fopen($rootPath . 'onlyQQFilteredByJack.txt', 'r');
fseek($fp, $num);
echo ftell($fp)."\n";
if(feof($fp))
{
echo "Out of the file's bond\n";
exit();
}
$i = 0;
while($i < $insert_num)
{
$data = fgets($fp);//没fget一次,指针自动加一,后面ftell的值也是加一后的值,而在while下造成了越界,下次在来读取后在上面的if(feof($fp))出现传递的文件指针无效可能会陷入无限循环中,因为 EOF 不会返回 TRUE。
$value = trim($data);
if(!empty($value))
{
$i++;
echo $value."\n";
}
}
echo ftell($fp)."\n";
file_put_contents($rootPath . 'line_number_mark.txt', ftell($fp));
?>
回忆未来(372647693) 14:52:20
PHP读取大文件,一次读取,php的内存往往不够用,大家如何解决类似的问题?
膘叔(19129540) 14:52:51
那就别一次读。
莫莫(3296320) 14:53:24
用fopen
莫莫(3296320) 14:53:30
然后一点一点移指针
PHP内置函数filesize()返回文件的长度是int类型的,按照PHP文档上的sprintf('%u', filesize($str_filename)),最大也只能正确表示不大于2748436857字节(约2.6GB)。解决办法有不少,整理了最通用的办法如下:
在本例中, $int_filesize实际上是以浮点型存储的,但应理解为整数
Linux系统
$int_filesize = 0 + trim(`stat -c%s $str_file`);
Windows系统
$int_size = 0 + exec('FOR %A IN ("'. $str_file.'") DO @ECHO %~zA');
完整函数
function big_filesize($str_filename)
{
$int_filesize = filesize($str_filename);
if($int_filesize < 0){
if(strtolower(substr($_SERVER['OS'], 0, 3)) == 'win'){
$int_size = 0 + exec('FOR %A IN ("'. $str_file.'") DO @ECHO %~zA');
}else{
$int_filesize = 0 + trim(`stat -c%s $str_file`);
}
}
return $int_filesize;
}
最近在工作中遇到了需要读取系统日志的问题,日志文件很大,大概在1G以上甚至更大,随即研究了读取得方法,直接使用PHP自带的函数就可以解决这几个问题,但是绝对不能使用file和file_get_contents,这两个函数是一次性将文件全部加载进来,如果文件在几十M 还是可以的,但是稍大的文件时不能用的,内存是会溢出的,贴个自己的方法,欢迎大家指教!
关于函数传入的变量$tag的值,根据系统不一样,传入的值也是有区别的:Windows用”\r\n”,linux/unix用”\n”,Mac OS用”\r”。
程序执行的大概流程:先定义读取文件的一些基础变量,然后打开文件,将指针定位在文件的指定位置,并读取指定大小的内容。每读取一次将内容存储在变量中,直到达到读取要求的行数或文件结束。
绝不要假定程序中的一切都将按计划运行。
根据上面的代码,虽然能够得到文件中指定位置、指定大小的数据,但这整个过程只执行了一次,并不能得到所有的数据。其实要得到所有的数据,可以在这个循环的外层再添加判断文件是否结束的循环,但这很浪费系统资源,甚至由于文件过大一直没法读完而导致PHP执行超时。另一种方法就是记录并存储上次读取数据后指针所在的位置,然后再次执行该循环的时候,将指针定位在上次结束的位置,这样就不存在一次循环要把文件从头读到尾的情况。
PHP还有其他方法能够解决,比如system函数,不足之处还望指正!
最近碰到一个比较有趣的问题,就是修改某个文件的某一行字符,不过文件太大,file()直接读取是不可能的,我使用fgets来跳转到指定行,并用fwrite修改某个字符串:</p>
$fp = fopen('d:/file.txt', 'r+');
if ($fp) {
$i = 1;
while (!feof($fp)) {
//修改第二行数据
if ($i == 2) {
fseek($fp, 2, SEEK_CUR);
fwrite($fp, '#');
break;
}
fgets($fp);
$i++;
}
fclose($fp);
}
这里需要注意的是fgets获取到一行后,文件指针指向行尾(也就是下一行开头),所以fwrite操作的是fgets后的下一行开头,至于从该行的第几个字符开始写,可以使用fseek函数来移动文件指针。另外一个需要注意的是,这里fwrite写入是执行替换操作,而不是插入操作,所以指针后面的字符会一个个被替换掉。至于怎么插入我就没研究了。估计很困难。为了效率可能只能写入另外一个临时文件了,不知道有没有其他更好的方法。
另外今天还看到了使用SPL进行操作的方法:
$fp = new SplFileObject('d:/file.txt', 'r+');
//转到第二行, seek方法参数从0开始计数, 经我测试指针指向行尾了, 所以修改的是第三行
$fp->seek(1);
//获取当前行内容(第二行)
$line = $fp->current();
//下面是对第三行的操作
$fp->fseek(2, SEEK_CUR);
$fp->fwrite('#');
SplFileObject提供的方法比基本的文件操作函数更丰富一些,包括采用key/value方法遍历文件行等。SPL应该是PHP5增加进去的吧,还有其他很多很有用的对象。包括数组、文件目录操作、异常处理、一些基本类型操作等,这些功能还在陆续增加,可以通过继承SPL扩展这些方法让我们处理底层的操作更方便。
读取到末尾的判断:$fp->eof()
来源:http://www.cnblogs.com/gaocheng/articles/1778499.html
http://www.php100.com/html/webkaifa/PHP/PHPyingyong/2009/1029/3453.html
从 handle 指向的文件中读取一行并返回长度最多为 length - 1 字节的字符串。碰到换行符(包括在返回值中)、EOF 或者已经读取了 length - 1 字节后停止(看先碰到那一种情况)。如果没有指定 length ,则默认为 1K,或者说 1024 字节。
bool feof ( resource $handle )
果传递的文件指针无效可能会陷入无限循环中,因为 EOF 不会返回 TRUE。
先上代码,正确的写法:
指针位置文件:line_number_mark.txt,需要读取N行的大文件:onlyQQFilteredByJack.txt
<?php
$num = 0;
$insert_num = 10; //insert amount every time.
if(file_exists('line_number_mark.txt'))
{
$num = trim(file_get_contents($rootPath . 'line_number_mark.txt'));
}
echo "pointer_num=".$num."\n";
$fp = fopen('./onlyQQFilteredByJack.txt', 'r');
fseek($fp, $num);
$i = 0;
while($i < $insert_num)
{
if(!feof($fp))
{
$data = fgets($fp);
$value = trim($data);
if(!empty($value))
{
$i++;
echo $value."\n";
}
}else
{
echo "out of the file's bindery,exit the while loop..\n";
$position = ftell($fp);
$flag = 1;//set flag means it's already end
break;//Break 退出循环
}
}
if($flag)
file_put_contents($rootPath . 'line_number_mark.txt', $position);
else
file_put_contents($rootPath . 'line_number_mark.txt', ftell($fp));
?>
指针的越位,错误的写法:
<?php
$num = 0;
$insert_num = 10; //insert amount every time.
if(file_exists($rootPath . 'line_number_mark.txt'))
{
$num = trim(file_get_contents($rootPath . 'line_number_mark.txt'));
}
echo "num=".$num."\n";
$fp = fopen($rootPath . 'onlyQQFilteredByJack.txt', 'r');
fseek($fp, $num);
echo ftell($fp)."\n";
if(feof($fp))
{
echo "Out of the file's bond\n";
exit();
}
$i = 0;
while($i < $insert_num)
{
$data = fgets($fp);//没fget一次,指针自动加一,后面ftell的值也是加一后的值,而在while下造成了越界,下次在来读取后在上面的if(feof($fp))出现传递的文件指针无效可能会陷入无限循环中,因为 EOF 不会返回 TRUE。
$value = trim($data);
if(!empty($value))
{
$i++;
echo $value."\n";
}
}
echo ftell($fp)."\n";
file_put_contents($rootPath . 'line_number_mark.txt', ftell($fp));
?>
回忆未来(372647693) 14:52:20
PHP读取大文件,一次读取,php的内存往往不够用,大家如何解决类似的问题?
膘叔(19129540) 14:52:51
那就别一次读。
莫莫(3296320) 14:53:24
用fopen
莫莫(3296320) 14:53:30
然后一点一点移指针
PHP内置函数filesize()返回文件的长度是int类型的,按照PHP文档上的sprintf('%u', filesize($str_filename)),最大也只能正确表示不大于2748436857字节(约2.6GB)。解决办法有不少,整理了最通用的办法如下:
在本例中, $int_filesize实际上是以浮点型存储的,但应理解为整数
Linux系统
$int_filesize = 0 + trim(`stat -c%s $str_file`);
Windows系统
$int_size = 0 + exec('FOR %A IN ("'. $str_file.'") DO @ECHO %~zA');
完整函数
function big_filesize($str_filename)
{
$int_filesize = filesize($str_filename);
if($int_filesize < 0){
if(strtolower(substr($_SERVER['OS'], 0, 3)) == 'win'){
$int_size = 0 + exec('FOR %A IN ("'. $str_file.'") DO @ECHO %~zA');
}else{
$int_filesize = 0 + trim(`stat -c%s $str_file`);
}
}
return $int_filesize;
}
最近在工作中遇到了需要读取系统日志的问题,日志文件很大,大概在1G以上甚至更大,随即研究了读取得方法,直接使用PHP自带的函数就可以解决这几个问题,但是绝对不能使用file和file_get_contents,这两个函数是一次性将文件全部加载进来,如果文件在几十M 还是可以的,但是稍大的文件时不能用的,内存是会溢出的,贴个自己的方法,欢迎大家指教!
关于函数传入的变量$tag的值,根据系统不一样,传入的值也是有区别的:Windows用”\r\n”,linux/unix用”\n”,Mac OS用”\r”。
程序执行的大概流程:先定义读取文件的一些基础变量,然后打开文件,将指针定位在文件的指定位置,并读取指定大小的内容。每读取一次将内容存储在变量中,直到达到读取要求的行数或文件结束。
绝不要假定程序中的一切都将按计划运行。
根据上面的代码,虽然能够得到文件中指定位置、指定大小的数据,但这整个过程只执行了一次,并不能得到所有的数据。其实要得到所有的数据,可以在这个循环的外层再添加判断文件是否结束的循环,但这很浪费系统资源,甚至由于文件过大一直没法读完而导致PHP执行超时。另一种方法就是记录并存储上次读取数据后指针所在的位置,然后再次执行该循环的时候,将指针定位在上次结束的位置,这样就不存在一次循环要把文件从头读到尾的情况。
PHP还有其他方法能够解决,比如system函数,不足之处还望指正!
最近碰到一个比较有趣的问题,就是修改某个文件的某一行字符,不过文件太大,file()直接读取是不可能的,我使用fgets来跳转到指定行,并用fwrite修改某个字符串:</p>
$fp = fopen('d:/file.txt', 'r+');
if ($fp) {
$i = 1;
while (!feof($fp)) {
//修改第二行数据
if ($i == 2) {
fseek($fp, 2, SEEK_CUR);
fwrite($fp, '#');
break;
}
fgets($fp);
$i++;
}
fclose($fp);
}
这里需要注意的是fgets获取到一行后,文件指针指向行尾(也就是下一行开头),所以fwrite操作的是fgets后的下一行开头,至于从该行的第几个字符开始写,可以使用fseek函数来移动文件指针。另外一个需要注意的是,这里fwrite写入是执行替换操作,而不是插入操作,所以指针后面的字符会一个个被替换掉。至于怎么插入我就没研究了。估计很困难。为了效率可能只能写入另外一个临时文件了,不知道有没有其他更好的方法。
另外今天还看到了使用SPL进行操作的方法:
$fp = new SplFileObject('d:/file.txt', 'r+');
//转到第二行, seek方法参数从0开始计数, 经我测试指针指向行尾了, 所以修改的是第三行
$fp->seek(1);
//获取当前行内容(第二行)
$line = $fp->current();
//下面是对第三行的操作
$fp->fseek(2, SEEK_CUR);
$fp->fwrite('#');
SplFileObject提供的方法比基本的文件操作函数更丰富一些,包括采用key/value方法遍历文件行等。SPL应该是PHP5增加进去的吧,还有其他很多很有用的对象。包括数组、文件目录操作、异常处理、一些基本类型操作等,这些功能还在陆续增加,可以通过继承SPL扩展这些方法让我们处理底层的操作更方便。
<?php
$fp= new SplFileObject('testfile.txt');
$fp->seek(1);
$line = $fp->current();
echo $line;
$fp->seek(881);
$line = $fp->current();
echo $line;
$fp->seek(123456);
$line = $fp->current();
echo $line;
?>
$fp= new SplFileObject('testfile.txt');
$fp->seek(1);
$line = $fp->current();
echo $line;
$fp->seek(881);
$line = $fp->current();
echo $line;
$fp->seek(123456);
$line = $fp->current();
echo $line;
?>
读取到末尾的判断:$fp->eof()
$fp = new SplFileObject('./chat.txt', 'r+');
$line = 0;
$totalLine = 0;
while (!$fp->eof()) {
$fp->current();
$totalLine++;
$fp->next();
}
$fp->seek($totalLine);
$line = 0;
$totalLine = 0;
while (!$fp->eof()) {
$fp->current();
$totalLine++;
$fp->next();
}
$fp->seek($totalLine);
<?php
$fp = new SplFileObject('./file.txt', 'r+');
//转到第二行, seek方法参数从0开始计数, 经我测试指针指向行尾了, 所以修改的是第三行
$fp->seek(1);
////获取当前行内容(第二行)
$line = $fp->current();
echo $line;
////下面是对第三行的操作
$fp->fseek(2, SEEK_CUR);
$fp->fwrite('#jackxiang');
?>
$fp = new SplFileObject('./file.txt', 'r+');
//转到第二行, seek方法参数从0开始计数, 经我测试指针指向行尾了, 所以修改的是第三行
$fp->seek(1);
////获取当前行内容(第二行)
$line = $fp->current();
echo $line;
////下面是对第三行的操作
$fp->fseek(2, SEEK_CUR);
$fp->fwrite('#jackxiang');
?>
来源:http://www.cnblogs.com/gaocheng/articles/1778499.html
http://www.php100.com/html/webkaifa/PHP/PHPyingyong/2009/1029/3453.html
作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:http://jackxiang.com/post/3495/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!
最后编辑: jackxiang 编辑于2013-9-26 17:20
评论列表