2010-11-18 6 views
1

ce que j'ai est un fichier texte qui ressemble à ceci:C# combinant lignes dans le même fichier texte

apple|2 
turkey|4 
mango|11 
apple|3 
turkey|4 
mango|4 

J'ai besoin que les résultats soient:

apple|5 
turkey|8 
mango|15 

Des conseils ou des suggestions? Je sais qu'il y a des Math.Abs ​​et autres, je ne suis pas sûr de savoir comment combiner les lignes. Toute aide serait géniale.

+3

Est-ce lié aux devoirs? –

+0

@Steve, si ce n'est pas, c'est une application incroyablement étrange. –

+0

LOL, non ce n'est pas de devoirs le fichier a effectivement une tonne de choses, si je vois la méthode pour l'exemple ci-dessus je peux faire le reste du travail. –

Répondre

4
File.WriteAllLines(
    "test.txt", 
    File.ReadLines("test.txt") 
     .Select(line => line.Split('|')) 
     .Select(tokens => new 
     { 
      Value = int.Parse(tokens[1]), 
      Text = tokens[0] 
     }) 
     .GroupBy(li => li.Text) 
     .Select(g => string.Format("{0}|{1}", g.Key, g.Sum(l => l.Value))) 
     .ToArray() 
); 

ou si vous préférez:

File.WriteAllLines(
    "test.txt", 
    (from lineItem in 
     from line in File.ReadLines("test.txt") 
     let tokens = line.Split('|') 
     select new 
     { 
      Value = int.Parse(tokens[1]), 
      Text = tokens[0] 
     } 
    group lineItem by lineItem.Text into g 
    let sum = g.Sum(x => x.Value) 
    select string.Format("{0}|{1}", g.Key, sum)).ToArray() 
); 
+1

+1 pour la solution 1 ligne: D – McKay

+0

Vous avez dit qu'il y avait une bonne façon de le faire dans LINQ. –

