fgets

(PHP 4, PHP 5, PHP 7, PHP 8)

fgets从文件指针获取行

说明

fgets(resource $stream, ?int $length = null): string|false

从文件指针获取一行。

参数

stream

文件指针必须有效,并且必须指向由 fopen()fsockopen() 成功打开的文件(并且尚未由 fclose() 关闭)。

length

读取在读取了 length - 1 个字节、遇到换行符(包含在返回值中)或遇到 EOF(以先发生者为准)时结束。如果未指定长度,则将持续从流中读取,直到到达行尾。

返回值

返回从 stream 指向的文件中读取的最大 length - 1 个字节的字符串。如果文件指针中没有更多数据可读,则返回 false

如果发生错误,则返回 false

示例

示例 #1 按行读取文件

<?php

$fp
= @fopen("/tmp/inputfile.txt", "r");

if (
$fp) {
while ((
$buffer = fgets($fp, 4096)) !== false) {
echo
$buffer, PHP_EOL;
}

if (!
feof($fp)) {
echo
"Error: unexpected fgets() fail\n";
}

fclose($fp);
}

?>

注释

注意: 如果 PHP 在读取 Mac 机上或由 Mac 机创建的文件时未正确识别行尾符,则启用 auto_detect_line_endings 运行时配置选项可能有助于解决此问题。

注意:

熟悉 fgets() 的 'C' 语义的人应该注意 EOF 的返回方式不同。

参见

添加注释

用户贡献的注释 38 个注释

Leigh Purdie
9 年前
一个更好的示例,用于说明 fgets 和 stream_get_line 在处理大型文件时的速度差异。

此示例模拟了从输入源读取可能非常长、长度不确定(但有最大缓冲区大小)的行的场景。

正如 Dade 指出的那样,我之前提供的示例过于简单,没有充分说明我要解决的问题。

请注意,为 fgets 指定明确的结束字符(例如:换行符)通常会相当显著地降低速度差异。

#!/usr/bin/php
<?php
$plaintext
=file_get_contents('http://loripsum.net/api/60/verylong/plaintext'); # 应该在 90k 个字符左右
$plaintext=str_replace("\n"," ",$plaintext); # 去掉换行符

$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 比 fgets 更快 - pdiff 是 ". ($pdiff>1?"faster":"slower") . " - pdiff 是 "$pdiff"\n";
?>

$ ./testcase.php
stream_get_line 比 fgets 更快 - pdiff 是 1.760398041785

需要注意的是,在大多数使用 PHP 的情况下,系统调用之间的微小速度差异无关紧要。
tavernadelleidee[italy]
18 年前
我认为,以相反顺序读取(长)文件的最快方法是

<?php
$myfile
= 'myfile.txt';
$command = "tac $myfile > /tmp/myfilereversed.txt";
passthru($command);
$ic = 0;
$ic_max = 100; // 在读取此行数后停止
$handle = fopen("/tmp/myfilereversed.txt", "r");
while (!
feof($handle) && ++$ic<=$ic_max) {
$buffer = fgets($handle, 4096);
echo
$buffer."<br>";
}
fclose($handle);
?>

它在读取文件时会回显行,因此对于像日志这样的长文件来说很好。

Borgonovo
Anonymous
4 年前
如果你出于某种原因需要从字符串而不是文件指针获取行,试试这个

<?php
function string_gets(string $source, int $offset = 0, string $delimiter = "\n"): ?string
{
$len = strlen($source);
if (
$len < $offset) {
// 超出范围.. 或许我应该抛出一个异常
return null;
}
if (
$len === $offset) {
// 字符串结束..
return null;
}
$delimiter_pos = strpos($source, $delimiter, $offset);
if (
$delimiter_pos === false) {
// 最后一行.
return substr($source, $offset);
}
return
substr($source, $offset, ($delimiter_pos - $offset) + strlen($delimiter));
}

?>

(我有一个大约 16GB 的字符串存储在内存中,需要逐行处理,但我如果尝试使用 explode("\n",$str); 处理会发生内存分配崩溃(在 32GB 内存系统上),所以我用这个来解决.. 有趣的是,fgets() 似乎比在 PHP 中进行内存内处理要快。 PHP 7.3.7)
David at Weintraub.name
17 年前
文档中有一个错误

文件指针必须有效,并且必须指向由 fopen() 或 fsockopen() 成功打开(并且尚未由 fclose() 关闭)的文件。

