Слияние содержимого строки текстовых файлов в c # - PullRequest
0 голосов
/ 10 января 2012

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

Файл 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());
        }


        }
    }

Ответы [ 5 ]

3 голосов
/ 10 января 2012

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

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()));
2 голосов
/ 10 января 2012

Я бы порекомендовал использовать 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 голос
/ 10 января 2012

Несколько хороших решений здесь.Вот еще один простой:

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

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 голосов
/ 10 января 2012

Структурируя это как запрос 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 использует аналогичный подход к словарю за кадром, и вы также получите преимущество от корреляции, выполняемой в нескольких потоках.

0 голосов
/ 10 января 2012

Если кто-то интересуется версией 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...