Шаблон для работы с локальными текстовыми файлами

  • Автор темы Автор темы axaptus
  • Дата начала Дата начала

axaptus

Активный пользователь
Регистрация
03.06.2018
Сообщения
101
Реакции
42
Баллы
28
Если два .txt файла, в name.txt содержатся имена:
Алексей
Сергей Владимирович
Павел

Во mail.txt содержатся имена и емэйлы, которые разделены табуляцией:
Алексей alex@bk.ru
Сергей Владимирович serg@mail.ru
Алексей alex2@list.ru

Шаблон должен проанализировать name.txt и mail.txt и выдать output.txt, представляющий собой mail.txt из которого удалены все строки с именами, которых нет в name.txt.
Как видно из примера, одно имя в mail.txt может содержаться более одного раза - в этом случае надо оставить все строки с этим именем. Шаблон должен работать с большими файлами (name.txt - 2 млн. строк; mail.txt - 15 млн. строк) и обрабатывать их за адекватное время.
Бюджет 1500 рублей.
@drdpython
 
с такими большими файлами работать - потребуется много оперативки.
проще в БД запихнуть.
и в БД вся задача делается двумя командами :-)
 
с такими большими файлами работать - потребуется много оперативки.
проще в БД запихнуть.
и в БД вся задача делается двумя командами :-)
Согласен. Если БД - это сложно - есть куча софта для дорвейщиков, который умеет такие выборки делать.

Вопрос лишь в интерпретации понятия "адекватное время".
 
Подскажите, почему моя конструкция с await Task.WhenAll(tasks) не срабатывает как я ожидаю.
Хотел следующего:
  • Разбить mail.txt на несколько частей.
  • Идти по списку имён name.txt в цикле.
  • Внутри него запускать несколько потоков (по потоку на каждую часть mail.txt) и записывать найденные строки в список output.

Но после этой операции список output пустой.
И строка
Console.WriteLine("Пройдено output: " + i);
вызывается только один раз, но должна на каждой итерации верхнего цикла.
 
Последнее редактирование:
Подскажите, почему моя конструкция с await Task.WhenAll(tasks) не срабатывает как я ожидаю.
Хотел следующего:
  • Разбить mail.txt на несколько частей.
  • Идти по списку имён name.txt в цикле.
  • Внутри него запускать несколько потоков (по потоку на каждую часть mail.txt) и записывать найденные строки в список output.

Но после этой операции список output пустой.
И строка
Console.WriteLine("Пройдено output: " + i);
вызывается только один раз, но должна на каждой итерации верхнего цикла.
потому что у тебя в коде создается таска, добавляется в список задач и тут же выполняется с очисткой всех задач. и уходит на добавление следующей таски.
сначала добавь все таски, потом уже выполняй их.
 
  • Спасибо
Реакции: kot2566
в общем не разобрался как там код сохранить, вот немного переделал, вроде похоже на правду. только в лог не все выводит, наверно ограничение той платформы.

C#:
Развернуть Свернуть Копировать
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Diagnostics;
using System.Threading.Tasks;

namespace del_dublicate_mails
{
    class Program
    {
        private static Random random = new Random();
        private static Stopwatch stopwatch = new Stopwatch();
        private static int generateNamesCount = 20000;
        private static int threads = 20;

        static void Main(string[] args)
        {

            generateNames();
            generateMails();

            filterMails();
        }


        static async void filterMails()
        {
            stopwatch.Start();
            var names = new List<string>(File.ReadAllLines("name1.txt"));
            var mails = new List<string>(File.ReadAllLines("mail1.txt"));
            var output = new List<string>();

            Console.WriteLine("Количество имён: " + names.Count);
            Console.WriteLine("Количество mail: " + mails.Count);

            names = names.Distinct().ToList();
            mails = mails.Distinct().ToList();

            Console.WriteLine("Количество имён после очистки: " + names.Count);
            Console.WriteLine("Количество mail после очистки: " + mails.Count);

            // Вводим дополнительный список строк без email
            var onlymail_names = new List<string>();
            for (int i=0;i<mails.Count;i++)
            {
               int index= mails[i].LastIndexOf(' ');
                string s = mails[i].Substring(0, index);
                onlymail_names.Add(s);
            }

            // Разбиваем список на несколько частей
            int unit = mails.Count / threads;
            List<Task> tasks = new List<Task>();

            // идём по списку имён
            Console.WriteLine($"names.Count = {names.Count}");
            for (int i = 0; i < names.Count; i++)
            {
                Console.WriteLine($"запуск задач для i = {i}");
                for (int j = 0; j < mails.Count; j += unit)
                {
                    int ii = i;
                    int jj = j;

                    // Пытаемся параллельно пройти наши разбитые части и сравнить Имя
                
                    tasks.Add(Task.Run(() =>
                    {
                        //Console.WriteLine($"jj={jj} , unit={unit}");
                        var ttt=jj;
                        var ttt1=jj;
                    
                        for (int k = jj; k < jj + unit; k++)
                        {
                            ttt++;
                            if (onlymail_names[k] == names[ii])
                            {
                                output.Add(mails[k]);
                            }
                        }                       
                        Console.WriteLine($"Пройдено onlymail_names:  c {ttt1} по {ttt} , для {i}");

                    }));
                }
            
                await Task.WhenAll(tasks);
                tasks.Clear();

            }

      
            output = output.Distinct().ToList();
            Console.WriteLine("Результат output: " + output.Count);

            File.Delete("output.txt");
            File.AppendAllLines("output.txt", output);

            stopwatch.Stop();
            Console.WriteLine("filterMails time: " + stopwatch.ElapsedMilliseconds);

        }