你应该还在文档中添加 "popen" 和 "pclose"。我是一名新的 PHP 开发人员,去验证我是否可以在我用 "popen" 使用的命令上使用 "fgets"。
Peter Schlaile
16 年前
fscanf($file, "%s\n") 并不是 fgets() 的一个好的替代方案,因为它会在第一个空格处停止解析,而不是在行尾处停止解析!

(请参阅 fscanf 页面以了解这方面的详细信息)
afwxkat at gmail dot com
15 年前
我发现 fgets 的一个问题,至少在 PHP 5.1.6 中,你可能需要使用一个 IF 语句来避免你的代码失控(并可能挂起服务器)。如果你在正在工作的服务器上没有 root 权限,这会导致问题。

这是我实现的代码($F1 是一个数组)

<?php
if($fh = fopen("filename","r")){
while (!
feof($fh)){
$F1[] = fgets($fh,9999);
}
fclose($fh);
}
?>

我注意到,如果没有 IF 语句,fgets 似乎会忽略 $fh 未定义的情况(即 "filename" 不存在)。如果发生这种情况,它将不断尝试从不存在的文件句柄中读取,直到进程被管理员终止或服务器挂起,以先发生者为准。
anacreo has gmail
16 年前
我正在使用这个函数修改一个大型 PostScript 文档的标题... 迄今为止运行速度非常快...

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);
}
angelo [at] mandato <dot> com
19 年前
有时,你想要从文件中读取的字符串不是用换行符分隔的。C 样式的 getline() 函数解决了这个问题。这是我的版本
<?php
function getline( $fp, $delim )
{
$result = "";
while( !
feof( $fp ) )
{
$tmp = fgetc( $fp );
if(
$tmp == $delim )
return
$result;
$result .= $tmp;
}
return
$result;
}

// 示例:
$fp = fopen("/path/to/file.ext", 'r');
while( !
feof($fp) )
{
$str = getline($fp, '|');
// 对 $str 进行操作
}
fclose($fp);
?>
d at foo.com
18 年前
对于套接字,如果您不想在没有数据的情况下让 fgets、fgetc 等函数阻塞。设置 socket_set_blocking(handle,false); 并使用 socket_set_blocking(handle,true); 恢复设置。
Carlo
6 年前
本页示例 1 中存在错误。

将以下行
$buffer = fgets($fd, 4096);
更改为
$buffer = fgets($handle, 4096);
lzsiga at freemail.c3.hu
14 年前
有些人试图在 fgets 之前调用 feof,然后忽略 fgets 的返回值。这种方法会导致在到达文件末尾时处理值为 FALSE。

错误示例
<?php
$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);
?>

正确示例
<?php
$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);
?>
kpeters AT-AT monolithss DEE OH TEE com
18 年前
似乎 fgets() 在 EOF 时会返回 FALSE(在 feof 有机会读取它之前),因此以下代码会抛出异常

while (!feof($fh)) {
$line = fgets($fh);
if ($line === false) {
throw new Exception("File read error");
}
}
Leigh Purdie
15 年前
对于大型文件,考虑使用 stream_get_line 而不是 fgets - 它可以带来显著的差异。

$ 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
dandrews OVER AT 3dohio DOT com
19 年前
Saku 的示例也可以这样使用

<?php
@ $pointer = fopen("$DOCUMENT_ROOT/foo.txt", "r"); // @ 抑制错误,因此您需要测试指针是否存在
if ($pointer) {
while (!
feof($pointer)) {
$preTEXT = fgets($pointer, 999);
// $TEXT .= $preTEXT; 对于字符串来说更好
$ATEXT[$I] = $preTEXT; // 作为数组可能更好
$I++;
}
fclose($pointer);
}
?>
Dade Brandon
10 年前
关于 Leigh Purdie 4 年前的评论,他认为 stream_get_line 对于大型文件更好,我决定测试一下,看看它是否在那之后进行了优化。我发现 Leigh 的评论完全错误。

fgets 的性能确实略好,但 Leigh 做的测试无法产生良好的结果。

建议的测试是

$ 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);'

0m1.616s

$ time yes "This is a test line" | head -1000000 | php -r '$fp=fopen("php://stdin","r"); while($line=fgets($fp,65535)) { 1; } fclose($fp);'

0m7.392s

造成这种情况的原因是 65535 的缓冲区大小完全没有必要。

将 "yes 'this is a test line'" 的输出管道到 PHP 中,使每行包含 19 个字符加上分隔符。

