2012-01-28 83 views
1

我正在尝试编写一个函数,为玩家编写一个分数列表。PHP:文件写入问题

例如:

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

该文件只是附加新分数并且不会覆盖以前的分数。有人能指出我的错误吗?

回答

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

希望它有帮助!

+0

啊,我现在明白了!谢谢一堆! – dukevin 2012-01-28 21:10:34

2

尝试改变:

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

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

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

PHP.fopen文件的打开方式

;)

Ë DIT 您可以通过将foreach循环后的fwrite()替换为加入行(这可能会导致性能问题)覆盖整个文件来覆盖您的播放器条目。

或者

尝试通过线环线使用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,但是在我的情况下它很好。

+0

是的,我已经试过,但它似乎并没有覆盖以前的条目 – dukevin 2012-01-28 10:49:03

+1

我会改进我的答案;)给我一秒 – veritas 2012-01-28 10:50:26

+0

感谢您的回答:)我仍然有问题,理解fgets虽然,你会介意写它应该怎么做(如果它不是太多的工作) – dukevin 2012-01-28 11:22:15

1

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

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

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

但你仍然会错过一些更新。你可以锁定文件,这意味着当一个人正在写另一个人时不能阅读。要做到这一点,你可以使用群功能:http://php.net/manual/en/function.flock.php

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