2010-03-08 5 views
14

J'ai un script qui, chaque fois appelé, obtient la première ligne d'un fichier. Chaque ligne est connue pour avoir exactement la même longueur (32 caractères alphanumériques) et se termine par "\ r \ n". Après avoir obtenu la première ligne, le script l'enlève.Est-ce le moyen le plus efficace pour obtenir et supprimer la première ligne du fichier?

Cela se fait ainsi:

$contents = file_get_contents($file)); 
$first_line = substr($contents, 0, 32); 
file_put_contents($file, substr($contents, 32 + 2)); //+2 because we remove also the \r\n 

Il est évident que cela fonctionne, mais je me demandais s'il y a une façon de le faire plus intelligemment (ou plus efficace) cela?

Dans ma solution simple je fondamentalement lire et réécrire le fichier entier juste pour prendre et enlever la première ligne.

+0

Vous pouvez le rendre plus efficace en mémoire (faire une boucle, lire une ligne à la fois, les écrire un à temps sauf le premier), mais il aura l'air compliqué et sera sujet aux erreurs. Je ferais la même chose que toi. Il n'y a pas moyen de contourner le fait que les fichiers sont stockés séquentiellement, à partir du premier octet. –

+1

si vous pouviez stocker le fichier en tant qu'indexé, et effectuer tout R/W à travers l'index, peut-être cette opération serait plus rapide car vous pouvez simplement supprimer cette ligne de l'index et le faire serait moins cher que de faire cette opération sur un fichier complet. Toutefois, si le fichier est petit, le coût des E/S serait inférieur au coût de maintenance de l'index. – anijhaw

+3

La seule solution optimale à un problème similaire que je puisse penser impliquerait un IOCTL dans le pilote du système de fichiers qui couperait le premier bloc logique (de taille dépendante du matériel et de l'implémentation) du fichier sans toucher le reste. Mais c'est un exercice académique pour résoudre le problème inexistant, et certainement pas ce que vous recherchez. :) –

Répondre

14

Il n'y a pas de moyen plus efficace de le faire que de réécrire le fichier.

2

est ici une façon:

$contents = file($file, FILE_IGNORE_NEW_LINES); 
$first_line = array_shift($contents); 
file_put_contents($file, implode("\r\n", $contents)); 

Il y a d'innombrables autres façons de le faire aussi, mais toutes les méthodes impliquerait la séparation de la première ligne en quelque sorte et sauver le reste. Vous ne pouvez pas éviter de réécrire le fichier entier. Une autre prise:

list($first_line, $contents) = explode("\r\n", file_get_contents($file), 2); 
file_put_contents($file, implode("\r\n", $contents)); 
+0

Votre premier exemple générera des retours à la ligne redondants. Sans l'indicateur 'FILE_IGNORE_NEW_LINES' pour' file() ', vous n'avez pas besoin de' implode() 'les lignes avec des retours à la ligne. –

+0

@fireeyedboy, c'est vrai, corrigé. –

+0

Ulman: +1 code très intéressant, merci! Je n'ai jamais utilisé la fonction de fichier avant. –

-1

Vous pouvez utiliser la méthode file().

Obtient la première ligne

$content = file('myfile.txt'); 
echo $content[0]; 
+0

Cela a fonctionné très bien pour moi. Je ne sais pas pourquoi quelqu'un vous a marqué pour cela. Merci. J'ai essayé de vous voter, mais vous êtes toujours à 0. :) – bozdoz

+1

Cette méthode est très inefficace contre les fichiers volumineux. –

+4

Cela ne supprime pas la 1ère ligne, elle l'obtient simplement. –

2

Vous pouvez stocker les informations de position dans le fichier lui-même. Par exemple, les 8 premiers octets du fichier peuvent stocker un nombre entier. Cet entier est le décalage d'octet de la première ligne réelle dans le fichier. Donc, vous ne supprimez plus jamais de lignes. Au lieu de cela, supprimer une ligne signifie modifier la position de départ. fseek() et lisez les lignes normalement.

Le fichier grossira éventuellement. Vous pouvez périodiquement nettoyer les lignes orphelines pour réduire la taille du fichier. Mais sérieusement, utilisez simplement une base de données et ne faites pas ce genre de choses.

3

Je recommande généralement pas ouvrir une coquille pour ce genre de chose, mais si vous faites cela souvent sur des fichiers très grands, il y a probablement quelque chose à dire pour:

$lines = `wc -l myfile` - 1; 
`tail -n $lines myfile > newfile`; 

Il est simple et cela n'implique pas de lire le fichier entier en mémoire.

Je ne recommanderais pas cela pour les petits fichiers, ou une utilisation extrêmement fréquente. Les frais généraux sont trop élevés.

+2

Ce n'est pas efficace en tout cas et le code n'est pas portable – anijhaw

+5

