C#: gestoion de la mémoire [Résolu]

Signaler
Messages postés
89
Date d'inscription
mardi 19 janvier 2010
Statut
Membre
Dernière intervention
21 juin 2016
-
Messages postés
834
Date d'inscription
samedi 15 novembre 2008
Statut
Membre
Dernière intervention
14 janvier 2017
-
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();
        }


Merci pour votre aide :)

3 réponses

Messages postés
834
Date d'inscription
samedi 15 novembre 2008
Statut
Membre
Dernière intervention
14 janvier 2017
28
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
}
}
}
}
}

bon code
Bob


C# is amazing, enjoy it!
Messages postés
89
Date d'inscription
mardi 19 janvier 2010
Statut
Membre
Dernière intervention
21 juin 2016
2
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?.
Messages postés
834
Date d'inscription
samedi 15 novembre 2008
Statut
Membre
Dernière intervention
14 janvier 2017
28
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.

Bob

C# is amazing, enjoy it!