Remplacer la longue liste de mots dans un gros fichier texte

j’ai besoin d’une méthode rapide pour travailler avec un gros fichier texte

j’ai 2 fichiers, un gros fichier texte (~ 20 Go) et un autre fichier texte contenant environ 12 millions de liste de mots combos

Je veux trouver tous les mots combo dans le premier fichier texte et le remplacer par un autre mot Combo (mot combo avec soulignement)

exemple “Informations sur l’ordinateur”> Remplacer par> “Informations_ordinateur”

J’utilise ce code mais les performances sont très médiocres

public partial class Form1 : Form { HashSet wordlist = new HashSet(); private void loadComboWords() { using (StreamReader ff = new StreamReader(txtComboWords.Text)) { ssortingng line; while ((line = ff.ReadLine()) != null) { wordlist.Add(line); } } } private void replacewords(ref ssortingng str) { foreach (ssortingng wd in wordlist) { // ReplaceEx(ref str,wd,wd.Replace(" ","_")); if (str.IndexOf(wd) > -1) str.Replace(wd, wd.Replace(" ", "_")); } } private void button3_Click(object sender, EventArgs e) { ssortingng line; using (StreamReader fread = new StreamReader(txtFirstFile.Text)) { ssortingng writefile = Path.GetFullPath(txtFirstFile.Text) + Path.GetFileNameWithoutExtension(txtFirstFile.Text) + "_ReplaceComboWords.txt"; StreamWriter sw = new StreamWriter(writefile); long intPercent; label3.Text = "initialing"; loadComboWords(); while ((line = fread.ReadLine()) != null) { replacewords(ref line); sw.WriteLine(line); intPercent = (fread.BaseStream.Position * 100) / fread.BaseStream.Length; Application.DoEvents(); label3.Text = intPercent.ToSsortingng(); } sw.Close(); fread.Close(); label3.Text = "Finished"; } } } 

toute idée de faire ce travail dans un délai raisonnable

Merci

À première vue, l’approche que vous avez adoptée a l’air bien – elle devrait fonctionner correctement et rien ne permet de penser, par exemple, à la récupération des ordures.

Je pense que l’essentiel, c’est que vous n’utiliserez qu’un de ces seize kernelx: rien n’est en place pour répartir la charge entre les quinze autres.

Je pense que le moyen le plus simple de le faire est de scinder le gros fichier de 20 Go en seize morceaux, puis d’parsingr chacun des morceaux ensemble, puis de les fusionner à nouveau. Le temps supplémentaire nécessaire au fractionnement et au réassemblage du fichier doit être minime par rapport au gain d’environ 16 fois impliqué dans l’parsing simultanée de ces seize morceaux.

En résumé, une façon de faire cela pourrait être:

