Surveiller les changements dans un fichier texte

Résolu
marliche0 Messages postés 66 Date d'inscription mardi 8 mars 2005 Statut Membre Dernière intervention 30 septembre 2008 - 6 juin 2007 à 11:22
marliche0 Messages postés 66 Date d'inscription mardi 8 mars 2005 Statut Membre Dernière intervention 30 septembre 2008 - 8 juin 2007 à 16:51
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

SharpMao Messages postés 1024 Date d'inscription mardi 4 février 2003 Statut Membre Dernière intervention 7 juin 2010 69
8 juin 2007 à 16:00
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)
3
marliche0 Messages postés 66 Date d'inscription mardi 8 mars 2005 Statut Membre Dernière intervention 30 septembre 2008
6 juin 2007 à 11:23
"Lecture de tout le fichier depuis le depuis."
=> depuis le début (erreur de frappe)
0
MorpionMx Messages postés 3466 Date d'inscription lundi 16 octobre 2000 Statut Membre Dernière intervention 30 octobre 2008 57
6 juin 2007 à 11:27
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# 
0
marliche0 Messages postés 66 Date d'inscription mardi 8 mars 2005 Statut Membre Dernière intervention 30 septembre 2008
6 juin 2007 à 11:38
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) {  ...  }
}
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
MorpionMx Messages postés 3466 Date d'inscription lundi 16 octobre 2000 Statut Membre Dernière intervention 30 octobre 2008 57
6 juin 2007 à 11:46
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# 
0
SharpMao Messages postés 1024 Date d'inscription mardi 4 février 2003 Statut Membre Dernière intervention 7 juin 2010 69
6 juin 2007 à 12:53
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)
0
marliche0 Messages postés 66 Date d'inscription mardi 8 mars 2005 Statut Membre Dernière intervention 30 septembre 2008
6 juin 2007 à 13:26
Oui, c'est exactement ce que je recherche mais de quel façon procèder??
0
SharpMao Messages postés 1024 Date d'inscription mardi 4 février 2003 Statut Membre Dernière intervention 7 juin 2010 69
6 juin 2007 à 13:45
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)
0
marliche0 Messages postés 66 Date d'inscription mardi 8 mars 2005 Statut Membre Dernière intervention 30 septembre 2008
6 juin 2007 à 14:21
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?
0
SharpMao Messages postés 1024 Date d'inscription mardi 4 février 2003 Statut Membre Dernière intervention 7 juin 2010 69
6 juin 2007 à 14:30
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)
0
marliche0 Messages postés 66 Date d'inscription mardi 8 mars 2005 Statut Membre Dernière intervention 30 septembre 2008
8 juin 2007 à 10:17
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...
0
SharpMao Messages postés 1024 Date d'inscription mardi 4 février 2003 Statut Membre Dernière intervention 7 juin 2010 69
8 juin 2007 à 15:06
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)
0
marliche0 Messages postés 66 Date d'inscription mardi 8 mars 2005 Statut Membre Dernière intervention 30 septembre 2008
8 juin 2007 à 15:45
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;
}
0
SharpMao Messages postés 1024 Date d'inscription mardi 4 février 2003 Statut Membre Dernière intervention 7 juin 2010 69
8 juin 2007 à 16:00
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)
0
marliche0 Messages postés 66 Date d'inscription mardi 8 mars 2005 Statut Membre Dernière intervention 30 septembre 2008
8 juin 2007 à 16:06
C'est le chargement du DataView qui prend toutes les ressources en fait...
0
SharpMao Messages postés 1024 Date d'inscription mardi 4 février 2003 Statut Membre Dernière intervention 7 juin 2010 69
8 juin 2007 à 16:18
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)
0
marliche0 Messages postés 66 Date d'inscription mardi 8 mars 2005 Statut Membre Dernière intervention 30 septembre 2008
8 juin 2007 à 16:51
Merci d'y avoir passé du temps, je vais chercher pour afficher le résultat différemment (listeView...).
0
Rejoignez-nous