suethi75
Messages postés101Date d'inscriptionmercredi 5 novembre 2008StatutMembreDernière intervention31 août 2009
-
26 déc. 2008 à 14:02
cs_Bidou
Messages postés5487Date d'inscriptiondimanche 4 août 2002StatutMembreDernière intervention20 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.
cs_Bidou
Messages postés5487Date d'inscriptiondimanche 4 août 2002StatutMembreDernière intervention20 juin 201361 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...
sebmafate
Messages postés4936Date d'inscriptionlundi 17 février 2003StatutMembreDernière intervention14 février 201437 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...
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
}
olibara
Messages postés666Date d'inscriptiondimanche 16 décembre 2007StatutMembreDernière intervention11 mars 20106 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
jesusonline
Messages postés6814Date d'inscriptiondimanche 15 décembre 2002StatutMembreDernière intervention13 octobre 201029 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)
olibara
Messages postés666Date d'inscriptiondimanche 16 décembre 2007StatutMembreDernière intervention11 mars 20106 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
cs_Bidou
Messages postés5487Date d'inscriptiondimanche 4 août 2002StatutMembreDernière intervention20 juin 201361 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...
olibara
Messages postés666Date d'inscriptiondimanche 16 décembre 2007StatutMembreDernière intervention11 mars 20106 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);
}
jesusonline
Messages postés6814Date d'inscriptiondimanche 15 décembre 2002StatutMembreDernière intervention13 octobre 201029 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)
cs_coq
Messages postés6349Date d'inscriptionsamedi 1 juin 2002StatutMembreDernière intervention 2 août 2014101 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).
jesusonline
Messages postés6814Date d'inscriptiondimanche 15 décembre 2002StatutMembreDernière intervention13 octobre 201029 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 ?