StreamReader в C# Windows Forms: фильтр строк по длине
Создайте приложение на C# Windows Forms с TextBox для порога, ListBox для строк и StreamReader/StreamWriter для чтения файла, фильтрации строк по длине и записи в новый файл. Рабочий код с async, using и обработкой ошибок.
Задание C#: Создайте приложение с графическим интерфейсом (например, Windows Forms), которое читает текстовый файл и переписывает в новый файл все строки, длина которых больше заданного числа. Как правильно реализовать это с использованием подходящих элементов управления (TextBox для ввода порога длины, кнопка для запуска, ListBox для отображения) и StreamReader/StreamWriter? Пожалуйста, приведите рабочий пример кода. Мой текущий код (учуcь, мало что знаю) — помогите исправить:
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.
Содержание
- Краткий обзор решения
- Элементы управления: TextBox (windows forms textbox) и ListBox (windows forms listbox)
- Использование StreamReader и StreamWriter в Windows Forms
- Рабочий пример кода (полный)
- Разбор и исправления вашего кода
- Оптимизация для больших файлов и UX
- Источники
- Заключение
Краткий обзор решения
Задача решается очень просто: читаем файл построчно, проверяем длину строки и, если она больше порога, записываем её в новый файл и добавляем в 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), чтобы пропускать только цифры:
private void textBoxThreshold_KeyPress(object sender, KeyPressEventArgs e)
{
e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar);
}
Совет: NumericUpDown устраняет потребность в ручной валидации.
Использование StreamReader и StreamWriter в Windows Forms
Правильный шаблон — использовать using для автоматического закрытия потоков:
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):
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
Разбор и исправления вашего кода
Ваш код (упрощённо):
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).
Исправленный минимальный вариант вашего обработчика (синхронный, без фонового потока):
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.
Источники
- C# и .NET | Чтение и запись текстовых файлов. StreamReader и StreamWriter — Metanit
- C#. Windows Forms. Разработка программы чтения и записи — BestProg
- Чтение и запись в текстовый файл с помощью Visual C# — Microsoft Learn
- FileStream. Чтение и запись файла — Metanit
- Учебное пособие по потоку в C#: StreamReader и StreamWriter — Guru99
- Чтение и запись текстовых файлов в C#. StreamWriter и StreamReader — WebDelphi
- StreamReader Класс (System.IO) — Microsoft Learn (API)
- C#. Текстовые файлы. Решение задач на модификацию текстовых файлов — BestProg
- Чтение текста из файла - .NET — Microsoft Learn
- Чтение текстового файла построчно на C# — CoderNotes
- How to read and filter lines in text file Using C# — StackOverflow
- Filtering text files in C# — StackOverflow
- How to read file line by line and print to a text box c# — StackOverflow
- Filtering specific lines in C# — StackOverflow
- C# Tutorial – Read Text File Line by Line in Windows Forms — MooICT
- What is the fastest way to filter certain lines from text file — CodeProject
- c# How to filter big txt file — StackOverflow
- C# Read Text File Line-by-Line : C# 411
- Is it possible to filter a TextFile in listview in C# — StackOverflow
- 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 + корректная валидация и обработка ошибок — даёт надёжный и быстрый результат.