我正在尝试制作一个为玩家编写分数列表的功能。
例如:
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);
}
该文件只会附加新分数,但不会覆盖以前的分数。有人可以指出我的错误吗?
答案 0 :(得分:2)
尝试更改:
$fp = fopen($logfile,'w');
到
$fp = fopen($logfile,'a');
中的
if( $pieces[0] == $player->name ) ...
PHP.fopen文件打开模式;)
修改强>
您可以通过使用已加入行覆盖整个文件,将fwrite()
置于foreach循环后覆盖您的播放器条目(这可能会导致性能问题)。
或者
尝试使用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);
}
希望它有所帮助!