Suppression d'une ligne dans un fichier .txt

suethi75 Messages postés 101 Date d'inscription mercredi 5 novembre 2008 Statut Membre Dernière intervention 31 août 2009 - 26 déc. 2008 à 14:02
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 - 30 déc. 2008 à 00:32
Bonjour tout le monde,

Je suis en train de faire un qcm. Je souhaite supprimer des éléments dans une ligne d'un fichier .txt.  Comment puis je faire?? Existe t-il une fonction qui permet de supprimer les éléments d'une ligne. Désolé je ne peux pas vous donnez mon code, je ne l'ai pas sur ce poste.

Merci en tout cas pour votre aide.

18 réponses

cs_coq Messages postés 6349 Date d'inscription samedi 1 juin 2002 Statut Membre Dernière intervention 2 août 2014 101
30 déc. 2008 à 00:26
Exemple avec le simple texte :
"Ligne1
Ligne2"

Enregistré en UTF16 Little Endian ("Unicode"), ça donne la représentation (hexa) suivante au stockage :
4C 00 69 00 67 00 6E 00 65 00 31 00 0D 00 0A 00
4C 00 69 00 67 00 6E 00 65 00 32 00

Enregistré en UTF16 Big Endian, ça donne :
00 4C 00 69 00 67 00 6E 00 65 00 31 00 0D 00 0A
00 4C 00 69 00 67 00 6E 00 65 00 32

