2012-01-10 5 views
0

У меня есть два огромных текстовых файла, формат которых приведен ниже.Слияние содержимого строк в текстовых файлах в C#

Файл 1:

ID1,20
ID2,20
ID3,30

Файл 2:

ID3,75
ID1,84
ID2 , 70

Оба этих файла содержат более 200 000 строк. Мне нужно прочитать как файлы и создать третий файл в этом формате:

Файл 3:

ID1,20,84
ID2,20,70
ID3,30,75

ID может быть любой строкой, вводимой пользователем. Третий файл должен быть создан путем сопоставления идентификатора в строках файла 1 с идентификатором в строках файла 2. Я написал код, но для генерации файла 3 требуется много времени. Задача под рукой заключается в параллелизации, поэтому я хотите, чтобы код спас меня как можно больше времени. Пожалуйста, предложите более быстрый и эффективный способ решения этой проблемы.

(Вот код, который я использовал)

public void positionCure(string afile,string bfile,string dfile) 
    { 
     string alphaFilePath = afile; 

     List<string> alphaFileContent = new List<string>(); 

     using (FileStream fs = new FileStream(alphaFilePath, FileMode.Open)) 
     using(StreamReader rdr = new StreamReader(fs)) 
     { 
      while(!rdr.EndOfStream) 
      { 
       alphaFileContent.Add(rdr.ReadLine()); 
      } 
     } 

     string betaFilePath = bfile; 

     StringBuilder sb = new StringBuilder(); 


     using (FileStream fs = new FileStream(betaFilePath, FileMode.Open)) 
     using (StreamReader rdr = new StreamReader(fs)) 
     { 

      while(! rdr.EndOfStream) 
      { 
       string[] betaFileLine = rdr.ReadLine().Split(Convert.ToChar(",")); 

       foreach (string alphaline in alphaFileContent) 
       { 
         string[] alphaFileLine = alphaline.Split(Convert.ToChar(",")); 

         if (alphaFileLine[0].Equals(betaFileLine[0].ToString())) 
         { 
          sb.AppendLine(String.Format("{0}, {1}, {2}", betaFileLine[0], betaFileLine[1], alphaline.Substring(alphaline.IndexOf(Convert.ToChar(","))+1))); 
         } 

       } 
      } 


      } 

     using (FileStream fs = new FileStream(dfile, FileMode.Create)) 
     using (StreamWriter writer = new StreamWriter(fs)) 
     { 
      writer.Write(sb.ToString()); 
     } 


     } 
    } 
+0

Ваши строки в файле1 отсортированы по идентификатору? –

+0

Нет, они не являются. – Sabi

+0

Код RedFilter работал блестяще ... он быстрый и эффективный. – Sabi

ответ

3

Я хотел бы сделать что-то вроде:

string[] files = new string[] { @"c:\temp\file1.txt", @"c:\temp\file2.txt" }; 
var hash = new Dictionary<string, Dictionary<string, bool>>(); 
foreach (var file in files) 
{ 
    string[] fileContents = File.ReadAllLines(file); 
    foreach (string line in fileContents) 
    { 
     string[] a = line.Split(','); 
     if (!hash.Keys.Contains(a[0])) 
      hash[a[0]] = new Dictionary<string, bool>(); 
     hash[a[0]][a[1]] = true; 
    } 
} 
foreach (var key in hash.Keys) 
    Console.WriteLine(key + "," + string.Join(",", hash[key].Keys.ToArray())); 
+0

Большое спасибо за предложения. Он работал довольно быстро. Никогда не думал об использовании словаря. Еще раз спасибо. – Sabi

+0

Я проверил некоторые тороидальные тесты, и кажется, что некоторые из строк в результате не имеют обоих чисел. Они просто содержат ID и одно число, а не другое ... Было бы здорово, если бы я мог избавиться от таких строк, не имея необходимости снова читать файл результатов ... идеи .. спасибо ... – Sabi

+0

http: // www.fileswap.com/dl/wcxhXGzjyT/file1.txt.html http://www.fileswap.com/dl/dfjr8PICue/file2.txt.html http://www.fileswap.com/dl/ZZL3xcaCMw/file3. txt.html – Sabi

2

Я бы рекомендовал использовать Dictionary:

var combined = new Dictionary<string, string>(); 

// loop through each of the rows in the first file, and the second file, 
while (going through each of the files) 
{  
    string id; 
    string number; 
    //and after splitting the line into the two variables: 
    if (combined.ContainsKey(id)) 
    { 
     combined[id] = combined[id] + "," + number; // or do something else, this is just an example 
     // changing it from a Dictionary<string, string> to a Dictionary<string, List<string>> might be more performant, especially if you have a bunch of files you want to do this do, but it also might not be necessary. 
    } 
    else 
    { 
     combined[id] = number; 
    } 
} 

// you can then run through the file and output it. 

foreach (var pair in combined) 
{ 
    file.Write(pair.Key); 
    file.Write(","); 
    file.Writeline(pair.Value); 
} 
1