2
// read the text file 
string[] wholeFile; 
var values = new Dictionary<string, int>() 
foreach (var line in lines) 
{ 
    var pieces = line.Split('|'); 
    var name = pieces[0]; 
    var value = int.Parse(pieces[1]); 
    if (!values.ContainsKey(name) 
    { 
     values[name] = 0; 
    } 

    values[name] = values[name] + value; 
} 
// spit it back out to a file. 

Je n'ai pas fait le fichier IO et je n'ai pas vérifié les erreurs, mais cela devrait vous aider à démarrer.

2

Créez un Dictionary<string, int>. Faites une boucle sur les lignes de votre fichier texte, divisez la ligne au "|". S'il n'y a pas d'entrée de dictionnaire pour la chaîne, créez-en une. Ajouter le nombre à la valeur de l'entrée du dictionnaire. Lorsque vous avez fini de parcourir le fichier texte, faites une boucle dans votre dictionnaire et affichez les résultats dans le format désiré.

3

Untested:

var lines = File.ReadLines(sourcePath); 
var totals = lines.Select(line => line.Split('|')) 
        .Select(line => new { 
         Item = line[0], 
         Count = Int32.Parse(line[1]) 
        }) 
        .GroupBy(x => x.Item) 
        .Select(g => new { 
         Item = g.Key, 
         Count = g.Sum(x => x.Count) 
        }); 

File.WriteAllLines(
    destinationPath, 
    totals.Select(total => String.Format("{0}|{1}", total.Item, total.Count) 
); 

Vous devez évidemment pour le rendre plus robuste, mais je suis sûr que cela a. compile et b. produit les résultats souhaités.

+2

pourquoi ne pas rouler tout dans une ligne de code? : D – McKay

+0

@McKay: Parce que cela devient alors un tas de code illisible. :-) – jason

+1

Hehe, je plaisantais. Mais en toute sincérité, je pense que Linq dans ce cas, il est plus difficile de rendre plus robuste. Une vérification d'erreur au milieu des instructions de linq rendrait les choses plus désordonnées. – McKay

1

Je suis sûr qu'il y a une bonne façon de le faire dans LINQ mais quelque chose comme ça fonctionnerait.

string[] lines = File.ReadAllLines("myfile"); 

Dictionary<string, int> totals = new Dictionary<string, int>(); 
foreach (string line in lines) 
{ 
    string[] fields = line.Split(new char[]{'|'}); 
    if (!totals.ContainsKey(fields[0])) 
    { 
    totals[fields[0]] = 0; 
    } 
    totals[fields[0]] += Convert.ToInt32(fields[1]); 
} 

foreach (KeyValuePair<string, int> total in totals) 
{ 
    Console.WriteLine("{0}|{1}", total.Key, total.Value); 
} 
0

Vous pouvez le faire en créant un dictionnaire et en itérant sur les lignes en ajoutant à la valeur si l'élément existe. Je n'ai pas visual studio avec moi en ce moment, si nue avec des erreurs de syntaxe potentielles dans les domaines suivants:

var linesInFile = File.ReadAllLines("c:\path\to\yourfile.txt"); 

var dict = new Dictionary<string, int>(); 

foreach(var line in linesInFile) 
{ 
    var parts = string.Split(new char[] {'|'}); 
    var item = parts[0]; 
    var amount = int.Parse(parts[1]); 
    if(dict.HasKey(item)){ 
     dict[item] += amount; 
    }else{ 
     dict.Add(item, amount); 
    } 
} 

foreach(var kvp in dict) 
{ 
    Console.WriteLine(string.Format("{0}: {1}", kvp.Key, kvp.Value)); 
} 
3

Les solutions LINQ sont élégantes , mais avec des fichiers texte, je préfère une petite vérification d'erreur.

string filePath = @"c:\temp\test.txt"; 
string line; 

Dictionary<string, int> dictAcculumator = new Dictionary<string,int>(); 

if (File.Exists(filePath)) 
{ 
    using (StreamReader file = new StreamReader(filePath)){ 
     try 
     { 
      while ((line = file.ReadLine()) != null) 
      { 
       if (line.Contains('|')){ 
        string[] items = line.Split('|'); 
        int count; 
        string item = items[1]; 
        if (int.TryParse(items[1], out count)){ 
         if (dictAcculumator.ContainsKey(item)){ 
          dictAcculumator[items[0]] += count; 
         } 
         else{ 
          dictAcculumator.Add(items[0], count); 
         } 
        } 
       } 
      } 
     } 
     finally 
     { 
      if (file != null) 
       file.Close(); 
     } 
    } 
} 

StringBuilder sb = new StringBuilder(); 
foreach (string key in dictAcculumator.Keys) 
{ 
    //alternatively write to text file 
    sb.AppendFormat("{0}|{1}\r\n", key, dictAcculumator[key].ToString()); 
} 

Console.Write(sb.ToString()); 
+0

En fait, tout code de la vie réelle qui fonctionne avec une entrée externe ** doit ** vérifier son exactitude, donc votre chemin est le seul moyen correct. (Votre solution manque cependant la réaction sur les erreurs, que je laisserais comme exercice pour l'OP.) – Vlad

+1

BTW, peut-être que vous n'avez pas besoin de 'try' /' finally', car autodisposing du 'file' à la fin du bloc 'using' le fermera quand même. – Vlad

+0

@vlad. Bonne observation sur la redondance de l'essai/enfin. – Laramie

0

Ce qui suit est testé et fonctionne:

using System; 
using System.Collections.Generic; 
using System.IO; 
namespace ConsoleApplication1 
{ 
class Program 
{ 
    static void Main(string[] args) 
    { 
     List<Data> items = new List<Data>(); 
     using (StreamReader reader = new StreamReader("C:\\chedberg\\combine.txt")) 
     { 
      string line; 
      string[] split; 
      string name; 
      int count; 
      while (!reader.EndOfStream) 
      { 
       line = reader.ReadLine(); 
       if (!string.IsNullOrEmpty(line)) 
       { 
        split = line.Split('|'); 
        name = split[0]; 
        if(Int32.TryParse(split[1],out count)) 
         items.Add(new Data() { Name = name, Count = count }); 
       } 
      } 

      List<Data> combinedItems = new List<Data>(); 
      items.ForEach(item => 
           { 
            if (!combinedItems.Exists(combinedItem => combinedItem.Name == item.Name)) 
             combinedItems.Add(new Data(){Name = item.Name, Count = item.Count}); 
            else 
            { 
             combinedItems.Find(combinedItem => combinedItem.Name == item.Name).Count += item.Count; 
            } 
           }); 
      using (StreamWriter writer = new StreamWriter("C:\\chedberg\\combine_out.txt")) 
      { 
       combinedItems.ForEach(item => writer.WriteLine(String.Format("{0}|{1}",item.Name,item.Count))); 
       writer.Flush(); 
      } 
     } 
    } 

    public class Data 
    { 
     public string Name { get; set; } 
     public int Count { get; set; } 
    } 
} 

}

Questions connexes