2012-01-28 109 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的例子很好,很簡單:沒有外部服務器進程或密碼...