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

axaptus

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

Во mail.txt содержатся имена и емэйлы, которые разделены табуляцией:
Алексей [email protected]
Сергей Владимирович [email protected]
Алексей [email protected]

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

Viking01

Client
Регистрация
19.08.2017
Сообщения
228
Благодарностей
151
Баллы
43
с такими большими файлами работать - потребуется много оперативки.
проще в БД запихнуть.
и в БД вся задача делается двумя командами :-)
 
Регистрация
03.12.2020
Сообщения
146
Благодарностей
103
Баллы
43
с такими большими файлами работать - потребуется много оперативки.
проще в БД запихнуть.
и в БД вся задача делается двумя командами :-)
Согласен. Если БД - это сложно - есть куча софта для дорвейщиков, который умеет такие выборки делать.

Вопрос лишь в интерпретации понятия "адекватное время".
 

kot2566

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

Но после этой операции список output пустой.
И строка
Console.WriteLine("Пройдено output: " + i);
вызывается только один раз, но должна на каждой итерации верхнего цикла.
 
Последнее редактирование:

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 720
Баллы
113
Подскажите, почему моя конструкция с await Task.WhenAll(tasks) не срабатывает как я ожидаю.
Хотел следующего:
  • Разбить mail.txt на несколько частей.
  • Идти по списку имён name.txt в цикле.
  • Внутри него запускать несколько потоков (по потоку на каждую часть mail.txt) и записывать найденные строки в список output.

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

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 720
Баллы
113
в общем не разобрался как там код сохранить, вот немного переделал, вроде похоже на правду. только в лог не все выводит, наверно ограничение той платформы.

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

kot2566

Client
Регистрация
25.11.2019
Сообщения
19
Благодарностей
6
Баллы
3
потому что у тебя в коде создается таска, добавляется в список задач и тут же выполняется с очисткой всех задач. и уходит на добавление следующей таски.
сначала добавь все таски, потом уже выполняй их.
не понимаю. А разве не именно это и происходит? Очистка и выполнение происходит после заполнения списка задач.

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

Phoenix78

Client
Read only
Регистрация
06.11.2018
Сообщения
11 790
Благодарностей
5 720
Баллы
113
не понимаю. А разве не именно это и происходит? Очистка и выполнение происходит после заполнения списка задач.

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

kot2566

Client
Регистрация
25.11.2019
Сообщения
19
Благодарностей
6
Баллы
3
в общем не разобрался как там код сохранить, вот немного переделал, вроде похоже на правду. только в лог не все выводит, наверно ограничение той платформы.

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); вообще не доходит.
 

Viking01

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

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

kot2566

Client
Регистрация
25.11.2019
Сообщения
19
Благодарностей
6
Баллы
3
Это он сколько тасков запустит? namesCount*mailsCount?
Нет. Если все посчитать, то namesCount * threads (threads =20), но если за одну итерацию верхнего цикла, то должно быть threads, т.е. 20. Там же выражение await Task.WhenAll(tasks); в конце верхнего цикла (т.е. ждём завершения текущих Тасков и только потом идём на следующую итерацию, на запуск новых Тасков).

Update: поменял await Task.WhenAll(tasks); на await Task.WaitAll(tasks); и вроде всё заработало.
Но, всё равно, медленно на 15 млн строк будет полдня работать
 

Reactor3000

Client
Регистрация
25.09.2019
Сообщения
434
Благодарностей
263
Баллы
63
Если два .txt файла, в name.txt содержатся имена:
Алексей
Сергей Владимирович
Павел

Во mail.txt содержатся имена и емэйлы, которые разделены табуляцией:
Алексей [email protected]
Сергей Владимирович [email protected]
Алексей [email protected]

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

Viking01

Client
Регистрация
19.08.2017
Сообщения
228
Благодарностей
151
Баллы
43

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