Консольный ввод и вывод в C#: Console.WriteLine, ReadLine и TryParse

Консольный ввод и вывод в C#: Console.WriteLine, ReadLine и TryParse Полезное

Консольный ввод и вывод в C# — это обмен текстом между программой и терминалом через статический класс System.Console: методы Write/WriteLine пишут в стандартный поток вывода, а ReadLine, Read и ReadKey читают то, что ввел пользователь. Важно различать три вещи: чтение строки (всегда возвращает текст), разбор этого текста в число (int.TryParse) и форматирование значения обратно в текст (интерполяция, форматные строки).

Ниже — полный рабочий пример, затем разбор вывода, ввода, типовых ошибок и того, что делать, если программа ведет себя не так. Примеры рассчитаны на .NET 10 и C# 14, но работают и на .NET 8.

Минимальная программа: спросить и ответить

Создай проект командами dotnet new console -o Hello, затем cd Hello и dotnet run. Шаблон использует инструкции верхнего уровня (top-level statements), поэтому класс Program и метод Main писать не нужно. Замени содержимое Program.cs на код ниже.

Console.Write("Как тебя зовут? ");
string? name = Console.ReadLine();

if (string.IsNullOrWhiteSpace(name))
{
    name = "гость";
}

int age;
while (true)
{
    Console.Write("Сколько тебе лет? ");
    string? input = Console.ReadLine();

    if (input is null) // ввод закрыт: Ctrl+Z и Enter в Windows, Ctrl+D в Linux/macOS
    {
        Console.Error.WriteLine("Ввод закрыт, выхожу.");
        return;
    }

    if (int.TryParse(input, out age) && age is >= 0 and <= 150)
    {
        break;
    }

    Console.WriteLine("Нужно целое число от 0 до 150, попробуй еще раз.");
}

Console.WriteLine($"Привет, {name.Trim()}! Через 10 лет тебе будет {age + 10}.");

Сеанс в терминале выглядит так (после вопросительного знака — то, что ввел пользователь):

Как тебя зовут? Аня
Сколько тебе лет? двадцать
Нужно целое число от 0 до 150, попробуй еще раз.
Сколько тебе лет? 20
Привет, Аня! Через 10 лет тебе будет 30.

Что здесь происходит по строкам:

  • Console.Write печатает вопрос без перевода строки, поэтому курсор остается справа от вопроса.
  • Console.ReadLine() ждет Enter и возвращает введенную строку без символа конца строки. Тип результата — string?: при закрытом вводе метод вернет null, и это нужно обработать.
  • int.TryParse пытается превратить текст в число. При неудаче он не бросает исключение, а возвращает false, и цикл просит ввести значение снова.
  • Console.Error.WriteLine пишет сообщение об ошибке в отдельный поток stderr, который не попадает в файл при перенаправлении основного вывода (> out.txt).

Вывод: Write, WriteLine и форматирование

Метод Что делает Когда брать
Console.Write(x) пишет значение без перевода строки подсказка перед вводом, вывод по частям
Console.WriteLine(x) пишет значение и Environment.NewLine обычный построчный вывод
Console.WriteLine() только перевод строки пустая строка-разделитель
Console.Error.WriteLine(x) пишет в stderr сообщения об ошибках и диагностика

Environment.NewLine зависит от ОС: в Windows это \r\n, в Linux и macOS — \n. WriteLine подставляет его сам, поэтому вручную писать \n обычно не нужно.

Собрать строку из значений можно тремя способами, в современном коде чаще берут интерполяцию.

using System.Globalization;

string item = "Кофе";
double price = 3.5;
int qty = 12;

Console.WriteLine("Товар: " + item + ", цена: " + price);   // конкатенация
Console.WriteLine("Товар: {0}, цена: {1}", item, price);     // составное форматирование
Console.WriteLine($"Товар: {item}, цена: {price}");          // интерполяция
Console.WriteLine($"|{item,-8}|{qty,5}|{price,8:F2}|");      // ширина, выравнивание, формат
Console.WriteLine(price.ToString("F2", CultureInfo.InvariantCulture));

Вывод при русской культуре системы (ru-RU):

Товар: Кофе, цена: 3,5
Товар: Кофе, цена: 3,5
Товар: Кофе, цена: 3,5
|Кофе    |   12|    3,50|
3.50

Разбор форматной части {значение,ширина:формат}:

  • {item,-8} — ширина 8 символов, минус означает выравнивание по левому краю;
  • {qty,5} — ширина 5, выравнивание по правому краю;
  • {price,8:F2} — ширина 8 и формат F2, то есть два знака после запятой. Другие частые форматы — N2 (с разделителем групп разрядов), D4 (целое с ведущими нулями), P1 (процент).

Разделитель дробной части берется из текущей культуры: в ru-RU это запятая, в en-US — точка. Если вывод читает другая программа (CSV, лог, скрипт), передавай CultureInfo.InvariantCulture, как в последней строке примера.

Ввод: ReadLine, Read и ReadKey

Метод Возвращает Когда ждет Типичная задача
ReadLine() string?, null при конце ввода до Enter ввод строки или числа
Read() int — код следующего символа, -1 при конце ввода до Enter (ввод буферизуется построчно) посимвольный разбор потока
ReadKey() ConsoleKeyInfo — клавиша, символ, модификаторы одно нажатие, Enter не нужен меню, «нажми любую клавишу», игры

Главное отличие: ReadLine и Read работают с текстом, который пришел в поток ввода, в том числе из файла через перенаправление <. ReadKey работает с клавиатурой. Если ввод перенаправлен, он бросает InvalidOperationException, поэтому перед ним стоит проверить Console.IsInputRedirected.

