Программирование

StreamReader в C# Windows Forms: фильтр строк по длине

Создайте приложение на C# Windows Forms с TextBox для порога, ListBox для строк и StreamReader/StreamWriter для чтения файла, фильтрации строк по длине и записи в новый файл. Рабочий код с async, using и обработкой ошибок.

Задание C#: Создайте приложение с графическим интерфейсом (например, Windows Forms), которое читает текстовый файл и переписывает в новый файл все строки, длина которых больше заданного числа. Как правильно реализовать это с использованием подходящих элементов управления (TextBox для ввода порога длины, кнопка для запуска, ListBox для отображения) и StreamReader/StreamWriter? Пожалуйста, приведите рабочий пример кода. Мой текущий код (учуcь, мало что знаю) — помогите исправить:

csharp
private void button2_Click(object sender, EventArgs e)
{
 StreamReader fileIn = new StreamReader("text.txt");
 StreamWriter fileOut = new StreamWriter("newText.txt");
 {
 string l;
 while ((l = fileIn.ReadLine()) != null)
 {
 listBox1.Items.Add(l);
 StringBuilder newText = new StringBuilder();
 for (int i = 0; i < l.Length; i += 2)
 {
 newText.Append(l[i]);
 }
 string r = newText.ToString();
 listBox2.Items.Add(r);
 fileOut.WriteLine(r);
 }
 }
}

Используйте StreamReader для построчного чтения и StreamWriter для записи подходящих строк; в Windows Forms поставьте TextBox для ввода порога и ListBox для отображения исходных и отфильтрованных строк. Ниже — безопасный, рабочий и асинхронный пример с использованием new StreamReader/System.IO.StreamReader, обработкой ошибок и исправлениями для вашего кода. Код показывает, как не блокировать UI и как корректно закрывать файлы через using.


Содержание


Краткий обзор решения

Задача решается очень просто: читаем файл построчно, проверяем длину строки и, если она больше порога, записываем её в новый файл и добавляем в ListBox. Чтение выполняем через StreamReader в блоке using (или через File.ReadLines для ленивой загрузки), запись — через StreamWriter. Чтобы не «подвесить» интерфейс, длинную операцию выполняем в фоновом потоке (Task.Run / async-await), а обновление ListBox делаем на UI-потоке.


Элементы управления: TextBox (windows forms textbox) и ListBox (windows forms listbox)

Рекомендуемые контролы и имена (в дизайнере Form):

  • TextBox для порога: textBoxThreshold (или NumericUpDown — ещё лучше, чтобы ограничить ввод только числа).
  • Кнопка запуска: buttonProcess (или button2 в вашем проекте).
  • Два ListBox: listBoxOriginal (все строки) и listBoxFiltered (строки, длина > порога).
  • Опционально: textBoxInput, textBoxOutput + кнопки выбора файлов (OpenFileDialog / SaveFileDialog).

Пример валидации ввода (KeyPress для textBoxThreshold), чтобы пропускать только цифры:

csharp
private void textBoxThreshold_KeyPress(object sender, KeyPressEventArgs e)
{
 e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar);
}

Совет: NumericUpDown устраняет потребность в ручной валидации.


Использование StreamReader и StreamWriter в Windows Forms

Правильный шаблон — использовать using для автоматического закрытия потоков:

csharp
using (var sr = new StreamReader(inputPath))
using (var sw = new StreamWriter(outputPath, false, Encoding.UTF8))
{
 string line;
 while ((line = sr.ReadLine()) != null)
 {
 // фильтр по длине
 }
}

new StreamReader автоматически определяет BOM при чтении; если нужна явная кодировка, передайте её. Полезные справки по API и примерам чтения/записи: StreamReader — Microsoft Learn и общие примеры чтения/записи файлов: Чтение и запись в текстовый файл — Microsoft Learn. Для краткого руководства и примеров на русском можно посмотреть и Metanit.


Рабочий пример кода (полный)

Ниже — полный, практичный пример для WinForms. Предполагается, что на форме есть:

  • textBoxThreshold — ввод порога,
  • textBoxInput — путь входного файла (можно оставить пустым, используется “text.txt”),
  • textBoxOutput — путь выходного файла (по умолчанию “newText.txt”),
  • buttonSelectInput / buttonSelectOutput — кнопки выбора файлов (опционально),
  • buttonProcess — кнопка запуска,
  • listBoxOriginal / listBoxFiltered — для отображения строк,
  • openFileDialog1 / saveFileDialog1 — компоненты диалогов.