(J'ai enlever les préambules et mis à la ligne pour plus de lisibilité)

/*
coq
MVP Visual C#
CoqBlog
*/
1
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 61
30 déc. 2008 à 00:32
Moui, en fait si on écrit le reste du stream (après avoir trouvé la ligne) en une seule fois, on gagne un if par octet restant, ce qui peut faire pas mal si le fichier est encore long...
En plus, il faudra rajouter de la logique qui sera exécutée sur chaque octet avant d'avoir trouvé la ligne, donc au final... on sera probablement très dépendant du type de fichier (taille, numéro de ligne à supprimer, etc).

Comme le dis coq, du code à écrire en plus pour gagner... presque rien?
En plus, WriteByte écrit dans un buffer je pense donc qu'avec

        public static void SuppressLign(string fileName, int lineNb)
        {
            string tmpFileName = Path.GetTempFileName();



            using (FileStream fsr = new FileStream(fileName, FileMode.Open))
            using (FileStream fsw = new FileStream(tmpFileName, FileMode.Create))
            {
                int c, lineCount = 0;                bool skip lineNb 0;



                while ((c = fsr.ReadByte()) != -1)
                {
                    if (c == 13)
                    {
                        ++lineCount;
                        bool sTemp = skip;                        skip lineCount lineNb;
                        if (sTemp) continue;
                    }
                    if (!skip) fsw.WriteByte((byte)c);
                }
            }
            File.Delete(fileName);
            File.Move(tmpFileName, fileName);
        }



on a quelque chose de tout à fait correct pour le peu de nombre de ligne que ça demande.
Concernant le problème d'encoding et représentation de octet avec big/little endian, faudrait faire des testes supplémentaires...





<hr />
-Blog-
-Site Perso-
1
sebmafate Messages postés 4936 Date d'inscription lundi 17 février 2003 Statut Membre Dernière intervention 14 février 2014 37
26 déc. 2008 à 14:31
bah... le plus simple, c'est de charger le contenu du fichier text dans un table de string (string[]), de supprimer la ligne concernée et de réécrire le contenu du fichier...

Sébastien FERRAND (blog)
Consultant Sénior
[Microsoft Visual C# MVP]
0
olibara Messages postés 666 Date d'inscription dimanche 16 décembre 2007 Statut Membre Dernière intervention 11 mars 2010 6
26 déc. 2008 à 14:56
Ben oui !

+1
0

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

Posez votre question
jesusonline Messages postés 6814 Date d'inscription dimanche 15 décembre 2002 Statut Membre Dernière intervention 13 octobre 2010 29
27 déc. 2008 à 22:56
Bonsoir,

Si je puis me permettre, on peut aussi faire du code efficace, optimisé !

static void Main(string[] args)
{
    String fileName = @"C:\XXX";
    String tmpFileName = Path.GetTempFileName();




    int lineCount = 0;


    using (FileStream originalFS = new FileStream(fileName, FileMode.Open))
    using (StreamReader sr = new StreamReader(originalFS))
    using (FileStream newFS = new FileStream(tmpFileName, FileMode.Create))
    using (StreamWriter sw = new StreamWriter(newFS)) {
        int c;
        Boolean skip = false;
        while ((c = originalFS.ReadByte()) != -1) {            if (c 13) {                                          // 13> newline
                lineCount++;                skip (lineCount 3);                            // la position de la ligne qu'on supprime
            }


            if (!skip) {
                newFS.WriteByte((Byte)c);
            }
        }
    }


    //File.Delete(fileName);
    //File.Copy(tmpFileName, fileName);
}





<hr />
Cyril - MVP ASP.net - MCPD ASP.net & MCTS SQL - Consultant indépendant
0
olibara Messages postés 666 Date d'inscription dimanche 16 décembre 2007 Statut Membre Dernière intervention 11 mars 2010 6
27 déc. 2008 à 23:05
Salut Jesus


Moi je veux supprimer la premiere ligne avec ce code


 
0
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 61
28 déc. 2008 à 17:40
Tu remplaces 3 par une variable ;-)

<hr />
-Blog-
-Site Perso-
0
olibara Messages postés 666 Date d'inscription dimanche 16 décembre 2007 Statut Membre Dernière intervention 11 mars 2010 6
28 déc. 2008 à 17:45
-1 pour Bidou
0
olibara Messages postés 666 Date d'inscription dimanche 16 décembre 2007 Statut Membre Dernière intervention 11 mars 2010 6
28 déc. 2008 à 17:59
Pour commenter le code de Jesus :

1- Skip ne peut passer true qu' APRES la detection du premier CR (entretemps les caracteres de la premiere ligne sont déja écrits)

2- Si on manipule un fichier UNIX, on est bien avec le CR !

3- Je n'ai pas encore eu l'occasion de faire beaucoup de test de performance en Csharp mais je suis CERTAIN qu'en C on ne peut pas prétendre "faire du code efficace, optimisé" en traitant ainsi un fichier caractére par caractere
0
jesusonline Messages postés 6814 Date d'inscription dimanche 15 décembre 2002 Statut Membre Dernière intervention 13 octobre 2010 29
28 déc. 2008 à 18:18
Euh, c'est juste un bout de code, effectivement pour le cas de la premiere ligne ca passe pas ! Mais je pense pas qu'il y a de difficulté à modifier le bout de code pour faire en sorte que ca passe ! Pour le linux, meme remarque que ci-dessus :-)
J'aurais du mettre un commentaire que ce bout de code n'avait pas été testé et qu'il contenant pleins de bugs ;-) 

En ce qui concerne  l'optimisation ! Partir sur une solution File.ReadAllLines et ensuite faire du traitement sur un String[], me fais un peu mal ! On entend souvent que le framework .net est pas efficace blabla, que cela pond des applications super lourdes blabla ... Le gros problème c'est que .net donne la possibilité de faire du code sans qu'on est besoin de réfléchir. Certes pour un fichier de 100 lignes, mettre tout dans un String[] on va pas voir de grosse différence, mais quand on travaille sur des fichiers de plusieurs milliers de lignes ... Bref, ca coute pas grand chose de passer 3 minutes sur une manipulation de Stream !

Pour la remarque, lire un fichier caractère par caractère. Si on reste dans le monde managed (donc pas de unmanaged avec des pointeurs & co) je ne pense pas que l'on puisse faire plus efficace ! Il y a certes une fonction ReadLine au niveau du StreamReader, je viens de la décompilé et ca ne fais que lire caractère par caractère jusqu'a tomber sur un 10 ou un 13.
A part si on connait par avance la taille de la ligne, je ne vois pas comment on peut optimiser ce bout de code ! (faut bien sur gérer les cas à la con, etc ... mais un forum est là pour donner une piste, pas une solution prête à l'emploi)

PS : je prefere Cyril que  Jesus :-)

