Surveiller les changements dans un fichier texte

Résolu
Signaler
Messages postés
66
Date d'inscription
mardi 8 mars 2005
Statut
Membre
Dernière intervention
30 septembre 2008
-
Messages postés
66
Date d'inscription
mardi 8 mars 2005
Statut
Membre
Dernière intervention
30 septembre 2008
-
Bonjour,

Je développe un outil winform sous visual studio qui affiche le contenu d'un fichier texte de trace. Cet outil winform se raffraichi toutes les secondes pour mettre à jour l'affichage. Actuellement, lors du raffraichissement, il est fait ceci:
Lecture de tout le fichier depuis le depuis.




StreamReader
sr =

new



StreamReader
(

"file"
);




string
rLine =

""
;











while
((rLine = sr.ReadLine()) !=

null
) {   ...   }








Mais plus ça va, plus le fichier est volumineux, et cela prend beaucoup de mémoire système.
J'aimerais si possible, que lors du raffraichissement, ne soit lu que les dernières données ajoutées depuis le précedent raffraichissement (car le fichier ne change pas, les nouvelles lignes sont ajoutées à la fin).

Auriez-vous une idée pour m'aider?
Merci par avance.

17 réponses

Messages postés
1024
Date d'inscription
mardi 4 février 2003
Statut
Membre
Dernière intervention
7 juin 2010
65
Hello,

Ce n'est pas le fait de lire le fichier qui est long, mais la construction et l'affichage de toutes ces infos.
Avec le code ci-dessous, je lis un fichier de de 7Mo.
using

(
StreamReader
sr =
new
StreamReader(
File.
Open(
filename,
FileMode.
Open,
FileAccess.
Read,
FileShare.
ReadWrite)))
{

   string
rLine;

   StringBuilder
sb =
new
StringBuilder();

   sr.
BaseStream.
Seek(
lastOffset,
SeekOrigin.
Begin);

   while ((
rLine =
sr.
ReadLine()) !=
null)
   {

      sb.
AppendLine(
rLine);
   }
             //Jusqu'à ce point, c'est quasiment instantané (d'un point de vue humain).

   textBox1.
Text + =
sb.
ToString(); //Cette ligne mets 5-10 secondes à s'éxécuter !

   lastOffset =
sr.
BaseStream.
Position;
}

Amicalement, SharpMao

"C'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!"
(Coluche / 1944-1986 / Pensées et anecdotes)
Messages postés
66
Date d'inscription
mardi 8 mars 2005
Statut
Membre
Dernière intervention
30 septembre 2008

"Lecture de tout le fichier depuis le depuis."
=> depuis le début (erreur de frappe)
Messages postés
3466
Date d'inscription
lundi 16 octobre 2000
Statut
Membre
Dernière intervention
30 octobre 2008
55
Salut,

Deja, plutot que de rafraichir toutes les secondes, pourquoi n'utilises-tu pas un FileSystemWatcher, qui declenchera un evenement dès que le fichier est modifié ?
Sinon, a priori, si tu liberes bien lles ressources utilisée par la lecture de ton fichier, tu ne devrais pas avoir de probleme de mémoire utilisée qui augmente sans cesse.

Mx
MVP C# 
Messages postés
66
Date d'inscription
mardi 8 mars 2005
Statut
Membre
Dernière intervention
30 septembre 2008

Merci, j'avais déjà regardé FileSystemWatcher mais je pensais que cela ne s'appliquait qu'aux répertoires.
Je fais rechercher à ce sujet.

Pour ce qui est de la libération des ressources utilisée par la lecture de mon fichier, je fais juste un close à la fin. Dois-je ajouter un dispose(), ou bien j'utilise un using??

StreamReadersr =
new
StreamReader(
"file");

stringrLine =
"";

while ((rLine = sr.ReadLine()) != null ) {   ...   }

sr.Dispose();   // à la place de sr.Close();

ou
using