if (Console.IsInputRedirected)
{
    Console.WriteLine("Ввод перенаправлен, интерактивное меню недоступно.");
    return;
}

Console.WriteLine("Нажимай клавиши, Esc - выход.");
while (true)
{
    ConsoleKeyInfo key = Console.ReadKey(intercept: true);
    if (key.Key == ConsoleKey.Escape)
    {
        break;
    }
    Console.WriteLine($"Клавиша: {key.Key}, символ: '{key.KeyChar}', модификаторы: {key.Modifiers}");
}

Параметр intercept: true не дает символу появиться на экране. При нажатии Shift+A программа напечатает:

Клавиша: A, символ: 'A', модификаторы: Shift

Числа из ввода: Parse против TryParse

ReadLine всегда возвращает строку, даже если пользователь ввел цифры. Строку нужно разобрать. Частая ошибка новичка — int.Parse без проверки.

Неверно:

int n = int.Parse(Console.ReadLine());
Console.WriteLine(n * 2);

Результат: компилятор выдает предупреждение CS8604 о возможной передаче null, а при вводе abc программа падает с необработанным исключением:

Unhandled exception. System.FormatException: The input string 'abc' was not in a correct format.

Исправление — TryParse, который сообщает об успехе через bool:

Console.Write("Введи целое число: ");
if (int.TryParse(Console.ReadLine(), out int n))
{
    Console.WriteLine(n * 2);
}
else
{
    Console.WriteLine("Это не целое число.");
}

На ввод 21 программа выведет 42, на abc или пустую строку — «Это не целое число.». TryParse принимает и null, возвращая false. Значение, которое не помещается в int (например, 99999999999), тоже дает false, тогда как int.Parse бросил бы OverflowException.

Несколько чисел в одной строке и дробные значения

Дробные числа по умолчанию разбираются по текущей культуре: в ru-RU строка 2,5 читается, а 2.5 — нет. Если формат ввода задаешь ты сам, зафиксируй его явно.

using System.Globalization;

Console.Write("Введи два числа через пробел: ");
string line = Console.ReadLine() ?? "";
string[] parts = line.Split(' ', StringSplitOptions.RemoveEmptyEntries);

if (parts.Length == 2
    && double.TryParse(parts[0], NumberStyles.Float, CultureInfo.InvariantCulture, out double a)
    && double.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out double b))
{
    Console.WriteLine($"Сумма: {(a + b).ToString(CultureInfo.InvariantCulture)}");
}
else
{
    Console.WriteLine("Формат: два числа через пробел, дробная часть через точку, например 2.5 4");
}

На ввод 2.5 4 программа выведет Сумма: 6.5. RemoveEmptyEntries убирает пустые элементы, поэтому лишние пробелы между числами не ломают разбор. Для денежных сумм вместо double бери decimal — он хранит десятичные дроби без двоичной погрешности в пределах своей точности.

Если не получилось

Симптом Причина Что сделать
Окно закрывается сразу после вывода программа завершилась, а запуск был двойным щелчком по .exe запускать через dotnet run из терминала или добавить в конец Console.ReadKey()
Вместо кириллицы знаки вопроса кодировка консоли не UTF-8 (чаще старая консоль Windows) в начале программы Console.OutputEncoding = System.Text.Encoding.UTF8;, для ввода — Console.InputEncoding
FormatException при вводе числа используется Parse без проверки или не та культура TryParse, для дробных — явная культура
Предупреждение CS8600 или CS8604 ReadLine возвращает string? проверить на null или взять ?? ""
InvalidOperationException в ReadKey ввод перенаправлен (файл, некоторые окна вывода IDE) проверять Console.IsInputRedirected, для потока использовать ReadLine

Выводы

  • Вывод в консоль в C# — Console.Write и Console.WriteLine, ошибки лучше писать в Console.Error.
  • Интерполяция $"{x,ширина:формат}" решает и подстановку, и выравнивание, и формат чисел; разделитель дробной части зависит от культуры.
  • ReadLine возвращает string?: null означает конец ввода, это не пустая строка.
  • Числа из ввода разбирай через TryParse, а не Parse; для машинно-читаемого формата указывай CultureInfo.InvariantCulture.
  • ReadKey нужен для работы с клавишами и не работает при перенаправленном вводе.

Где применяется / связь с практикой

Консольные программы — формат первых учебных задач, утилит и скриптов автоматизации. Те же TryParse, культура и обработка null потом встречаются при разборе конфигов, файлов и параметров веб-запросов.

Освойте тему на практике

Если хочешь системно пройти основы языка от консольных программ до ООП и коллекций с практикой и разбором домашних заданий, посмотри курс C# Developer. Basic. Попробовать формат обучения бесплатно можно на открытых уроках Otus.

FAQ

Как очистить консоль в C#?
Методом Console.Clear(). Если вывод перенаправлен в файл, в Windows он бросает IOException, а в Linux просто ничего не делает, поэтому в утилитах вызывай его только при !Console.IsOutputRedirected.

Как вывести текст цветом?
Задать Console.ForegroundColor = ConsoleColor.Green; перед выводом и вернуть исходные цвета через Console.ResetColor().

Как прочитать весь ввод до конца, например из файла через <?
Читать ReadLine в цикле, пока он не вернет null, или вызвать Console.In.ReadToEnd(), чтобы получить весь текст одной строкой.

OTUS Журнал
Скидка 5% 12-19 октября на курсы (popup)
Бесплатные открытые уроки (поп-ап)