<hr />Cyril - MVP ASP.net - MCPD ASP.net & MCTS SQL - Consultant indépendant
0
olibara Messages postés 666 Date d'inscription dimanche 16 décembre 2007 Statut Membre Dernière intervention 11 mars 2010 6
28 déc. 2008 à 18:30
Salut Cyril
C'est ta remarque qui m'avait donné envie de te taquiner
"Si je puis me permettre, on peut aussi faire du code efficace, optimisé ! "

Et c'est bidou qui est tombé dans le panneau !
J'espère que tu ne m'en veux pas ...
D'accord le readAllLlines c'est certainement la plus mauvaise chose a faire !


Faire un ReadLine a l'avantage de rendre le code plus lisible et d'eviter les bete bug Unix ou premiere ligne


 


 
0
jesusonline Messages postés 6814 Date d'inscription dimanche 15 décembre 2002 Statut Membre Dernière intervention 13 octobre 2010 29
28 déc. 2008 à 18:46
vivi, aucun soucis, j'avais bien vu que c'était un taquinement ;-)

De toute facon, les habitués du forum savent bien qu'il faut pas trop faire confiance à ce bidou<strike>ille</strike>

PS : Bidou : sans rancune ? ;-)

<hr />Cyril - MVP ASP.net - MCPD ASP.net & MCTS SQL - Consultant indépendant
0
cs_Bidou Messages postés 5487 Date d'inscription dimanche 4 août 2002 Statut Membre Dernière intervention 20 juin 2013 61
29 déc. 2008 à 14:22
Bah j'étais en pseudo-vacances (comprennez par la: pas chez moi) donc je suis excusé!
Allez, pour la peine voici un code fonctionnel:

 public static void SuppressLign(string fileName, int lineNb)
        {
            string tmpFileName = Path.GetTempFileName();


            using (FileStream originalFS = new FileStream(fileName, FileMode.Open))
            using (StreamReader sr = new StreamReader(originalFS))
            using (FileStream newFS = new FileStream(tmpFileName, FileMode.Create))
            {
                int c, lineCount = 0;                bool skip lineNb 0;


                while ((c = originalFS.ReadByte()) != -1)
                {
                    if (c == 13)
                    {
                        ++lineCount;
                        bool skipTmp = skip;                        skip lineCount lineNb;
                        if (skipTmp) continue;
                    }
                    if (!skip) newFS.WriteByte((byte)c);
                }
            }
            File.Delete(fileName);
            File.Copy(tmpFileName, fileName);
        }

Peut-être qu'on peut mieux faire en lisant avec un buffer...





<hr />
-Blog-
-Site Perso-
0
olibara Messages postés 666 Date d'inscription dimanche 16 décembre 2007 Statut Membre Dernière intervention 11 mars 2010 6
29 déc. 2008 à 20:04
Bon allez !

Je vais proposer le mien ! : c'est encore plus court et je fais un rename plutot qu'un copy !

public static void SuppressLign(string fileName, int lineNb)
{
  string tmpFileName = Path.GetTempFileName();


  using (FileStream originalFS = new FileStream(fileName, FileMode.Open))
  using (StreamReader sr = new StreamReader(originalFS))
  using (FileStream newFS = new FileStream(tmpFileName, FileMode.Create))
  {
    int c, lineCount = 0;
    string buf="";
    lineNb++;
    while ((buf=sr.ReadLine()) != null)
    {
      lineCoun++;
      if (lineCount==lineNb)
      {
        continue;
      }
      newFS.WriteLine(buf);
    }
  }
  File.Delete(fileName);
  File.Move(tmpFileName, fileName);
}
0
jesusonline Messages postés 6814 Date d'inscription dimanche 15 décembre 2002 Statut Membre Dernière intervention 13 octobre 2010 29
29 déc. 2008 à 20:41
FileStream ne possède pas de méthode WriteLine :) et même si c'était le cas, la méthode ReadLine fait un while(readbyte() != 10 || 13) puis met chaque caractère dans un StringBuilder puis retourne sb.toString(); du coup cette solution sera moins efficace que boucler sur chaque caractère.
De plus évite d'utiliser "" mais String.Empty; et dans ton cas, le plus propre serait de mettre buf = null; (utiliser "" instancie un String alors que String.Empty utilise une instance de String partagé dans tout le framework)
Par contre le File.Move plutot que le File.Copy :-)

