using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using OfficeOpenXml; using OfficeOpenXml; using System.Text.RegularExpressions; using System.Diagnostics; namespace NoDuplicate { public partial class Form1 : Form { private DataTable emailTable; // Таблица для хранения данных public Form1() { InitializeComponent(); } private void импортToolStripMenuItem_Click(object sender, EventArgs e) { OpenFileDialog openFileDialog = new OpenFileDialog { Filter = "Excel Files|*.xlsx", Title = "Выберите файл с email-адресами" }; if (openFileDialog.ShowDialog() == DialogResult.OK) { string filePath = openFileDialog.FileName; try { // Указываем лицензионный контекст для EPPlus OfficeOpenXml.ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial; using (var package = new ExcelPackage(new FileInfo(filePath))) { var worksheet = package.Workbook.Worksheets[0]; // Первый лист int rows = worksheet.Dimension.Rows; // Количество строк emailTable = new DataTable(); emailTable.Columns.Add("Email", typeof(string)); for (int row = 1; row <= rows; row++) { string email = worksheet.Cells[row, 1].Text; if (!string.IsNullOrEmpty(email)) { emailTable.Rows.Add(email); } } dataGridView1.DataSource = emailTable; //MessageBox.Show("Данные успешно загружены!", "Импорт"); } } catch (Exception ex) { MessageBox.Show($"Ошибка при импорте файла: {ex.Message}", "Ошибка"); } } } private List removedEmails = new List(); // Список для хранения удалённых адресов private string reportsDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "MyAppReports"); // Путь к папке для отчётов private void SaveReport(List validEmails, List invalidEmails, List removedEmails) { try { if (!Directory.Exists(reportsDirectory)) { Directory.CreateDirectory(reportsDirectory); } string reportFileName = $"EmailReport_{DateTime.Now:yyyyMMdd_HHmmss}.txt"; string reportPath = Path.Combine(reportsDirectory, reportFileName); using (StreamWriter writer = new StreamWriter(reportPath)) { writer.WriteLine($"Отчёт о проверке email-адресов ({DateTime.Now}):"); writer.WriteLine("\nКорректные email:"); writer.WriteLine($"Всего корректных адресов: {validEmails.Count}"); foreach (var email in validEmails) { writer.WriteLine(email); } writer.WriteLine("\nНевалидные email:"); writer.WriteLine($"Всего невалидных адресов: {invalidEmails.Count}"); foreach (var email in invalidEmails) { writer.WriteLine(email); } writer.WriteLine("\nУдалённые дубликаты:"); writer.WriteLine($"Всего удалено дубликатов: {removedEmails.Count}"); foreach (var email in removedEmails) { writer.WriteLine(email); } } MessageBox.Show($"Отчёт сохранён в папке: {reportsDirectory}", "Успех"); } catch (Exception ex) { MessageBox.Show($"Ошибка при сохранении отчёта: {ex.Message}", "Ошибка"); } } private void btnRemoveDuplicates_Click(object sender, EventArgs e) { if (emailTable == null || emailTable.Rows.Count == 0) { lblStatus.Text = "Таблица пуста или не загружена."; lblStatus.ForeColor = Color.Red; // Красный цвет для ошибки return; } try { var allEmails = emailTable.AsEnumerable() .Select(row => row.Field("Email").Trim()) .ToList(); // Проверяем валидность email var validEmails = allEmails.Where(email => IsValidEmail(email)).ToList(); var invalidEmails = allEmails.Except(validEmails).ToList(); var distinctEmails = validEmails.Distinct(StringComparer.OrdinalIgnoreCase).ToList(); removedEmails = allEmails .GroupBy(email => email, StringComparer.OrdinalIgnoreCase) .Where(group => group.Count() > 1) .SelectMany(group => group.Skip(1)) .ToList(); emailTable.Rows.Clear(); foreach (var email in distinctEmails) { emailTable.Rows.Add(email); } dataGridView1.DataSource = emailTable; // Передаем все три списка в метод SaveReport SaveReport(validEmails, invalidEmails, removedEmails); // Обновляем сообщение на форме lblStatus.Text = $"Удаление завершено! Удалено {removedEmails.Count} адрес(ов)."; lblStatus.ForeColor = Color.Green; // Зелёный цвет для успеха } catch (Exception ex) { lblStatus.Text = $"Ошибка: {ex.Message}"; lblStatus.ForeColor = Color.Red; // Красный цвет для ошибки } } private void экспортToolStripMenuItem_Click(object sender, EventArgs e) { if (emailTable == null || emailTable.Rows.Count == 0) { MessageBox.Show("Нет данных для экспорта.", "Ошибка"); return; } // Открываем диалог для выбора места сохранения файла SaveFileDialog saveFileDialog = new SaveFileDialog { Filter = "Excel Files|*.xlsx", Title = "Сохранить как", FileName = "Emails.xlsx" }; if (saveFileDialog.ShowDialog() == DialogResult.OK) { string filePath = saveFileDialog.FileName; try { OfficeOpenXml.ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial; using (var package = new OfficeOpenXml.ExcelPackage()) { // Создаем новый лист var worksheet = package.Workbook.Worksheets.Add("Emails"); // Записываем данные из DataTable в Excel for (int i = 0; i < emailTable.Rows.Count; i++) { worksheet.Cells[i + 1, 1].Value = emailTable.Rows[i]["Email"]; // Первая строка без заголовка } // Сохраняем файл package.SaveAs(new FileInfo(filePath)); } //MessageBox.Show("Данные успешно экспортированы!", "Успех"); } catch (Exception ex) { MessageBox.Show($"Ошибка при экспорте файла: {ex.Message}", "Ошибка"); } } } private void помощьToolStripMenuItem_Click(object sender, EventArgs e) { MessageBox.Show("Инструкция по работе с программой\r\nПрограмма для работы с email-адресами позволяет удалять дубликаты из списка адресов и создавать отчёты об удалённых данных. Следуйте этим шагам:\r\n\r\n1. Импорт файла\r\nНажмите кнопку \"Импорт\" или выберите пункт меню \"Файл → Импорт\".\r\nВ появившемся окне выберите файл в формате .xlsx, содержащий список email-адресов.\r\nПрограмма загрузит данные и отобразит их в таблице.\r\n2. Удаление дубликатов\r\nПосле импорта нажмите кнопку \"Удалить дубликаты\".\r\nПрограмма автоматически:\r\nУдалит повторяющиеся email-адреса (с учётом регистра и пробелов).\r\nОбновит таблицу, оставив только уникальные адреса.\r\nСоздаст отчёт в папке MyAppReports в \"Моих документах\". Отчёт содержит список удалённых адресов.\r\n3. Экспорт файла\r\nЧтобы сохранить уникальные адреса, нажмите кнопку \"Экспорт\" или выберите пункт меню \"Файл → Экспорт\".\r\nВыберите место и имя для нового файла в формате .xlsx.\r\nПрограмма создаст файл с уникальными адресами.\r\n4. Папка отчётов\r\nВсе отчёты об удалённых адресах сохраняются в папке MyAppReports (расположена в \"Моих документах\").\r\nКаждый отчёт содержит:\r\nОбщее количество удалённых адресов.\r\nПолный список удалённых адресов.\r\nВы можете открыть эту папку вручную или добавить кнопку для её быстрого открытия."); } private bool IsValidEmail(string email) { if (string.IsNullOrWhiteSpace(email)) return false; // Регулярное выражение для проверки email var emailRegex = new Regex(@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.[a-zA-Z]{2,}$"); if (!emailRegex.IsMatch(email.Trim())) return false; // Проверяем валидность домена string domain = email.Split('@').LastOrDefault(); if (!IsValidDomain(domain)) return false; // Проверяем, что нет двойной точки в любом месте email if (email.Contains("..")) return false; return true; } private bool IsValidDomain(string domain) { // Список валидных доменных окончаний string[] validTlds = { "com", "org", "net", "edu", "gov", "io", "co", "uk", "ru", "de", "fr", "jp", "cn", "us", "museum", "jobs", "info", "biz", "xyz", "ai", "name" }; string[] parts = domain.Split('.'); if (parts.Length < 2) return false; // Минимум 2 части: имя домена и TLD // Проверяем, что каждая часть домена не пуста, не начинается и не заканчивается тире foreach (string part in parts) { if (string.IsNullOrEmpty(part) || part.StartsWith("-") || part.EndsWith("-")) return false; } // Проверяем валидность TLD string tld = parts.LastOrDefault(); if (!validTlds.Contains(tld)) return false; return true; } private void btnValidateEmails_Click(object sender, EventArgs e) { if (emailTable == null || emailTable.Rows.Count == 0) { lblStatus.Text = "Таблица пуста или не загружена."; lblStatus.ForeColor = Color.Red; return; } var validEmails = new List(); var invalidEmails = new List(); foreach (DataRow row in emailTable.Rows) { string email = row.Field("Email").Trim(); // Убираем пробелы bool isValid = IsValidEmail(email); if (isValid) { validEmails.Add(email); } else { invalidEmails.Add(email); } // Отладочный вывод Debug.WriteLine($"Email: '{email}', Корректный: {isValid}"); } // Проверяем, что корректные email записываются обратно Debug.WriteLine("Корректные email:"); foreach (var email in validEmails) { Debug.WriteLine(email); } Debug.WriteLine("Невалидные email:"); foreach (var email in invalidEmails) { Debug.WriteLine(email); } // Обновляем основную таблицу (только валидные email) emailTable.Rows.Clear(); foreach (var email in validEmails) { emailTable.Rows.Add(email); } dataGridView1.DataSource = emailTable; // Обновляем таблицу невалидных email var invalidEmailTable = new DataTable(); invalidEmailTable.Columns.Add("Invalid Emails", typeof(string)); foreach (var email in invalidEmails) { invalidEmailTable.Rows.Add(email); } dataGridView2.DataSource = invalidEmailTable; // Обновляем статус lblStatus.Text = $"Проверка завершена! Корректных адресов: {validEmails.Count}, невалидных: {invalidEmails.Count}."; lblStatus.ForeColor = Color.Green; } } }