Код (Form1.cs):

csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

public partial class Form1 : Form
{
 public Form1()
 {
 InitializeComponent();
 }

 private async void buttonProcess_Click(object sender, EventArgs e)
 {
 // Валидация порога
 if (!int.TryParse(textBoxThreshold.Text.Trim(), out int threshold) || threshold < 0)
 {
 MessageBox.Show("Введите корректное неотрицательное число порога длины.", "Неверный ввод",
 MessageBoxButtons.OK, MessageBoxIcon.Warning);
 textBoxThreshold.Focus();
 return;
 }

 string inputPath = string.IsNullOrWhiteSpace(textBoxInput.Text) ? "text.txt" : textBoxInput.Text;
 string outputPath = string.IsNullOrWhiteSpace(textBoxOutput.Text) ? "newText.txt" : textBoxOutput.Text;

 listBoxOriginal.Items.Clear();
 listBoxFiltered.Items.Clear();
 buttonProcess.Enabled = false;

 var originalLines = new List<string>();
 var filteredLines = new List<string>();

 try
 {
 // Выполняем чтение/запись в фоновом потоке, чтобы не блокировать UI
 await Task.Run(() =>
 {
 // Для больших файлов полезно использовать FileStream с опцией SequentialScan
 using (var fs = new FileStream(inputPath, FileMode.Open, FileAccess.Read, FileShare.Read,
 bufferSize: 4096, FileOptions.SequentialScan))
 using (var sr = new StreamReader(fs, Encoding.UTF8))
 using (var sw = new StreamWriter(outputPath, false, Encoding.UTF8))
 {
 string line;
 while ((line = sr.ReadLine()) != null)
 {
 originalLines.Add(line); // собираем, чтобы потом обновить UI разом
 if (line.Length > threshold)
 {
 filteredLines.Add(line);
 sw.WriteLine(line);
 }
 }
 }
 });

 // Обновляем UI на UI-потоке
 listBoxOriginal.BeginUpdate();
 foreach (var s in originalLines) listBoxOriginal.Items.Add(s);
 listBoxOriginal.EndUpdate();

 listBoxFiltered.BeginUpdate();
 foreach (var s in filteredLines) listBoxFiltered.Items.Add(s);
 listBoxFiltered.EndUpdate();

 MessageBox.Show($"Готово: {filteredLines.Count} строк(а) записано в '{outputPath}'.", "Успех",
 MessageBoxButtons.OK, MessageBoxIcon.Information);
 }
 catch (FileNotFoundException)
 {
 MessageBox.Show($"Входной файл '{inputPath}' не найден.", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
 }
 catch (UnauthorizedAccessException)
 {
 MessageBox.Show("Нет доступа к файлу.", "Ошибка доступа", MessageBoxButtons.OK, MessageBoxIcon.Error);
 }
 catch (Exception ex)
 {
 MessageBox.Show("Ошибка: " + ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
 }
 finally
 {
 buttonProcess.Enabled = true;
 }
 }

 private void buttonSelectInput_Click(object sender, EventArgs e)
 {
 if (openFileDialog1.ShowDialog() == DialogResult.OK) textBoxInput.Text = openFileDialog1.FileName;
 }

 private void buttonSelectOutput_Click(object sender, EventArgs e)
 {
 if (saveFileDialog1.ShowDialog() == DialogResult.OK) textBoxOutput.Text = saveFileDialog1.FileName;
 }

 private void textBoxThreshold_KeyPress(object sender, KeyPressEventArgs e)
 {
 // Разрешаем только цифры и управление
 e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar);
 }
}

Комментарий: здесь мы используем new StreamReader через конструктор StreamReader(fs, Encoding.UTF8), собираем строки в List в фоновом потоке и обновляем ListBox только после завершения чтения — это быстро и безопасно. Если файл маленький и вы не против блокировки UI, можно упростить и читать синхронно.


Разбор и исправления вашего кода

Ваш код (упрощённо):

csharp
StreamReader fileIn = new StreamReader("text.txt");
StreamWriter fileOut = new StreamWriter("newText.txt");
while ((l = fileIn.ReadLine()) != null)
{
 listBox1.Items.Add(l);
 StringBuilder newText = new StringBuilder();
 for (int i = 0; i < l.Length; i += 2)
 {
 newText.Append(l[i]);
 }
 string r = newText.ToString();
 listBox2.Items.Add(r);
 fileOut.WriteLine(r);
}

Что было неправильно и как исправлено:

