Цикл по File1.txt и File2.txt действительно медленный.Оба файла имеют размер 280 МБ - PullRequest
1 голос
/ 15 марта 2012

У меня есть 2 больших текстовых файла с 400 000 строк текста в каждом файле.В File2.txt мне нужно найти строку, содержащую userId из текущей строки в File1.txt.Найдя правильную строку в File2.txt, я делаю некоторые вычисления и записываю строку в новый текстовый файл.

Код, который я создал для этого, работает очень медленно.Я пытался переписать его различными способами, но он всегда пыхтит и никогда не заканчивается.Как я могу сделать это быстро?

private void btnExecute_Click(object sender, EventArgs e) {        
    string line1 = "";
    string line2 = "";

    //the new text file we are creating. Located in IVR_Text_Update\bin\Debug
    StreamWriter sw = new StreamWriter("NewFile.txt");

    //the new text file which contains the registrants which need removing
    StreamWriter sw_removeRegs = new StreamWriter("RemoveRegistrants.txt");

    //address has changed so we write the line to the address file
    StreamWriter sw_addressChange = new StreamWriter("AddressChanged.txt");

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

    using (StreamReader sr = new StreamReader(openFileDialog2.FileName)) {
        string line;
        while ((line = sr.ReadLine()) != null) {
            lines_secondFile.Add(line);
        }
    }

    //loop through the frozen file one line at a time
    while ((line1 = sr1.ReadLine()) != null) {
        //get the line from the update file, assign it to line2
        //function accepts (userId, List)
        line2 = getLine(line1.Substring(3, 8), lines_secondFile);

        //if line2 is null then userId was not found therefore we write
        //the line to Remove Registrants file
        if (line2 == null) {
            sw_removeRegs.Write(line1 + Environment.NewLine);
        }

        //address between the two lines was found to be different so we still write
        //them to the new text file but don't update codes
        else if (line1.Substring(93, 53) != line2.Substring(93, 53)) {
            sw_addressChange.Write(line1 + Environment.NewLine);
            sw.Write(line1 + Environment.NewLine);
        }

        //test for null then write the new line in our new text file
        else if ((line1 != null) && (line2 != null)) {
            sw.Write(line1.Substring(0, 608) +                    
                     line2.Substring(608, 9) +
                     line2.Substring(617, 9) +
                     line2.Substring(626, 9) +
                     line2.Substring(635, 9) +
                     line2.Substring(644, 9) +
                     line2.Substring(653, 9) +
                     line2.Substring(662, 9) +
                     line2.Substring(671, 9) +
                     line2.Substring(680, 9) +

                     line1.Substring(680, 19) + 
                     Environment.NewLine);
        }
    }

    textBox1.Text = "Finished.";
    sr1.Close();
    sw.Close();
    sw_removeRegs.Close();
    sw_addressChange.Close();
}

//returns the line from the update file which has the corresponding userId
//from the frozen file
string getLine(string userId, List<string> lines_secondFile) {

    foreach (string currentLine in lines_secondFile) {
        if (currentLine.Contains(userId)) {
            return currentLine;
        }
    }

    return null;
}

Ответы [ 3 ]

4 голосов
/ 15 марта 2012

Не принимая во внимание скорость доступа к диску, ваш текущий алгоритм равен O(n^2) - для каждой строки в первом файле вы просматриваете список , чтобы найти идентификатор пользователя - вы можете использовать некоторое кэшированиечтобы избежать многократного поиска одного и того же идентификатора пользователя, я предполагаю, что у вас менее 400 тыс. пользователей, поэтому в большинстве случаев дубликаты должны быть:

private Dictionary<string, string> userMap = new Dictionary<string, string>();
string getLine(string userId, List<string> lines_secondFile) 
{
    if(userMap.ContainsKey(userId))
        return userMap[userId];
    else
    {
      foreach (string currentLine in lines_secondFile) 
      {
        if (currentLine.Contains(userId)) 
        {
            userMap.Add(userId, currentLine);
            return currentLine;
        }
    }
    return null;
}
4 голосов
/ 15 марта 2012

Вместо того, чтобы читать его построчно, попробуйте прочитать все файлы одновременно. Это намного быстрее, чем делать много запросов на чтение в файл. Это потому, что доступ к файлам намного медленнее, чем доступ к памяти. Попробуйте File.ReadAllText

С учетом вышесказанного, вы должны попробовать профилировать код, чтобы точно определить узкое место в коде.

0 голосов
/ 15 марта 2012

Если у вас есть ресурсы, вы можете поместить весь файл (ы) в память. Тогда скорость должна быть увеличена. До C # 4 вам приходилось использовать WIN32 API для отображения файла в памяти, но C # 4 добавил System.IO.MemoryMappedFiles.MemoryMappedFile.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...