因此,虽然我不知道为什么 stream_get_line 在缓冲区过大时性能更好,但如果两个缓冲区大小都正确或默认,那么它们的性能差异微不足道 - 虽然值得注意的是,stream_get_line 是一致的 - 但是,如果您正在考虑切换,请确保了解两个函数之间的区别,即 stream_get_line 不会追加分隔符,而 fgets 会追加分隔符。

以下是我在一台服务器上的测试结果。

缓冲区大小 65535
stream_get_line: 0.340s
fgets: 2.392s

缓冲区大小 1024
stream_get_line: 0m0.348s
fgets: 0.404s

缓冲区大小 8192(两者默认值)
stream_get_line: 0.348s
fgets: 0.552s

缓冲区大小 100
stream_get_line: 0.332s
fgets: 0.368s
Anonymous
18 年前
文档中提到的 Macintosh 行尾符指的是 Mac OS Classic。对于与类 Unix 的 OS X 互操作,您不需要此设置。
ecvej
18 年前
我希望以下代码片段具有相同的行为:-

<?php

/* 这会正确超时 */
while (!feof($fp)) {
echo
fgets($fp);
}

/* 这在 EOF 之前超时 */
while ($line=fgets($fp)) {
echo
$line;
}

/* 一个合理的解决办法是设置一个较长的超时时间 */
stream_set_timeout($fp, 180);
while (
$line=fgets($fp)) {
echo
$line;
}
?>
bfb1985 at yahoo dot es
8 年前
我在 WAMP(Windows 服务器)上使用 fgets 读取时遇到了很多麻烦。在本地,文件顺利进入 <pre> 标签,但是当我将代码移动到 LAMP 生产服务器时,每个 \r\n 都创建了两个 fgets,我得到了额外的空行。

我尝试使用 $string=str_replace("\r\n","\n",$string); 删除,但它没有任何效果。解决方案是使用 fread() 并根据 PHP_EOL 拆分内容,然后使用 foreach($lines as $line) 进行操作,这样每行就不会重复。

以下示例代码

$file=fopen("test.txt,"r");
$text=fread($file,filesize("test.txt"));
$lines=explode(PHP_EOL,$text);
foreach($lines as $line)
{
// 执行操作
}
Anonymous
6 年前
<form>
<input type='text' name='filepath' value='<?php echo ((isset($_GET["filepath"])) ? $_GET["filepath"] : "");?>'>
<br>
<select name='sel'>
<option value='var1' <?php echo ((isset($_GET["sel"]) && $_GET["sel"] == "var1") ? "selected=true" : "");?>> For Year</option>
<option value='var2' <?php echo ((isset($_GET["sel"]) && $_GET["sel"] == "var2") ? "selected=true" : "");?>>姓名</option>
<option value='var3' <?php echo ((isset($_GET["sel"]) && $_GET["sel"] == "var3") ? "selected=true" : "");?>>姓名&年份</option>
</select>
<br>
<input type='submit' value='按钮'>

</form>

<?php

if((!isset($_GET['filepath']) || !file_exists($_GET['filepath'])) || !isset($_GET['sel']))
exit(
"");

echo
"列表<br>";
$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
"<p>";
for(
$i = 0; $i < count($arr); $i++)
echo
"<i>{$arr[$i][0]}</i>; ";
echo
"</p>";
}
else if(
$_GET["sel"] == "var2"){
for(
$i = 0; $i < count($arr); $i++)
echo
"<p><b>{$arr[$i][1]}</b></p>";
}
else if(
$_GET["sel"] == "var3"){
for(
$i = 0; $i < count($arr); $i++)
echo
"<p><b>{$arr[$i][1]}</b>: <i>{$arr[$i][0]}</i></p>";
}

?>
sam dot bryan at montal dot com
18 年前
一种简单的方法,可以在非 Windows 或非域机器上运行的脚本中验证 Windows 域用户 - 将提交的用户名和密码传递到 Windows 机器上的 IMAP 服务。

<?php
$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;

return
false;
}
?>
svayn at yahoo dot com
18 年前
fgets 在扫描大型文件时速度很慢。如果你没有 PHP 5,可以使用 fscanf($file, "%s\n") 代替。
lelkesa
19 年前
请注意,据我所知,fgets 会读取一行,直到遇到换行符 (\\n)。回车符 (\\r) 不会被处理为行尾。
但是,nl2br 会在回车符之前插入 <br /> 标签。
这在你想将多行存储在一个文件中时很有用(但不是很好 - 我必须承认)。
<?php
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
'<u>部分</u><p>nl2br'.($line).'</p>';
}
fclose($file);
}
?>