  • Логика с StringBuilder и i += 2 берёт каждый второй символ — это не фильтрация по длине. Уберите этот фрагмент и просто проверяйте if (l.Length > threshold).
  • Нет using — файловые потоки не закрываются при исключении. Решение: using(…) { … }.
  • Нет проверки/валидации порога (int.TryParse).
  • UI блокируется при больших файлах (работа в UI-потоке). Решение: Task.Run / async.
  • Не очищаются ListBox перед заполнением — будут дубли.
  • Нет обработки ошибок (FileNotFoundException, IOException, UnauthorizedAccessException).

Исправленный минимальный вариант вашего обработчика (синхронный, без фонового потока):

csharp
private void button2_Click(object sender, EventArgs e)
{
 if (!int.TryParse(textBoxThreshold.Text, out int threshold)) { MessageBox.Show("Введите число"); return; }

 listBox1.Items.Clear();
 listBox2.Items.Clear();

 using (var fileIn = new StreamReader("text.txt"))
 using (var fileOut = new StreamWriter("newText.txt"))
 {
 string l;
 while ((l = fileIn.ReadLine()) != null)
 {
 listBox1.Items.Add(l);
 if (l.Length > threshold)
 {
 listBox2.Items.Add(l);
 fileOut.WriteLine(l);
 }
 }
 }
}

Оптимизация для больших файлов и UX

Несколько практических советов:

  • Для очень больших файлов: используйте StreamReader/StreamWriter в цикле и записывайте строки по мере появления — это экономит память. File.ReadLines тоже лениво возвращает строки и удобен с LINQ.
  • Если нужно максимум скорости при последовательном чтении — используйте FileStream с параметром FileOptions.SequentialScan (пример в коде выше).
  • Избегайте частых операций с UI (частые добавления в ListBox) — собирайте в коллекцию и обновляйте UI разом (BeginUpdate/EndUpdate) или добавляйте пачками.
  • Не блокируйте UI: выполняйте чтение/запись в Task.Run и обновляйте интерфейс после await.
  • Учёт кодировки: если файлы в Windows-1251, передавайте соответствующую кодировку в StreamReader/StreamWriter, иначе получите «кракозябры».
  • Для интерактивности можно показывать прогресс (число обработанных строк) с помощью Progress и IProgress.

Дополнительные источники по производительности и ленивому чтению: обсуждения на StackOverflow и CodeProject содержат практические паттерны, например How to read and filter lines — StackOverflow и идеи оптимизации на CodeProject.


Источники

  1. C# и .NET | Чтение и запись текстовых файлов. StreamReader и StreamWriter — Metanit
  2. C#. Windows Forms. Разработка программы чтения и записи — BestProg
  3. Чтение и запись в текстовый файл с помощью Visual C# — Microsoft Learn
  4. FileStream. Чтение и запись файла — Metanit
  5. Учебное пособие по потоку в C#: StreamReader и StreamWriter — Guru99
  6. Чтение и запись текстовых файлов в C#. StreamWriter и StreamReader — WebDelphi
  7. StreamReader Класс (System.IO) — Microsoft Learn (API)
  8. C#. Текстовые файлы. Решение задач на модификацию текстовых файлов — BestProg
  9. Чтение текста из файла - .NET — Microsoft Learn
  10. Чтение текстового файла построчно на C# — CoderNotes
  11. How to read and filter lines in text file Using C# — StackOverflow
  12. Filtering text files in C# — StackOverflow
  13. How to read file line by line and print to a text box c# — StackOverflow
  14. Filtering specific lines in C# — StackOverflow
  15. C# Tutorial – Read Text File Line by Line in Windows Forms — MooICT
  16. What is the fastest way to filter certain lines from text file — CodeProject
  17. c# How to filter big txt file — StackOverflow
  18. C# Read Text File Line-by-Line : C# 411
  19. Is it possible to filter a TextFile in listview in C# — StackOverflow
  20. How remove txt all line where available $ or ! or >? — StackOverflow

Заключение

Кратко: используйте StreamReader для построчного чтения и StreamWriter для записи отфильтрованных строк, валидируйте порог через TextBox (или NumericUpDown), обновляйте ListBox на UI-потоке и не забывайте using для безопасного закрытия файлов. Если файл большой — читайте и пишите в фоне (Task.Run) и применяйте FileOptions.SequentialScan или File.ReadLines. Основная практика — streamreader + streamwriter + корректная валидация и обработка ошибок — даёт надёжный и быстрый результат.

Авторы
Проверено модерацией
Модерация