Pour, par exemple, un fichier de 3 gigaoctets, ce sera beaucoup plus efficace que la plupart des réponses postées ici. La plupart des réponses affichées vont disparaître avec des erreurs de mémoire insuffisante sur les gros fichiers. Tu as raison de dire que ce n'est pas portable. Il existe un ensemble très spécifique de circonstances dans lesquelles cette solution serait utile/acceptable. –

+0

Serait sympa d'avoir des commentaires sur celui-ci, je connais bien linux et j'ai toujours des problèmes avec celui-ci. ty – iGNEOS

4

vous pouvez itérer le fichier, au lieu de les mettre tous en mémoire

$handle = fopen("file", "r"); 
$first = fgets($handle,2048); #get first line. 
$outfile="temp"; 
$o = fopen($outfile,"w"); 
while (!feof($handle)) { 
    $buffer = fgets($handle,2048); 
    fwrite($o,$buffer); 
} 
fclose($handle); 
fclose($o); 
rename($outfile,$file); 
+0

+1: Je pense que c'est plus efficace en mémoire, mais pas plus rapide. Certes, il ne va pas exploser si le fichier est trop gros pour tenir dans la mémoire. –

17

je suis venu avec cette idée hier:

function read_and_delete_first_line($filename) { 
    $file = file($filename); 
    $output = $file[0]; 
    unset($file[0]); 
    file_put_contents($filename, $file); 
    return $output; 
} 
+0

Vous lisez et réécrivez toujours le fichier entrie, mais j'avoue que c'est un peu mieux. +1 –

+0

Merci, juste ce que je cherchais – snapplex

+1

Ne fonctionne pas avec de gros fichiers – ana

11

Pas besoin de créer un second fichier temporaire, ni mettre le fichier entier dans la mémoire:

if ($handle = fopen("file", "c+")) {    // open the file in reading and editing mode 
    if (flock($handle, LOCK_EX)) {    // lock the file, so no one can read or edit this file 
     while (($line = fgets($handle, 4096)) !== FALSE) { 
      if (!isset($write_position)) {  // move the line to previous position, except the first line 
       $write_position = 0; 
      } else { 
       $read_position = ftell($handle); // get actual line 
       fseek($handle, $write_position); // move to previous position 
       fputs($handle, $line);   // put actual line in previous position 
       fseek($handle, $read_position); // return to actual position 
       $write_position += strlen($line); // set write position to the next loop 
      } 
     } 
     fflush($handle);       // write any pending change to file 
     ftruncate($handle, $write_position);  // drop the repeated last line 
     flock($handle, LOCK_UN);     // unlock the file 
    } 
    fclose($handle); 
} 
+1

Pourriez-vous s'il vous plaît ajouter de brefs commentaires à côté du code pour expliquer ce que vous faites ?! –

+0

Idée intéressante +1 –

+0

Ce code ne fonctionne tout simplement pas, il écrase simplement les lignes en place. Voir la version de travail basée sur celui-ci dans la [réponse par Marcos Fernandez Ramo] (https://stackoverflow.com/questions/2404707/is-this-the-most-efficace-way-to-get-and-remove-first -line-in-file/23269245 # 23269245) – user

5

Cela déplacera la première ligne d'un fichier, vous n'avez pas besoin de charger le fichier entier en mémoire comme vous le faites en utilisant la fonction 'fichier'. Peut-être que pour les petits fichiers, c'est un peu plus lent qu'avec 'file' (peut-être mais je ne parie pas) mais il est capable de gérer les plus gros fichiers sans problèmes.

$firstline = false; 
if($handle = fopen($logFile,'c+')){ 
    if(!flock($handle,LOCK_EX)){fclose($handle);} 
    $offset = 0; 
    $len = filesize($logFile); 
    while(($line = fgets($handle,4096)) !== false){ 
     if(!$firstline){$firstline = $line;$offset = strlen($firstline);continue;} 
     $pos = ftell($handle); 
     fseek($handle,$pos-strlen($line)-$offset); 
     fputs($handle,$line); 
     fseek($handle,$pos); 
    } 
    fflush($handle); 
    ftruncate($handle,($len-$offset)); 
    flock($handle,LOCK_UN); 
    fclose($handle); 
} 
+0

Est-il en quelque sorte mieux que la [réponse par Edakos] (https://stackoverflow.com/questions/2404707/-this-the-most-efficace-way-to-get-and-remove-first-line-in -fichier/20908373 # 20908373)? – user

+0

Je l'ai fait parce que j'étais incapable de faire fonctionner la solution Edakos correctement. Dans la ligne 3, il y a un "continuer" erroné. –

+0

En effet, le code dans cette réponse ne fonctionne pas. +1 à vous. – user

0

Je pense que c'est le meilleur pour toute taille de fichier

$myfile = fopen("yourfile.txt", "r") or die("Unable to open file!"); 
$ch=1; 

while(!feof($myfile)) { 
    $dataline= fgets($myfile) . "<br>"; 
    if($ch == 2){ 
    echo str_replace(' ', '&nbsp;', $dataline)."\n"; 
    } 
    $ch = 2; 
} 
fclose($myfile); 
Questions connexes