试试看。
Anonymous
19 年前
如果你需要模拟一个非缓冲的 fgets,以便 stdin 不在那里挂起等待输入(即它只有在有数据可用时才读取),请使用以下代码
<?php

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));
}

}

?>
Daniel Klein
16 年前
fgets() 使用的文件指针也可以使用 proc_open() 函数创建,并与从执行的进程创建的 stdout 管道一起使用。
timr
20 年前
如果你需要将整个文件读入一个字符串,请使用 file_get_contents()。fgets() 在你需要分别处理文件中的行时最有用。
dan at censornet dot com
12 年前
警告!fgets() 和我想象中对文件句柄的任何 read() 调用,例如:

while(!feof(STDIN)) {
$line = fgets(STDIN);

...对 $line 做一些有用的操作...
}

...在我的安装中,将在默认的 60 秒后超时。这种行为是非标准的(不符合 POSIX 规范),在我看来是一个错误,或者如果没有,也是一个应该更清楚地记录的重大问题。

在超时后,fgets() 将返回 FALSE (=== FALSE),但是,你可以通过检查 feof($stream) 来查看流(文件句柄)是否真正关闭,例如:

while(!feof(STDIN)) {
$line = fgets(STDIN);

if($line === FALSE) {
if(feof(STDIN)) {
break;
}
continue;
}

...对 $line 做一些有用的操作...
}
bobo
7 年前
一个简单的示例,演示如何使用“fgets”
此示例展示了如何从 .txt 文件中读取信息

<?php
$z
=fopen("protocol.txt","r"); //r = read
if($pointer!=false) //是否可以打开文件?!
{
//重复 protocol.txt 中的所有信息
while(!feof($pointer)) //循环运行,直到指针位于文件的末尾
{
$row = fgets($pointer); // $row 读取文件行中的信息
echo "<p>".$row."</p>";
}
fclose($pointer); //必须关闭文件
}
else
{
echo
"<p> 无法打开文件!</p>";
}
?>
hackajar <matt> yahoo <trot> com
18 年前
处理非常大的文件时,PHP 往往会崩溃并停止运行。

以下是一种从文件中快速提取数据块的简洁方法,不会在行中间停止,而是在最后一个已知行的末尾停止。它在约 24 秒内处理了一个超过 3000 万行、900 MB 的文件。

注意
$buf 仅保存当前要处理的数据块。如果您尝试使用 “$buf .=” (注意 “=” 前面的点)追加 $buff,脚本在处理约 100 MB 的数据时会变得非常慢,因此请处理当前数据,然后继续进行!

//要打开的文件
$file = "huge.file";
//打开文件(不要使用 a+ 指针,它将是错误的!)
$fp = fopen($file, 'r');
//读取 16 MB 块
$read = 16777216;
//\n 标记
$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;
//如果我们位于文件的末尾,只需获取剩余部分并停止循环
elseif(feof($fp)) {
$i = $read;
$buf = substr($rbuf, 0, $i+1);
break;
}
}
//这是我们想要处理的缓冲区,也许可以将其交给一个函数?
$buf = substr($rbuf, 0, $i+1);
//将标记点回退到最后一个 \n 点
$part = ftell($fp)-($read-($i+1));
fseek($fp, $part);
}
fclose($fp);
zsjpxah
4 年前
奇怪的是,没有人在这个上下文中提到 "0"。

由于 "0" 被认为是 false,因此如果使用 while 赋值习语,则包含单个 "0" 的行可以被视为 EOF。

while ($line = fgets(STDIN, 2)) {
}

这可能令人惊讶地会在行以 ")" 开头时中断。
bogdan at insightmed dot eu
8 年前
即使这与 PHP 本身并没有直接关系,在 Linux 系统上使用 fgets 从 CLI 读取输入时也要小心,因为它可能由于这些系统上参数长度的限制而表现出意想不到的行为。例如,在用户输入大于 4095 个字符时执行 rtrim(fgets(STDIN), "\n") 会将输入字符串截断为 4095 个字符。可以通过在脚本运行之前执行 "stty -icanon",并在脚本运行之后执行 "stty icanon" 来解决此问题。
alex at alex-at dot ru
8 年前
对于想要为套接字提供适当的非阻塞 fgets 的任何人,这里有一个很小的代码片段可以做到这一点(尽管与 fgets 相比,性能应该很糟糕)

<?php
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;
}
?>
apardo at NOSPAM dot gmail dot com
12 年前
这是一个检测任何文件换行符类型的简单函数。

