Быстрый поиск строки одного текстового файла в другом текстовом файле - PullRequest
1 голос
/ 29 февраля 2012

Есть ли более быстрый способ поиска в каждой строке одного текстового файла для вхождения в другой текстовый файл, чем переходить строка за строкой в ​​обоих файлах?

У меня есть два текстовых файла - у одного ~ 2500 строк (назовем его TxtA), у другого ~ 86000 строк (TxtB).Я хочу найти TxtB для каждой строки в TxtA и вернуть строку в TxtB для каждого найденного соответствия.

В настоящее время у меня есть такая настройка: для каждой строки в TxtA, поиск TxtB строка за строкой для соответствия.Однако это занимает очень много времени для обработки.Похоже, что на поиск всех совпадений уйдет 1-3 часа.

Вот мой код ...

private static void getGUIDAndType()
    {

        try
        {

            Console.WriteLine("Begin.");
            System.Threading.Thread.Sleep(4000);

            String dbFilePath = @"C:\WindowsApps\CRM\crm_interface\data\";
            StreamReader dbsr = new StreamReader(dbFilePath + "newdbcontents.txt");
            List<string> dblines = new List<string>();

            String newDataPath = @"C:\WindowsApps\CRM\crm_interface\data\";
            StreamReader nsr = new StreamReader(newDataPath + "HolidayList1.txt");
            List<string> new1 = new List<string>();

            string dbline;
            string newline;

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

            while ((newline = nsr.ReadLine()) != null)
            {
                //Reset
                dbsr.BaseStream.Position = 0;
                dbsr.DiscardBufferedData();

                while ((dbline = dbsr.ReadLine()) != null)
                {
                    newline = newline.Trim();
                    if (dbline.IndexOf(newline) != -1)
                    {//if found... get all info for now
                        Console.WriteLine("FOUND: " + newline);
                        System.Threading.Thread.Sleep(1000);
                        new1.Add(newline);
                        break;
                    }
                    else
                    {//the first line of db does not contain this line... 
                        //go to next dbline. 
                        Console.WriteLine("Lines do not match - continuing");
                        continue;
                    }
                }
                Console.WriteLine("Going to next new Line");
                System.Threading.Thread.Sleep(1000);
                //continue;
            }

            nsr.Close();

            Console.WriteLine("Writing to dbc3.txt");
            System.IO.File.WriteAllLines(@"C:\WindowsApps\CRM\crm_interface\data\dbc3.txt", results.ToArray());
            Console.WriteLine("Finished. Press ENTER to continue.");

            Console.WriteLine("End.");
            Console.ReadLine();
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error:  " + ex);
            Console.ReadLine();
        }
    }

Пожалуйста, дайте мне знать, если есть более быстрый путь.Желательно, чтобы это заняло 5-10 минут ... Я слышал об индексации, но не нашел много об этом для текстовых файлов.Я протестировал регулярные выражения, и это не быстрее, чем indexof.Содержимое не будет работать, потому что линии никогда не будут точно такими же.

Спасибо.

Ответы [ 3 ]

3 голосов
/ 29 февраля 2012

РЕДАКТИРОВАТЬ: обратите внимание, что я предполагаю, что разумно прочитать по крайней мере один файл в память. Возможно, вы захотите поменять местами приведенные ниже запросы, чтобы избежать загрузки «большого» файла в память, но даже 86 000 строк с (скажем) 1 КБ на строку будут меньше 2 ГБ памяти - что относительно мало для того, чтобы сделать что-то существенное.

Вы читаете "внутренний" файл каждый раз. В этом нет необходимости. Загрузите оба файла в память и перейдите оттуда. Черт возьми, для точных совпадений вы можете легко выполнить все в LINQ:

var query = from line1 in File.ReadLines("newDataPath + "HolidayList1.txt")
            join line2 in File.ReadLines(dbFilePath + "newdbcontents.txt")
            on line1 equals line2
            select line1;

var commonLines = query.ToList();

Но для не-соединений это все еще просто; просто сначала полностью прочитайте один файл (явно), а затем передайте другой:

// Eagerly read the "inner" file
var lines2 = File.ReadAllLines(dbFilePath + "newdbcontents.txt");
var query = from line1 in File.ReadLines("newDataPath + "HolidayList1.txt")
            from line2 in lines2
            where line2.Contains(line1)
            select line1;

var commonLines = query.ToList();

Здесь нет ничего умного - это просто очень простой способ написания кода для чтения всех строк в одном файле, а затем итерации по строкам в другом файле и проверки каждой строки по всем строкам в первом файле. Но даже без чего-либо умного, я сильно подозреваю, что это будет достаточно хорошо для вас. Сконцентрируйтесь на простоте, устраните ненужные операции ввода-вывода и посмотрите, достаточно ли это хорошо, прежде чем пытаться делать что-то более изумительное.

Обратите внимание, что в исходном коде вы должны использовать операторы using для переменных StreamReader, чтобы обеспечить их правильное расположение. Использование приведенного выше кода упрощает даже то, что это не нужно ...

2 голосов
/ 29 февраля 2012

Возможно, есть более быстрый путь, но этот подход LINQ должен быть быстрее, чем 3 часа, и его лучше читать и поддерживать:

var f1Lines    = File.ReadAllLines(f1Path);
var f2LineInf1 = File.ReadLines(f2Path)
                .Where( line => f1Lines.Contains(line))
                .Select(line => line).ToList();

Редактировать : проверено и требуетсяменее 1 секунды на 400000 строк в файле2 и 17000 строк в файле1.Я могу использовать File.ReadLines для большого файла, который не загружает все в память сразу.Для меньшего файла мне нужно использовать File.ReadAllLines, поскольку Contains нужен полный список строк файла 1.

Если вы хотите записать результат в третий файл:

File.WriteAllLines(logPath, f2LineInf1);
0 голосов
/ 29 февраля 2012

Быстро и грязно, потому что я должен идти ... Если вы можете сделать это в памяти, попробуйте поработать с этим фрагментом:

    //string[] searchIn = File.ReadAllLines("File1.txt");
    //string[] searchFor = File.ReadAllLines("File2.txt");

    string[] searchIn = new string[] {"A","AB","ABC","ABCD", null, "", "    "};
    string[] searchFor = new string[] {"A","BC","BCD", null, "", "   "};

    matchDictionary;

    foreach(string item in file2Content)
    {
        string[] matchingItems = Array.FindAll(searchIn, x => (x == item) || (!string.IsNullOrEmpty(x) && !string.IsNullOrEmpty(item) ? (x.Contains(item) || item.Contains(x)) : false));
    }
...