        static void generateMails()
        {
            stopwatch.Start();
            List<string> list = new List<string>();
            StreamReader sr = new StreamReader("name1.txt");
            StreamWriter sw = new StreamWriter("mail1.txt");

            string line = "";

            while ((line = sr.ReadLine()) != null)
            {
                line += " " + RandomString(5) + "@mail.ru";
                sw.WriteLine(line);
            }

            sr.Close();
            sw.Close();
            stopwatch.Stop();
            Console.WriteLine("generateMails time: " + stopwatch.ElapsedMilliseconds);
        }
    

        static void generateNames()
        {
            stopwatch.Start();
            List<string> list = new List<string>();
            StreamWriter sw = new StreamWriter("name1.txt");

            int k = generateNamesCount;

            while(k>0)
            {
                sw.WriteLine(RandomString(5));
                k--;
            }

            sw.Close();
            stopwatch.Stop();
            Console.WriteLine("generateNames time: " + stopwatch.ElapsedMilliseconds);
        }


        public static string RandomString(int length)
        {
            const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            return new string(Enumerable.Repeat(chars, length)
              .Select(s => s[random.Next(s.Length)]).ToArray());
        }
    }
}
 
  • Спасибо
Реакции: kot2566
потому что у тебя в коде создается таска, добавляется в список задач и тут же выполняется с очисткой всех задач. и уходит на добавление следующей таски.
сначала добавь все таски, потом уже выполняй их.
не понимаю. А разве не именно это и происходит? Очистка и выполнение происходит после заполнения списка задач.

В подцикле for (int j = 0; j < mails.Count; j += unit)for (int j = 0; j < mails.Count; j += unit) таски создаются, запускаются и там же добавляются в список Тасков.
Затем после этого подцикла ждёт завершения всех Тасков await Task.WhenAll(tasks) и потом только очистка списка tasks.Clear()
 
не понимаю. А разве не именно это и происходит? Очистка и выполнение происходит после заполнения списка задач.

В подцикле for (int j = 0; j < mails.Count; j += unit)for (int j = 0; j < mails.Count; j += unit) таски создаются, запускаются и там же добавляются в список Тасков.
Затем после этого подцикла ждёт завершения всех Тасков await Task.WhenAll(tasks) и потом только очистка списка tasks.Clear()
ну возможно я не сразу уловил суть кода. возможно причина была в другом. ну пример гляньте. там вроде срабатывает как задумано.
 
в общем не разобрался как там код сохранить, вот немного переделал, вроде похоже на правду. только в лог не все выводит, наверно ограничение той платформы.

C#:
Развернуть Свернуть Копировать
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Diagnostics;
using System.Threading.Tasks;

namespace del_dublicate_mails
{
    class Program
    {
        private static Random random = new Random();
        private static Stopwatch stopwatch = new Stopwatch();
        private static int generateNamesCount = 20000;
        private static int threads = 20;

        static void Main(string[] args)
        {

            generateNames();
            generateMails();

            filterMails();
        }


        static async void filterMails()
        {
            stopwatch.Start();
            var names = new List<string>(File.ReadAllLines("name1.txt"));
            var mails = new List<string>(File.ReadAllLines("mail1.txt"));
            var output = new List<string>();

            Console.WriteLine("Количество имён: " + names.Count);
            Console.WriteLine("Количество mail: " + mails.Count);

            names = names.Distinct().ToList();
            mails = mails.Distinct().ToList();

            Console.WriteLine("Количество имён после очистки: " + names.Count);
            Console.WriteLine("Количество mail после очистки: " + mails.Count);

            // Вводим дополнительный список строк без email
            var onlymail_names = new List<string>();
            for (int i=0;i<mails.Count;i++)
            {
               int index= mails[i].LastIndexOf(' ');
                string s = mails[i].Substring(0, index);
                onlymail_names.Add(s);
            }

            // Разбиваем список на несколько частей
            int unit = mails.Count / threads;
            List<Task> tasks = new List<Task>();

            // идём по списку имён
            Console.WriteLine($"names.Count = {names.Count}");
            for (int i = 0; i < names.Count; i++)
            {
                Console.WriteLine($"запуск задач для i = {i}");
                for (int j = 0; j < mails.Count; j += unit)
                {
                    int ii = i;
                    int jj = j;

                    // Пытаемся параллельно пройти наши разбитые части и сравнить Имя
              
                    tasks.Add(Task.Run(() =>
                    {
                        //Console.WriteLine($"jj={jj} , unit={unit}");
                        var ttt=jj;
                        var ttt1=jj;
                  
                        for (int k = jj; k < jj + unit; k++)
                        {
                            ttt++;
                            if (onlymail_names[k] == names[ii])
                            {
                                output.Add(mails[k]);
                            }
                        }                     
                        Console.WriteLine($"Пройдено onlymail_names:  c {ttt1} по {ttt} , для {i}");

                    }));
                }
          
                await Task.WhenAll(tasks);
                tasks.Clear();

            }

    
            output = output.Distinct().ToList();
            Console.WriteLine("Результат output: " + output.Count);

            File.Delete("output.txt");
            File.AppendAllLines("output.txt", output);

            stopwatch.Stop();
            Console.WriteLine("filterMails time: " + stopwatch.ElapsedMilliseconds);

        }