La solution la plus "correcte" me parait donc etre celle de Bidou (j'ai pas lu en détail le code)

<hr />Cyril - MVP ASP.net - MCPD ASP.net & MCTS SQL - Consultant indépendant
0
cs_coq Messages postés 6349 Date d'inscription samedi 1 juin 2002 Statut Membre Dernière intervention 2 août 2014 101
29 déc. 2008 à 21:19
Logiquement StreamReader gère un buffer en interne, il ne va pas aller lire octet par octet depuis le disque (logiquement ici on a un buffer de 1Ko).

Sinon attention avec les exemples manipulant directement les octets : vous semblez partir du principe que l'entrée sera dans un encodage mono-octet, quid des autres (et de leurs différences d'endianess) ?

Et puisque vous avez envie de vous amuser, un test à faire ici (si on est en présence de fichiers d'une taille conséquente, autrement l'algo se complexifie pour pas grand chose, donc risque de bug accru pour peu de gains) : une fois la ligne (ou les lignes) a supprimer passée(s), il faudrait voir quel gain serait apporté par un passage en copie brute des octets d'un flux à l'autre (évitemment de la surcharge de la logique de ReadLine).

/*
coq
MVP Visual C#
CoqBlog
*/
0
olibara Messages postés 666 Date d'inscription dimanche 16 décembre 2007 Statut Membre Dernière intervention 11 mars 2010 6
29 déc. 2008 à 22:51
Ok  5/5

Ce que tu dis tu ReadLine m'affecte beaucoup ! quel bazar 
Pour le WriteLine c'est vrai j'avais un streamwriter en tete

Mais pour la logique générale je penche pour l'avis de coq
0
jesusonline Messages postés 6814 Date d'inscription dimanche 15 décembre 2002 Statut Membre Dernière intervention 13 octobre 2010 29
29 déc. 2008 à 23:20
Après discussion msn avec coq (oui, je sais, c'est pas bien de traiter d'un sujet de forum hors forum)

pour le ReadLine(); c'est pas exactement mon code qui est utilisé, ils utilises en interne un Char[] qui aura l'avantage de prendre correctement en compte le cas multi-octet (utf-16)
En effet, "mon" code fonctionne pour des fichiers multi-octets car le caractère \n (ou \r je sais jamais qui est qui) vaut 0013 en UTF-16 (little ou big endian, je ne sais plus non plus qui est qui) c'est à dire 2 Bytes 0x00 et 0x0D. Lorsque j'itere sur tous les charactère je vais malgré tout faire une comparaison sur 0x0D si j'ai une fichier (en utf16) qui vaut (en hexa)

0074 0026 000D
00E3 00E5 000D
0034 00D3 000D

"mon" code va correctement bien supprimer toute la ligne, le byte 0x00 sera compris dans la ligne à supprimer. Par contre, d'après ce que j'ai compris, en little (ou big) endian, le fichier vaut qqchose du genre

7400 2600 0D00
E300 E500 0D00
3400 D300 0D00

"mon" code fonctionnera aussi supprime toutes les lignes sauf la premiere, en effet le byte 0x00 restant de la suppression servira pour la suppression suivante. Par contre dans le cas de la suppression de la premiere ligne, on aura un décalage d'un byte (0x00), il faudra donc gérer le cas "1ère ligne" d'une facon bien particuliere.

Pour ce qui est de la copie "en masse" une fois les lignes supprimés (ie passé par un Byte[] plutot qu'un Byte) serait une idée à tester. J'ai regardé en interne et le framework passe par du unmanaged et la classe static System.Buffer (que je ne connaissais pas) pour faire ses copies.  Il est donc possible que l'on obtienne des meilleures performances (mais on triche puisqu'on utilise une méthode du framework qui utilise du unmanaged ;-))

Bref, plutot que de parler, qui s'y colle à faire des tests de perf ?

<hr />Cyril - MVP ASP.net - MCPD ASP.net & MCTS SQL - Consultant indépendant
0
Rejoignez-nous