PHP:文件编写问题

时间:2012-01-28 10:43:10

标签: php file fwrite

我正在尝试制作一个为玩家编写分数列表的功能。

例如:

player_1 100 12 12 10
player_2 39 13 48 29

当玩家击败(或做得更差)时,他们的分数会被新分数覆盖。

我写了一个有效的功能,但有很多问题。

function write($player)
{
    global $logfile;
    $lines = file($logfile);
    foreach($lines as $i => $line)
    {
        $pieces = explode(" ", $line);
        $pieces[0] = trim($pieces[0]);
        if( $pieces[0] == $player->name )  //found name
        {   
            trim($lines[$i]);
            unset($lines[$i]);  //remove the old player data
            $lines[$i] = "{$player->name} {$player->lvl} {$player->exp} {$player->mana} \n"; //write the new score
            $fp = fopen($logfile,'a');
            fwrite($fp,$lines[$i]);
            $found = TRUE;
            break;
        }
    }
    if(!$found) //record a new player whose score isn't in the file
    {
        $fp = fopen($logfile,'a');
        $newp = "$player->name $player->lvl $player->exp $player->mana \n";
        fwrite($fp, $newp); 
    }
    fclose($fp);
}

该文件只会附加新分数,但不会覆盖以前的分数。有人可以指出我的错误吗?

3 个答案:

答案 0 :(得分:2)

尝试更改:

$fp = fopen($logfile,'w');

$fp = fopen($logfile,'a');

中的

if( $pieces[0] == $player->name ) ...

PHP.fopen文件打开模式;)

修改 您可以通过使用已加入行覆盖整个文件,将fwrite()置于foreach循环后覆盖您的播放器条目(这可能会导致性能问题)。

或者

尝试使用fgets()逐行循环,然后如果找到正确的匹配,请使用fseek()到上一行并覆盖它;)

fgets() fseek()

第二次编辑

<?php

$find = 'player_1';

$h = fopen('play.txt','r+');
$prev_pos = 0;
while(($line = fgets($h, 4096)) !== false){
    $parts = explode(' ', $line);

    if($parts[0] == $find) {
        fseek($h, $prev_pos);
        fwrite($h, "player_222 12 22 411");
        break;
    }
    $prev_pos = ftell($h);
}

fclose($h);

?>

请求的代码示例;)想法是保存上一行位置,然后将其用于fseek并覆盖。我不确定fwrite在行尾没有PHP_EOL的情况下是否能在所有环境中正常运行,但在我看来它很好。

答案 1 :(得分:1)

此代码是否在同时访问许多用户的Web服务器上运行?

如果是,想象当一个用户刚刚打开文件进行写入时会发生什么,文件被清空,另一个用户在第一个用户完成数据写入之前打开它进行读取。

部分解决方案是写入临时文件,并在完成后将temp重命名为原始文件。重命名是原子的,因此用户将看到原始文件或新文件,而不是介于两者之间。

但你仍然会错过一些更新。您可以锁定文件,这意味着当一个人正在写另一个人时无法读取。为此,您将使用flock函数:http://php.net/manual/en/function.flock.php

正确的解决方案使用真实数据库。例如,Sqlite很简单:没有外部服务器进程或密码...

答案 2 :(得分:1)

首先,让我们看看它重复记录的原因。 $lines是一个数组,您可以在其中更新特定播放器的记录。但是在更新记录之后,您将它附加到文件(使用“a”模式),因此复制该播放器的条目。

这个想法应该是更新文件中的那条记录。根据您的逻辑,最好的方法是将$lines重写为文件。由于$lines将始终包含更新的条目,因此它是有道理的。

现在进入逻辑,你正在为新玩家制作一个条目。这种逻辑没有错,但可以通过将新条目附加到$lines而不是写入文件来改进。

这是更新的代码。请注意,我删除了不需要的行。

function write($player) {
    global $logfile;
    $found = FALSE;
    $lines = file($logfile);
    foreach($lines as $i => $line) {
        $pieces = explode(" ", $line);
        $pieces[0] = trim($pieces[0]);
        if( $pieces[0] == $player->name ) {  //found name
            $lines[$i] = "{$player->name} {$player->lvl} {$player->exp} {$player->mana} \n"; //write the new score
            $found = TRUE;
            break;
        }
    }
    if(!$found) { //record a new player whose score isn't in the file
        $lines[] = "$player->name $player->lvl $player->exp $player->mana \n";
    }

    file_put_contents($logfile, $lines);
}

希望它有所帮助!