Модуль readline в Node.js: чтение ввода построчно
Содержание
О проверке кода. Примеры не запускались в этой сессии (по договорённости — фокус на контенте, а не прогонах). Говорим прямо, а не ставим бейдж «проверено».
readline в Node.js решает две задачи: запросить ввод у пользователя в консоли и прочитать большой файл построчно. Обе — частые, обе он делает без внешних зависимостей. Разберём современный подход.
Ввод пользователя: промис-версия
Современный способ — модуль readline/promises с await:
const readline = require('node:readline/promises');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const name = await rl.question('Как вас зовут? ');
const age = await rl.question('Сколько вам лет? ');
console.log(`Привет, ${name}! Вам ${age}.`);
rl.close(); // обязательно — иначе процесс не завершится
rl.question показывает вопрос и ждёт ответа пользователя. Промис-версия (node:readline/promises) возвращает промис, поэтому читается как синхронный код через await — без вложенных колбэков. Интерфейс создаётся на process.stdin/process.stdout. Важно вызвать rl.close(), иначе процесс останется висеть, ожидая ввода.
Старая колбэк-версия (node:readline) тоже работает, но await заметно чище:
// старый стиль — колбэк
rl.question('Имя? ', (answer) => {
console.log(answer);
rl.close();
});
Чтение файла построчно
Главное серьёзное применение — обработка больших файлов без загрузки в память:
const readline = require('node:readline');
const fs = require('node:fs');
const rl = readline.createInterface({
input: fs.createReadStream('huge.log'), // поток чтения файла
crlfDelay: Infinity, // корректно с \r\n
});
let lineCount = 0;
for await (const line of rl) { // перебираем строки по одной
lineCount++;
if (line.includes('ERROR')) {
console.log(`Строка ${lineCount}: ${line}`);
}
}
console.log(`Всего строк: ${lineCount}`);
createInterface на потоке чтения файла выдаёт файл по одной строке, а for await их перебирает. Файл при этом не загружается целиком — в памяти в каждый момент лишь одна строка. Это работает и для гигабайтных логов, которые в память просто не поместятся.
Почему не readFile
fs.readFile — весь файл сразу
Для больших файлов не годится
- Грузит файл целиком в память
- Файл 4 ГБ → нужно 4 ГБ ОЗУ
- Больше памяти → падение
- Ок только для мелких файлов
readline — по строке
Файл любого размера
- В памяти одна строка за раз
- Файл любого размера — ок
- Обработка на лету
- Основа для логов и CSV
fs.readFile загружает файл в память полностью. Для конфига в пару килобайт это нормально, но файл больше доступной памяти так не прочитать — процесс упадёт с нехваткой памяти. readline через поток держит лишь текущую строку, поэтому справляется с файлами любого размера. Правило: мелкий файл — readFile, большой построчно — readline.
Практический пример: простое меню
const readline = require('node:readline/promises');
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
let running = true;
while (running) {
const cmd = await rl.question('> ');
if (cmd === 'exit') {
running = false;
} else if (cmd === 'time') {
console.log(new Date().toLocaleTimeString('ru-RU'));
} else {
console.log('Неизвестная команда');
}
}
rl.close();
Цикл с await rl.question — основа интерактивных консольных утилит: спросить, обработать, повторить. Это удобно для небольших инструментов, генераторов конфигов, обучающих скриптов — там, где нужен диалог с пользователем без веб-интерфейса.
Когда это нужно
- интерактивные CLI-скрипты — спросить имя проекта, подтвердить действие, выбрать вариант;
- построчная обработка логов и CSV — фильтр, подсчёт, поиск по гигабайтным файлам;
- простые REPL-подобные инструменты — свой цикл «команда → ответ».
Для сложных CLI с флагами и подкомандами берут библиотеки, а util.parseArgs разбирает аргументы командной строки. Но для чтения ввода и построчной обработки файлов readline — точный инструмент из коробки.
Смежные темы: потоки — как читается файл; process и stdin; файловая система — readFile против потока. Полный список — в уроках Node.js.