PHP: проблема записи файлов - PullRequest
1 голос
/ 28 января 2012

Я пытаюсь создать функцию, которая записывает список очков для игроков.

Например:

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 ]

2 голосов
/ 28 января 2012

Попробуйте изменить:

$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 голос
/ 28 января 2012

Во-первых, давайте посмотрим причину, почему он дублирует запись. $lines - это массив, в котором вы обновляете запись конкретного игрока. Но после обновления записи вы добавляете ее в файл (в режиме «а») и, следовательно, дублируете запись этого проигрывателя.

Идея должна состоять в том, чтобы обновить эту запись в файле. А с вашей логикой лучше всего переписать $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);
}

Надеюсь, это поможет!

1 голос
/ 28 января 2012

Этот код выполняется на веб-сервере с одновременным доступом многих пользователей?

Если это так, представьте, что происходит, когда один пользователь только что открыл файл для записи, файл очищается, идругой открывает его для чтения до того, как первый закончит запись данных.

Частичным решением является запись во временный файл и переименование временного файла в исходное.Переименование является атомарным, поэтому пользователи увидят либо оригинальный файл, либо новый, а не что-то среднее.

Но вы все равно пропустите некоторые обновления.Вы можете заблокировать файл, что означает, что когда один человек пишет, другой не может читать.Для этого вы должны использовать функцию flock: http://php.net/manual/en/function.flock.php

Правильное решение - с использованием реальной базы данных .Sqlite, например, хорош и прост: никаких внешних серверных процессов или паролей ...

...