  private List SplitFileIntoChunks(ssortingng baseFile) { // Split the file into chunks, and return a list of the filenames. } private void AnalyseChunk(ssortingng filename) { // Analyses the file and performs replacements, // perhaps writing to the same filename with a different // file extension } private void CreateOutputFileFromChunks(ssortingng outputFile, List splitFileNames) { // Combines the rewritten chunks created by AnalyseChunk back into // one large file, outputFile. } public void AnalyseFile(ssortingng inputFile, ssortingng outputFile) { List splitFileNames = SplitFileIntoChunks(inputFile); var tasks = new List(); foreach (ssortingng chunkName in splitFileNames) { var task = Task.Factory.StartNew(() => AnalyseChunk(chunkName)); tasks.Add(task); } Task.WaitAll(tasks.ToArray()); CreateOutputFileFromChunks(outputFile, splitFileNames); } 

Un petit nit: déplacez le calcul de la longueur du stream hors de la boucle, vous n’avez besoin que de l’obtenir une fois.

EDIT: incluez également l’idée de @Pavel Gatilov d’inverser la logique de la boucle interne et de rechercher chaque mot de la ligne dans la liste des 12 millions.

Plusieurs idées:

  1. Je pense qu’il sera plus efficace de scinder chaque ligne en mots et de regarder si chacun des mots apparaît dans votre liste de mots. 10 recherches dans un hashset sont meilleures que des millions de recherches dans une sous-chaîne. Si vous avez des mots-clés composites, créez des index appropriés: un qui contient tous les mots simples qui figurent dans les mots-clés réels et un autre qui contient tous les mots-clés réels.
  2. Peut-être que le chargement de chaînes dans SsortingngBuilder est préférable pour le remplacement.
  3. Mise à jour de la progression après, disons 10000 lignes traitées, pas après chacune d’elles.
  4. Traiter en arrière-plan. Cela ne le fera pas beaucoup plus vite, mais l’application sera responsable.
  5. Paralléliser le code, comme Jeremy l’a suggéré.

METTRE À JOUR

Voici un exemple de code illustrant l’idée d’index par mot:

 static void ReplaceWords() { ssortingng inputFileName = null; ssortingng outputFileName = null; // this dictionary maps each single word that can be found // in any keyphrase to a list of the keyphrases that contain it. IDictionary> singleWordMap = null; using (var source = new StreamReader(inputFileName)) { using (var target = new StreamWriter(outputFileName)) { ssortingng line; while ((line = source.ReadLine()) != null) { // first, we split each line into a single word - a unit of search var singleWords = SplitIntoWords(line); var result = new SsortingngBuilder(line); // for each single word in the line foreach (var singleWord in singleWords) { // check if the word exists in any keyphrase we should replace // and if so, get the list of the related original keyphrases IList interestingKeyPhrases; if (!singleWordMap.TryGetValue(singleWord, out interestingKeyPhrases)) continue; Debug.Assert(interestingKeyPhrases != null && interestingKeyPhrases.Count > 0); // then process each of the keyphrases foreach (var interestingKeyphrase in interestingKeyPhrases) { // and replace it in the processed line if it exists result.Replace(interestingKeyphrase, GetTargetValue(interestingKeyphrase)); } } // now, save the processed line target.WriteLine(result); } } } } private static ssortingng GetTargetValue(ssortingng interestingKeyword) { throw new NotImplementedException(); } static IEnumerable SplitIntoWords(ssortingng keyphrase) { throw new NotImplementedException(); } 

Le code montre les idées de base:

  1. Nous divisons les phrases clés et les lignes traitées en unités équivalentes qui peuvent être efficacement comparées: les mots.
  2. Nous stockons un dictionnaire qui, quel que soit le mot, nous donne rapidement des références à toutes les phrases clés contenant ce mot.
  3. Ensuite, nous appliquons votre logique d’origine. Cependant, nous ne le faisons pas pour toutes les phrases clés 12 millions de clés, mais plutôt pour un très petit sous-ensemble de phrases clés comportant une intersection d’au moins un mot avec la ligne traitée.

Je vous laisse le rest de la mise en œuvre.

Le code a cependant plusieurs problèmes:

  1. SplitIntoWords doit en fait normaliser les mots sous une forme canonique. Cela dépend de la logique requirejse. Dans le cas le plus simple, le fractionnement et la mise en minuscule des caractères d’espacement seront probablement acceptables. Mais il se peut que vous ayez besoin d’une correspondance morphologique, ce qui serait plus difficile (très proche des tâches de recherche en texte intégral).
  2. Par souci de rapidité, il sera probablement préférable que la méthode GetTargetValue soit appelée une fois pour chaque phrase clé avant le traitement de l’entrée.
  3. Si beaucoup de vos phrases clés ont des mots qui coïncident, vous aurez toujours une quantité significative de travail supplémentaire. Dans ce cas, vous devez conserver la position des mots-clés dans les phrases clés afin d’utiliser le calcul de la distance entre mots pour exclure les phrases clés non pertinentes lors du traitement d’une ligne de saisie.
  4. De plus, je ne suis pas sûr que SsortingngBuilder soit réellement plus rapide dans ce cas particulier. Vous devriez expérimenter SsortingngBuilder et ssortingng pour découvrir la vérité.
  5. C’est un échantillon après tout. Le design n’est pas très bon. J’envisagerais d’extraire des classes avec des interfaces cohérentes (par exemple, KeywordsIndex ).