        static void generateMails()
        {
            stopwatch.Start();
            List<string> list = new List<string>();
            StreamReader sr = new StreamReader("name1.txt");
            StreamWriter sw = new StreamWriter("mail1.txt");

            string line = "";

            while ((line = sr.ReadLine()) != null)
            {
                line += " " + RandomString(5) + "@mail.ru";
                sw.WriteLine(line);
            }

            sr.Close();
            sw.Close();
            stopwatch.Stop();
            Console.WriteLine("generateMails time: " + stopwatch.ElapsedMilliseconds);
        }
  

        static void generateNames()
        {
            stopwatch.Start();
            List<string> list = new List<string>();
            StreamWriter sw = new StreamWriter("name1.txt");

            int k = generateNamesCount;

            while(k>0)
            {
                sw.WriteLine(RandomString(5));
                k--;
            }

            sw.Close();
            stopwatch.Stop();
            Console.WriteLine("generateNames time: " + stopwatch.ElapsedMilliseconds);
        }


        public static string RandomString(int length)
        {
            const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            return new string(Enumerable.Repeat(chars, length)
              .Select(s => s[random.Next(s.Length)]).ToArray());
        }
    }
}
Да, посмотрел, спасибо. Но, всё равно, не так отрабатывается.
Выражение Console.WriteLine($"запуск задач для i = {i}"); срабатывает только несколько раз, а не names.Count Раз.
Вызов функцииawait Task.WhenAll(tasks); всё "ломает", точнее я неправильно ожидаю того, как она должна работать.
И до строки после циклов Console.WriteLine("Результат output: " + output.Count); вообще не доходит.
 
Подскажите, почему моя конструкция с await Task.WhenAll(tasks) не срабатывает как я ожидаю.
Хотел следующего:
  • Разбить mail.txt на несколько частей.
  • Идти по списку имён name.txt в цикле.
  • Внутри него запускать несколько потоков (по потоку на каждую часть mail.txt) и записывать найденные строки в список output.

Но после этой операции список output пустой.
И строка
Console.WriteLine("Пройдено output: " + i);
вызывается только один раз, но должна на каждой итерации верхнего цикла.
Ошибка в логике. Это он сколько тасков запустит? namesCount*mailsCount?
Не сильно вникал, но предполагаю что async там вообще лишний.
Сразу что вижу - нельзя писать в List из разных потоков вот так, без лока, это не потокобезопасно.
 
  • Спасибо
Реакции: SergSh и kot2566
Это он сколько тасков запустит? namesCount*mailsCount?
Нет. Если все посчитать, то namesCount * threads (threads =20), но если за одну итерацию верхнего цикла, то должно быть threads, т.е. 20. Там же выражение await Task.WhenAll(tasks); в конце верхнего цикла (т.е. ждём завершения текущих Тасков и только потом идём на следующую итерацию, на запуск новых Тасков).

Update: поменял await Task.WhenAll(tasks); на await Task.WaitAll(tasks); и вроде всё заработало.
Но, всё равно, медленно на 15 млн строк будет полдня работать
 
Если два .txt файла, в name.txt содержатся имена:
Алексей
Сергей Владимирович
Павел

Во mail.txt содержатся имена и емэйлы, которые разделены табуляцией:
Алексей alex@bk.ru
Сергей Владимирович serg@mail.ru
Алексей alex2@list.ru

Шаблон должен проанализировать name.txt и mail.txt и выдать output.txt, представляющий собой mail.txt из которого удалены все строки с именами, которых нет в name.txt.
Как видно из примера, одно имя в mail.txt может содержаться более одного раза - в этом случае надо оставить все строки с этим именем. Шаблон должен работать с большими файлами (name.txt - 2 млн. строк; mail.txt - 15 млн. строк) и обрабатывать их за адекватное время.
Бюджет 1500 рублей.
@drdpython
Это делается софтом, который тут нельзя упоминать за 15 секунд. Вроде во вкладке Пост Обработка или Фильтр Базы.
Могу сделать бесплатно
 

Кто просматривает тему: (Всего: 0, Пользователи: 0, Гости: 0)