Пара хороших решений здесь. Вот еще один простой один:

захватить содержимое в словарь:

private Dictionary<string, string> LoadFile(string path) 
     { 
      string line; 
      Dictionary<string, string> vals = new Dictionary<string, string>(); 
      using (StreamReader file = new StreamReader(path)) 
      { 
       while ((line = file.ReadLine()) != null) 
       { 
        string[] parts = line.Split(','); 
        vals.Add(parts[0], parts[1]); 
       } 
      } 
      return vals; 
     } 

Тогда в вашей программе, загрузите каждый файл и объединить

Dictionary<string, string> fileAValues = LoadFile(@"C:\Temp\FileA.txt"); 
Dictionary<string, string> fileBValues = LoadFile(@"C:\Temp\FileB.txt"); 

      using (StreamWriter sr = new StreamWriter(@"C:\Temp\FileC.txt")) 
      { 
       foreach (string key in fileAValues.Keys) 
       { 
        if (fileBValues.ContainsKey(key)) 
        { 
         string combined = key + "," + 

          String.Join(",", fileAValues[key].ToString(), 
         fileBValues[key].ToString()); 
         sr.WriteLine(combined); 
        } 
       } 
      } 
0

Если кто-то заинтересован в VB.NET (я всегда слишком медленный с C#), так что для полноты. Я также использую словарь.

Dim dic1 As New Dictionary(Of String, List(Of String)) 
Dim file1 = System.IO.File.ReadAllLines("C:\Temp\File1.txt") 
For Each l In file1 
    Dim cols = l.Split(","c) 
    If cols.Any Then 
     Dim key = cols(0) 
     If Not dic1.ContainsKey(key) Then 
      Dim values = (From col In cols Skip (1)).ToList 
      dic1.Add(key, values) 
     End If 
    End If 
Next 

Dim file2 = System.IO.File.ReadAllLines("C:\Temp\File2.txt") 
For Each l In file2 
    Dim cols = l.Split(","c) 
    If cols.Any Then 
     Dim key = cols(0) 
     If dic1.ContainsKey(key) Then 
      ' append ' 
      Dim values = (From col In cols Skip (1)).ToList 
      dic1(key).AddRange(values) 
     Else 
      Dim values = (From col In cols Skip (1)).ToList 
      dic1.Add(key, values) 
     End If 
    End If 
Next 

Using writer = New System.IO.StreamWriter("C:\Temp\File3.txt") 
    For Each entry In dic1 
     writer.WriteLine(String.Format("{0},{1}", entry.Key, String.Join(",", entry.Value))) 
    Next 
End Using 
0

Структурируя это как запрос LINQ, вы можете воспользоваться методом AsParallel, чтобы выполнить его на несколько потоков. Это значительно улучшит производительность вашего алгоритма, учитывая, что у вас так много данных.

Во-первых, нам нужно получить более структурированную структуру.Мы можем моделировать значения, которые вы используете:

public class InputLine 
{ 
    public string Id { get; set; } 

    public string Value { get; set; } 
} 

public class OutputLine 
{ 
    public string Id { get; set; } 

    public string Value1 { get; set; } 

    public string Value2 { get; set; } 
} 

Мы также можем определить производителей и потребителей этих значений:

public class InputFile 
{ 
    private readonly string _path; 

    public InputFile(string path) 
    { 
     _path = path; 
    } 

    public IEnumerable<InputLine> GetLines() 
    { 
     return 
      from line in File.ReadAllLines(_path) 
      let parts = line.Split(',') 
      select new InputLine { Id = parts[0], Value = parts[1] }; 
    } 
} 

public class OutputFile 
{ 
    private readonly string _path; 

    public OutputFile(string path) 
    { 
     _path = path; 
    } 

    public void WriteLines(IEnumerable<OutputLine> lines) 
    { 
     File.WriteAllLines(_path, lines.Select(line => String.Join(",", line.Id, line.Value1, line.Value2))); 
    } 
} 

Теперь у нас есть ингредиенты, чтобы написать запрос, который связывает все вместе. Есть два ключевых аспекта этого запроса:

  1. Используйте метод .AsParallel() расширения для его выполнения в параллельном
  2. Используйте оператор join соотнести ключи между двумя входными файлами

Нам просто нужно два входных файлов и выходной файл:

public void WriteResults(InputFile file1, InputFile file2, OutputFile resultFile) 
{ 
    var resultLines = 
     from file1Line in file1.GetLines().AsParallel() 
     join file2Line in file2.GetLines() on file1Line.Id equals file2Line.Id 
     select new OutputLine 
     { 
      Id = file1Line.Id, 
      Value1 = file1Line.Value, 
      Value2 = file2Line.Value 
     }; 

    resultFile.WriteLines(resultLines); 
} 

оператор join использует подобный подход к г ictionary за кадром, и вы также получите выгоду от корреляции, выполняемой по нескольким потокам.

Смежные вопросы