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:
SsortingngBuilder
est préférable pour le remplacement. 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:
Je vous laisse le rest de la mise en œuvre.
Le code a cependant plusieurs problèmes:
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). GetTargetValue
soit appelée une fois pour chaque phrase clé avant le traitement de l’entrée. SsortingngBuilder
soit réellement plus rapide dans ce cas particulier. Vous devriez expérimenter SsortingngBuilder
et ssortingng
pour découvrir la vérité. KeywordsIndex
).