Karibot
Messages postés89Date d'inscriptionmardi 19 janvier 2010StatutMembreDernière intervention21 juin 2016
-
22 oct. 2010 à 16:51
cs_Robert33
Messages postés834Date d'inscriptionsamedi 15 novembre 2008StatutMembreDernière intervention14 janvier 2017
-
22 oct. 2010 à 23:35
bonjour à tous,
j'ai un gros fichier que j'essaye de dédupliquer...
pour cela je fais un Dictionnary<>, et je compte en meme temps le nombre d'occurence de chaque ligne, sauf que la mémoire n'en peut plus. le fichier fait plusieurs millions de lignes (2,4Go)
j'essaye de trouver un moyen de pouvoir aller au bout de ma tache sans faire plater le programme à chaque fois (ce que je comprend puisqu'il charge tout dans le dico qui bouffe ma RAM).
voici ma fonction:
public static void Deduplique(string p)
{
StreamReader oReader = new StreamReader(p);
StreamWriter oWriter = new StreamWriter(p+".uniq.txt");
Dictionary<string, int> dico = new Dictionary<string, int>();
string line;
while ((line= oReader.ReadLine()) !=null)
{
if (dico.ContainsKey(line))
dico[line]++;
else
dico.Add(line, 1);
}
foreach (KeyValuePair<string, int> oKv in dico)
{
oWriter.WriteLine("{0}\t{1}", oKv.Key, oKv.Value);
}
oReader.Close();
oWriter.Close();
}
cs_Robert33
Messages postés834Date d'inscriptionsamedi 15 novembre 2008StatutMembreDernière intervention14 janvier 201733 22 oct. 2010 à 19:21
Bonsoir,
Pas simple comme probleme. je suppose que tu n'as pas une base de données sous la main.
perso je pencherais pour un traitement en 2 passes, et j'utiliserais un HashCode pour verifier l'existance de lignes
mais tout dépend des lignes, principalement leur longueur, car le hashcode n'est pas certain, mais ça prend moins de place en mémoire.
ex:
public static void Deduplique(string p)
{
Dictionary lineCounter = new Dictionary();
//premiere passe on compte
using (StreamReader oReader = new StreamReader(p))
{
string line;
while ((line = oReader.ReadLine()) != null)
{
int hash = line.GetHashCode();
if (lineCounter.ContainsKey(hash))
lineCounter[hash]++;
else
lineCounter.Add(hash, 1);
}
}
//Deuxieme passe on écrit
using (StreamReader oReader = new StreamReader(p))
{
string line;
using (StreamWriter oWriter = new StreamWriter(p + ".uniq.txt"))
{
while ((line = oReader.ReadLine()) != null)
{
int hash = line.GetHashCode();
if (lineCounter[hash] >0)//si ==0 alors déjà traitée
{
oWriter.Write(string.Format("{0}\t{1}",line,lineCounter[hash]));
lineCounter[hash]=0;// pour eviter la suppression qui entrainerait une reord du dictionnaire
}
}
}
}
}
Karibot
Messages postés89Date d'inscriptionmardi 19 janvier 2010StatutMembreDernière intervention21 juin 20162 22 oct. 2010 à 21:05
Top, merci Robert33.
autre question: quand j'essaye de remplir une list<string> à partir d'un gros fichier, ou remplir un stringBuilder trop gros j'ai aussi ce type d'erreur (outofmemory), comment on peut régler le problème? on n'utilise pas de Using(){} dans ces cas là, non. quelle est l'internative?.
cs_Robert33
Messages postés834Date d'inscriptionsamedi 15 novembre 2008StatutMembreDernière intervention14 janvier 201733 22 oct. 2010 à 23:35
Salut Karibot,
La clause using permet juste d'être certain que les resources déclarées sont bien liberées, c'est plutot une facilité d'écriture.
une liste, ou un stringbuilder, c'est de la mémoire, quand on a plus assez de mémoire il faut utiliser le disque, c'est pour cela, entre autre, qu'on a créé les base de données.
le travail sur les fichiers ne necessite pas toujours de le monter en memoire, tu peux aussi utiliser des pointeurs de lecture sur le fichier disque.
avec un stream tu peux te déplacer directement dans un fichier et ne stocker en mémoire que les informations minimales
bref, il n'y a pas de solution simple à ce type de probleme, c'est vraiment du cas par cas.