(
StreamReader sr =
new
StreamReader(
"file")){

string rLine =
"";

while ((rLine = sr.ReadLine()) !=
null) {  ...  }
}
Messages postés
3466
Date d'inscription
lundi 16 octobre 2000
Statut
Membre
Dernière intervention
30 octobre 2008
55
En fait, le using(){} est une façon élégante d'appeler Dispose() a la fin du bloc, qu'il y ait une erreur ou non (equivalent d'un bloc finally{})

Donc un using fera l'affaire. Par contre, tu peux utiliser ReadToEnd() plutot qu'une boucle avec ReadLine();

Pour le FileSystemWatcher, tu peux tout a fait monitorer le dossier complet, en ne filtrant que les modifs sur le type de fichier qui t'interesse (*.txt par exemple), et lorsqu'il rencontre des modifs dans ce type de fichier (evenement Changed) alors tu lis le fichier qui t'interesse. A mon avis, ca restera quand meme plus propre que d'ouvrir/fermer ton fichier toutes les secondes.

Mx
MVP C# 
Messages postés
1024
Date d'inscription
mardi 4 février 2003
Statut
Membre
Dernière intervention
7 juin 2010
65
Hello,

Si les modifications du fichiers ne sont faites qu'à la fin (uniquement des ajouts et pas de modifications), tu peux aussi aller au dernier caractère connu et ne lire que la suite, au lieu de relire complétement le fichier à chaque fois.

Amicalement, SharpMao

"C'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!"
(Coluche / 1944-1986 / Pensées et anecdotes)
Messages postés
66
Date d'inscription
mardi 8 mars 2005
Statut
Membre
Dernière intervention
30 septembre 2008

Oui, c'est exactement ce que je recherche mais de quel façon procèder??
Messages postés
1024
Date d'inscription
mardi 4 février 2003
Statut
Membre
Dernière intervention
7 juin 2010
65
Tu peux essayer avec quelque chose de ce genre :

using (
StreamReader
sr =
new
StreamReader(
File.
Open(
filename,
FileMode.
Open,
FileAccess.
Read,
FileShare.
ReadWrite)))
{

   sr.
BaseStream.
Seek(
lastOffset,
SeekOrigin.
Begin);

   textBox1.
Text +=
sr.
ReadToEnd();

   lastOffset =
sr.
BaseStream.
Position;
}

Amicalement, SharpMao

"C'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!"
(Coluche / 1944-1986 / Pensées et anecdotes)
Messages postés
66
Date d'inscription
mardi 8 mars 2005
Statut
Membre
Dernière intervention
30 septembre 2008

Super,

par contre, je faisait du ligne à ligne avec ReadLine pour l'inserer dans un datarow.

while ((rLine = sr.ReadLine()) != null)

 {

DataRow row = table.NewRow();

row["Line"] = rLine;

row["Num"] = currentLine++;
...

}

Comment je peux lire le résultat du sr.ReadToEnd
()ligne à ligne?
Messages postés
1024
Date d'inscription
mardi 4 février 2003
Statut
Membre
Dernière intervention
7 juin 2010
65
Si tu veux faire du ligne à ligne, tu remplaces juste :

textBox1.Text += sr.ReadToEnd
();

Par :

while ((rLine = sr.ReadLine()) != null ) {   ...   }

Il y a peut-être de petits ajustements à faire, genre ajouter ou pas le premier rline à la dernière ligne connue,... mais en gros, ça doit fonctionner comme ça.

Amicalement, SharpMao

"C'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!"
(Coluche / 1944-1986 / Pensées et anecdotes)
Messages postés
66
Date d'inscription
mardi 8 mars 2005
Statut
Membre
Dernière intervention
30 septembre 2008

Bon j'avais essayé sur un petit fichier et c'était parfait, cela fonctionnait. Si je vide le tableau, il n'affiche que les lignes ajoutées.
Mais après avoir fait un test sur un très gros fichier, toutes mes ressources system sont utilisées pour lire ce fichier car le BaseStream parse quand même tout le fichier depuis le début...
J'ai essayé en partant de la fin mais j'ai le même résultat, il parse quand même la totalité du fichier...

long pos = lastOffset - fi.Lenght;
sr.BaseStream.Seek(pos,
SeekOrigin
.End);
while
((rLine = sr.ReadLine()) !=
null
)  {...}

Pas d'autres idées??
Je vais essayer le ReadToEnd...
Messages postés
1024
Date d'inscription
mardi 4 février 2003
Statut
Membre
Dernière intervention
7 juin 2010
65
Hello,

Chez moi, la ligne

sr.BaseStream.Seek(pos, SeekOrigin.End);

ne pose aucun problème, même avec un fichier de presque 1Go, par contre, si tu veux lire tout le contenu du fichier et le mettre en mémoire, c'est une autre histoire !

Amicalement, SharpMao

"C'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!"
(Coluche / 1944-1986 / Pensées et anecdotes)
Messages postés
66
Date d'inscription
mardi 8 mars 2005
Statut
Membre
Dernière intervention
30 septembre 2008

Alors je dois avoir des erreurs dans mon code mais là je ne vois plus... Car chez moi c'est super long. Le refresh est appelé sur l'appel d'un bouton.



private



void
RefreshFile()
{
      timer.Enabled =

false
;


      Cursor =

Cursors
.WaitCursor;


      try

      {


               FileInfo
fi =

new



FileInfo
( filename );


               if
(fi.LastWriteTime fileDate && fi.Length fileSize)
               {
                        Cursor =

Cursors
.Default;
                        timer.Enabled = autoRefresh.Checked;
                         

return
;
                }


               if
((fileDate ==

DateTime
.MinValue) || (fi.Length < fileSize))
               {
                        currentLine = 1;
                        lastOffset = 0;
                        table.Rows.Clear();
                

}               dataGrid.SuspendLayout();
               dataView.Table =


null
;
               

fileDate = fi.LastWriteTime;   
               fileSize = fi.Length;
               


fi.CopyTo( "file", true );


               StreamReader sr = new StreamReader( "file" );






               string
rLine =

""
;
               

long
pos = lastOffset - fileSize;   
               sr.BaseStream.Seek(pos,

SeekOrigin
.End);


               while
((rLine = sr.ReadLine()) !=

null
)
               {
                           

DataRow
row = table.NewRow();
                           row[

"Line"
] = rLine;
                           row[

"Num"
] = currentLine++;
                           

if
(rLine.IndexOf(

"[Error]"
) != -1)
                           {
                                    row[

"Type"
] =

"Error"
;
                           }
                           

else



if
(rLine.IndexOf(

"[Warning]"
) != -1)
                           {
                                    row[

"Type"
] =

"Warning"
;
                           }


                           else



if
(rLine.IndexOf(

"[Info]"
) != -1)
                           {
                                    row[

"Type"
] =

"Info"
;
                           }


                           else



if
(rLine.IndexOf(

"[Verbose]"
) != -1)
                           {
                                    row[

"Type"
] =

"Verbose"
;
                           }


                           else



if
(showNone.Checked)
                           {
                                    row[

"Type"
] =

"None"
;
                           }
                           table.Rows.Add(row);
               }               lastOffset = sr.BaseStream.Position;
               sr.Close();   
               fi =


new



FileInfo
(

"file"
);
               fi.Delete();
               dataView.Table = table;
               dataGrid.ResumeLayout(

false
);
               SetFilter();
      }
      

catch
(

Exception
e) {}
      Cursor =

Cursors
.Default;
      timer.Enabled = autoRefresh.Checked;
}
Messages postés
1024
Date d'inscription
mardi 4 février 2003
Statut
Membre
Dernière intervention
7 juin 2010
65
Argh, j'ai de nouveau perdu des espaces en route, heureusement qu'il y a la couleur !

Amicalement, SharpMao

"C'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!"
(Coluche / 1944-1986 / Pensées et anecdotes)
Messages postés
66
Date d'inscription
mardi 8 mars 2005
Statut
Membre
Dernière intervention
30 septembre 2008

C'est le chargement du DataView qui prend toutes les ressources en fait...
Messages postés
1024
Date d'inscription
mardi 4 février 2003
Statut
Membre
Dernière intervention
7 juin 2010
65
Eh oui,

C'est pour ça que quand je m'étais amusé à faire un éditeur héxadécimal, j'avais fait mon propre control pour afficher le contenu du fichier.
Je gérais complétement l'affichage, mais c'était fluide même avec un fichier de 1Go, et ça prtenais très peu d'espace mémoire..

Amicalement, SharpMao

"C'est pas parce qu'ils sont nombreux à avoir tort qu'ils ont raison!"
(Coluche / 1944-1986 / Pensées et anecdotes)
Messages postés
66
Date d'inscription
mardi 8 mars 2005
Statut
Membre
Dernière intervention
30 septembre 2008

Merci d'y avoir passé du temps, je vais chercher pour afficher le résultat différemment (listeView...).