<?php
function detectEndOfLine($file)
{
$handle = @fopen($file, "r");
if (
$handle)
{
$char=0;
while (!
$eol || feof($handle))
{
$char++;
$line = fgets($handle, $char);
$eol = preg_match("/(\r)+/", $line)? "W" : "";
if(!
$eol) $eol = preg_match("/(\n)+/", $line)? "L" : "";
}
return
$eol;
fclose($handle);
}
}
?>
wojons
10 年前
这篇文章是献给 Leigh Purdie(5 年前)和 Dade Brandon(4 个月前)的。

所以我说 Leigh 发布了帖子,然后我心想天哪,我需要把所有 fgets 都改成 stream_get_line。然后我按照 Leigh Purdie 评论中的说明运行了测试,他的结果是:

$ 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

我的结果与他的结果显示了相同的问题。但首先,PHP 至少比第一次运行测试时快了 2 到 5 倍(或者硬件更好了)。

现在让我们来谈谈 Dade Brandon,他指出如果你使用正确的缓冲区大小,性能会相当接近。

$ 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

如你所见,它们非常接近,fgets 稍微领先了一点点。我怀疑 fgets 是反向读取缓冲区,或者将所有内容加载到自身,然后尝试找出答案,而正确设置的缓冲区则可以解决问题。Dade Brandon 指出 fgets 会让你知道行是如何分隔的。stream_get_line 允许你使用它的第三个参数选择你想要使用的分隔符。

fgets 还有一个重要的选项,你无需设置行长度。因此,在你不确定行长度的情况下,例如在处理 HTTP 协议或日志行等其他内容时,你可以简单地省略它,仍然可以获得出色的性能。

$ 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

这比设置缓冲区要好。
hgs at cs dot columbia dot edu
13 年前
套接字和 auto_detect_line_endings 设置之间似乎存在交互,会导致相当奇特的行为。显然,如果从套接字读取的第一行跨越两个 TCP 数据包,检测器将查看第一个 TCP 数据包并确定系统使用的是 MacOS(\r)换行符,即使 LF 包含在下一个数据包中。例如,这影响了 PEAR Net_SMTP 包,它会神秘地仅对某些电子邮件服务器失败。
david_sitller at blackbit dot de
16 年前
如果你使用命令描述中的示例,我建议在进一步使用之前修剪 $buffer。换行符仍然位于行的末尾。我在使用 PHP CLI 时看到了这一点。

例如,检查文件列表中是否存在条目

$handle = fopen ("/tmp/files.txt", "r");
while (!feof($handle)) {
$buffer = fgets($handle, 4096);
if (file_exists(rtrim($filename,"\n"))) {
echo $buffer;
} else {
echo $buffer." has been removed."
}
fclose ($handle);
Anonymous
10 年前
这篇文章是献给 Leigh Purdie(5 年前)和 Dade Brandon(4 个月前)的。

所以我说 Leigh 发布了帖子,然后我心想天哪,我需要把所有 fgets 都改成 stream_get_line。然后我按照 Leigh Purdie 评论中的说明运行了测试,他的结果是:

$ 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

我的结果与他的结果显示了相同的问题。但首先,PHP 至少比第一次运行测试时快了 2 到 5 倍(或者硬件更好了)。

现在让我们来谈谈 Dade Brandon,他指出如果你使用正确的缓冲区大小,性能会相当接近。

$ 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

如你所见,它们非常接近,fgets 稍微领先了一点点。我怀疑 fgets 是反向读取缓冲区,或者将所有内容加载到自身,然后尝试找出答案,而正确设置的缓冲区则可以解决问题。Dade Brandon 指出 fgets 会让你知道行是如何分隔的。stream_get_line 允许你使用它的第三个参数选择你想要使用的分隔符。

fgets 还有一个重要的选项,你无需设置行长度。因此,在你不确定行长度的情况下,例如在处理 HTTP 协议或日志行等其他内容时,你可以简单地省略它,仍然可以获得出色的性能。

$ 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

这比设置缓冲区要好。
Pete
20 年前
如果你在版本 <= 4.3.2 中读取二进制数据时遇到问题,请升级到 4.3.3。
二进制安全实现似乎存在一些错误,这些错误已在 4.3.3 中修复。
jerem-NoSpam-ified at live dot com
16 年前
值得注意的是,此函数仅将 chr(10) 视为换行符,而不使用 chr(13)。我个人更喜欢使用 chr(13) 作为换行符。
To Top