Readline
Исходный код: lib/readline.js
Модуль readline предоставляет интерфейс для чтения данных из потока Readable (например, process.stdin) по одной строке за раз. К нему можно получить доступ, используя:
const readline = require('readline'); Следующий простой пример демонстрирует основное использование модуля readline.
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question('What do you think of Node.js? ', (answer) => {
// TODO: Log the answer in a database
console.log(`Thank you for your valuable feedback: ${answer}`);
rl.close();
}); После вызова этого кода приложение Node.js не завершится до тех пор, пока readline.Interface не будет закрыто, так как интерфейс ожидает получения данных из потока input.
Класс: Interface
- Расширяет: <EventEmitter>
Экземпляры класса readline.Interface создаются с помощью метода readline.createInterface(). Каждый экземпляр связан с одним потоком Readable input и одним потоком Writable output. Поток output используется для вывода запросов на ввод данных пользователя, поступающих и считываемых из потока input.
Событие: 'close'
Событие 'close' генерируется в следующих случаях:
- Вызов метода
rl.close()и экземплярreadline.Interfaceосвобождает контроль над потокамиinputиoutput; - Поток
inputполучает событие'end'; - Поток
inputполучает Ctrl+D для сигнализации об окончании передачи (EOT); - Поток
inputполучает Ctrl+C для сигнализации обSIGINTи нет обработчика события'SIGINT'зарегистрированного на экземпляреreadline.Interface.
Функция-обработчик вызывается без передачи каких-либо аргументов.
Экземпляр readline.Interface завершается, когда генерируется событие 'close'.
Событие: 'line'
Событие 'line' генерируется всякий раз, когда поток input получает ввод с новой строки (\n, \r, или \r\n). Обычно это происходит при нажатии пользователем клавиши Enter или Return.
Функция-обработчик вызывается со строкой, содержащей единственную полученную строку.
rl.on('line', (input) => {
console.log(`Received: ${input}`);
}); Событие: 'history'
Событие 'history' генерируется всякий раз, когда изменяется массив истории.
Функция-обработчик вызывается с массивом, содержащим массив истории. Он будет отражать все изменения, добавленные и удаленные строки из-за historySize и removeHistoryDuplicates.
Основная цель — позволить обработчику сохранять историю. Также обработчик может изменить объект истории. Это может быть полезно для предотвращения добавления определенных строк в историю, например, пароля.
rl.on('history', (history) => {
console.log(`Received: ${history}`);
}); Событие: 'pause'
Событие 'pause' генерируется в следующих случаях:
- Поток
inputприостановлен. - Поток
inputне приостановлен и получает событие'SIGCONT'. (См. события'SIGTSTP'и'SIGCONT'.)
Функция-обработчик вызывается без передачи каких-либо аргументов.
rl.on('pause', () => {
console.log('Readline paused.');
}); Событие: 'resume'
Событие 'resume' генерируется всякий раз, когда поток input возобновляется.
Функция-обработчик вызывается без передачи каких-либо аргументов.
rl.on('resume', () => {
console.log('Readline resumed.');
}); Событие: 'SIGCONT'
Событие 'SIGCONT' генерируется, когда процесс Node.js, ранее переведенный в фоновый режим с помощью Ctrl+Z (т.е. SIGTSTP), затем возвращается на передний план с помощью fg(1p).
Если поток input был приостановлен до запроса SIGTSTP, это событие не будет сгенерировано.
Функция-обработчик вызывается без передачи каких-либо аргументов.
rl.on('SIGCONT', () => {
// `prompt` will automatically resume the stream
rl.prompt();
}); Событие 'SIGCONT' не поддерживается в Windows.
Событие: 'SIGINT'
Событие 'SIGINT' генерируется всякий раз, когда поток input получает ввод Ctrl+C, обычно известный как SIGINT. Если обработчики событий 'SIGINT' не зарегистрированы, когда поток input получает SIGINT, будет сгенерировано событие 'pause'.
Функция-обработчик вызывается без передачи каких-либо аргументов.
rl.on('SIGINT', () => {
rl.question('Are you sure you want to exit? ', (answer) => {
if (answer.match(/^y(es)?$/i)) rl.pause();
});
}); Событие: 'SIGTSTP'
Событие 'SIGTSTP' генерируется, когда поток input получает ввод Ctrl+Z, обычно известный как SIGTSTP. Если обработчики событий 'SIGTSTP' не зарегистрированы, когда поток input получает SIGTSTP, процесс Node.js будет перемещен на задний план.
При возобновлении программы с помощью fg(1p) будут сгенерированы события 'pause' и 'SIGCONT'. Эти события могут использоваться для возобновления потока input.
События 'pause' и 'SIGCONT' не будут сгенерированы, если поток input был приостановлен до отправки процесса на задний план.
Функция-обработчик вызывается без передачи каких-либо аргументов.
rl.on('SIGTSTP', () => {
// This will override SIGTSTP and prevent the program from going to the
// background.
console.log('Caught SIGTSTP.');
}); Событие 'SIGTSTP' не поддерживается в Windows.
rl.close()
Метод rl.close() закрывает экземпляр readline.Interface и освобождает контроль над потоками input и output. При вызове генерируется событие 'close'.
Вызов rl.close() не останавливает немедленно другие события (включая 'line') от генерации экземпляром readline.Interface.
rl.pause()
Метод rl.pause() приостанавливает поток input, позволяя его возобновить в случае необходимости.
Вызов rl.pause() не останавливает немедленно другие события (включая 'line') от генерации экземпляром readline.Interface.
rl.prompt([preserveCursor])
-
preserveCursor<boolean> Еслиtrue, предотвращает сброс позиции курсора до0.
Метод rl.prompt() записывает заданные для экземпляра readline.Interface параметры на новую строку в output, чтобы предоставить пользователю новое место для ввода.
При вызове метод rl.prompt() возобновит поток input, если он был приостановлен.
Если экземпляр readline.Interface был создан с output установленным в null или undefined, запрос не будет написан.
rl.question(query[, options], callback)
-
query<string> Выражение или вопрос для записи вoutput, добавленное перед запросом. -
options<Object>-
signal<AbortSignal> Опционально позволяет отменить запросquestion()с помощью сигналаAbortController.
-
-
callback<Function> Функция-обработчик, которая вызывается с вводом пользователя в ответ на запросquery.
Метод rl.question() отображает запрос query, записывая его в output, ожидает ввода пользователя в input, затем вызывает функцию callback, передавая предоставленный ввод в качестве первого аргумента.
При вызове метод rl.question() возобновит поток input, если он был приостановлен.
Если экземпляр readline.Interface был создан с output установленным в null или undefined, запрос query не будет выведен.
Функция callback, переданная в rl.question(), не следует типичному шаблону принятия объекта Error или сигнала null в качестве первого аргумента. Функция callback вызывается с предоставленным ответом как единственным аргументом.
Пример использования:
rl.question('What is your favorite food? ', (answer) => {
console.log(`Oh, so your favorite food is ${answer}`);
}); Использование сигнала AbortController для отмены запроса.
const ac = new AbortController();
const signal = ac.signal;
rl.question('What is your favorite food? ', { signal }, (answer) => {
console.log(`Oh, so your favorite food is ${answer}`);
});
signal.addEventListener('abort', () => {
console.log('The food question timed out');
}, { once: true });
setTimeout(() => ac.abort(), 10000); Если этот метод вызывается в виде его util.promisify() версии, он возвращает Promise, который выполняется с ответом. Если вопрос отменён с помощью AbortController, он будет отклонен с AbortError.
const util = require('util');
const question = util.promisify(rl.question).bind(rl);
async function questionExample() {
try {
const answer = await question('What is you favorite food? ');
console.log(`Oh, so your favorite food is ${answer}`);
} catch (err) {
console.error('Question rejected', err);
}
}
questionExample();
rl.resume()
Метод rl.resume() возобновляет поток input если он был приостановлен.
rl.setPrompt(prompt)
-
prompt<string>
Метод rl.setPrompt() устанавливает запрос, который будет написан в output при вызове rl.prompt().
rl.getPrompt()
- Возвращает: <string> текущую строку запроса
Метод rl.getPrompt() возвращает текущий запрос, используемый rl.prompt().
rl.write(data[, key])
-
data<строка> -
key<Объект>-
ctrl<логическое>trueдля обозначения клавиши Ctrl. -
meta<логическое>trueдля обозначения клавиши Meta. -
shift<логическое>trueдля обозначения клавиши Shift. -
name<строка> Название клавиши.
-
Метод rl.write() будет записывать либо data, либо последовательность клавиш, идентифицируемую key, в output. Аргумент key поддерживается только если output является текстовым терминалом TTY. См. Сочетания клавиш TTY для списка комбинаций клавиш.
Если key указан, data игнорируется.
При вызове rl.write() возобновит поток input, если он был приостановлен.
Если readline.Interface был создан с output установленным на null или undefined, data и key не записываются.
rl.write('Delete this!');
// Simulate Ctrl+U to delete the line written previously
rl.write(null, { ctrl: true, name: 'u' }); Метод rl.write() запишет данные в readline Interface's input как если бы они были введены пользователем.
rl[Symbol.asyncIterator]()
- Возвращает: <АсинхронныйИтератор>
Создает объект AsyncIterator, который итерирует по каждой строке в потоке ввода как строке. Этот метод позволяет асинхронную итерацию по объектам readline.Interface через циклы for await...of.
Ошибки в потоке ввода не передаются.
Если цикл прерван с помощью break, throw, или return, будет вызван rl.close(). Другими словами, итерация по readline.Interface всегда полностью потребляет поток ввода.
Производительность не соответствует традиционному API событий 'line'. Для приложений, чувствительных к производительности, используйте 'line'.
async function processLineByLine() {
const rl = readline.createInterface({
// ...
});
for await (const line of rl) {
// Each line in the readline input will be successively available here as
// `line`.
}
} readline.createInterface() начнёт потреблять поток ввода при вызове. Наличие асинхронных операций между созданием интерфейса и асинхронной итерацией может привести к пропускам строк.
rl.line
Текущие данные ввода, обрабатываемые узлом.
Это может быть использовано при сборе ввода с потока TTY для получения текущего значения, которое было обработано до сих пор, перед тем как будет выброшено событие line. После того, как событие line было выброшено, это свойство будет пустой строкой.
Обратите внимание, что изменение значения во время выполнения экземпляра может иметь непредвиденные последствия, если rl.cursor также не контролируется.
Если вы не используете поток TTY для ввода, используйте событие 'line'.
Один из возможных вариантов использования:
const values = ['lorem ipsum', 'dolor sit amet'];
const rl = readline.createInterface(process.stdin);
const showResults = debounce(() => {
console.log(
'\n',
values.filter((val) => val.startsWith(rl.line)).join(' ')
);
}, 300);
process.stdin.on('keypress', (c, k) => {
showResults();
});
rl.cursor
Положение курсора относительно rl.line.
Это отслеживает, где текущий курсор находится в строке ввода при чтении ввода с потока TTY. Положение курсора определяет часть строки ввода, которая будет изменена при обработке ввода, а также столбец, в котором будет отображаться терминальный курсор.
rl.getCursorPos()
- Возвращает: <Объект>
Возвращает реальное положение курсора относительно подсказки ввода + строки. В расчёты включены длинные строки ввода (перенос строк) и многострочные подсказки.
readline.clearLine(stream, dir[, callback])
-
stream<stream.Writable> -
dir<число>-
-1: влево от курсора -
1: вправо от курсора -
0: вся строка
-
-
callback<Функция> Вызывается после завершения операции. - Возвращает: <логическое>
falseеслиstreamхочет, чтобы вызывающий код ожидал, пока событие'drain'будет выброшено, прежде чем продолжить запись дополнительных данных; в противном случаеtrue.
Метод readline.clearLine() очищает текущую строку заданного потока TTY в заданном направлении, идентифицируемом dir.
readline.clearScreenDown(stream[, callback])
-
stream<stream.Writable> -
callback<Функция> Вызывается после завершения операции. - Возвращает: <логическое>
falseеслиstreamхочет, чтобы вызывающий код ожидал, пока событие'drain'будет выброшено, прежде чем продолжить запись дополнительных данных; в противном случаеtrue.
Метод readline.clearScreenDown() очищает заданный поток TTY от текущего положения курсора вниз.
readline.createInterface(options)
-
options<Объект>-
input<stream.Readable> Поток Readable, за которым нужно следить. Эта опция обязательна. -
output<stream.Writable> Поток Writable, в который нужно записывать данные readline. -
completer<Функция> Дополнительная функция, используемая для автозаполнения Tab. -
terminal<булево>true, если потокиinputиoutputдолжны обрабатываться как TTY и к ним должны записываться управляющие коды ANSI/VT100. По умолчанию: проверкаisTTYна потокеoutputпри инициализации. -
history<строковый массив> Начальный список строк истории. Эта опция имеет смысл только еслиterminalустановлено значениемtrueпользователем или в ходе внутренней проверкиoutput, в противном случае механизм кэширования истории не будет инициализирован. По умолчанию:[]. -
historySize<число> Максимальное количество строк истории, которые сохраняются. Для отключения истории установите это значение в0. Эта опция имеет смысл только еслиterminalустановлено значениемtrueпользователем или в ходе внутренней проверкиoutput, в противном случае механизм кэширования истории не будет инициализирован. По умолчанию:30. -
removeHistoryDuplicates<булево> Еслиtrue, когда новая строка ввода добавляется в список истории и дублирует более старую, то более старая строка удаляется из списка. По умолчанию:false. -
prompt<строка> Строка приглашения для использования. По умолчанию:'> '. -
crlfDelay<число> Если задержка между\rи\nпревышаетcrlfDelayмиллисекунд, оба\rи\nбудут обрабатываться как отдельный ввод конца строки.crlfDelayбудет приведено к числу не меньше100. Его можно установить вInfinity, в этом случае\r, за которым следует\n, всегда будет рассматриваться как одна новая строка (что может быть разумным для чтения файлов с разделителем строк\r\n). По умолчанию:100. -
escapeCodeTimeout<число> Длительностьreadlineожидания символа (при чтении неоднозначной последовательности символов, которая может как образовать полную последовательность символов, используя прочитанный ввод, так и принять дополнительный ввод для завершения более длинной последовательности символов) в миллисекундах. По умолчанию:500. -
tabSize<целое число> Количество пробелов, которое равно значению табуляции (минимум 1). По умолчанию:8. -
signal<AbortSignal> Позволяет закрыть интерфейс с помощью AbortSignal. Прерывание сигнала вызовет внутреннеcloseна интерфейсе.
-
- Возвращает: <readline.Interface>
Метод readline.createInterface() создает новый экземпляр readline.Interface.
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
}); После создания экземпляра readline.Interface, наиболее распространённым случаем является прослушивание события 'line'.
rl.on('line', (line) => {
console.log(`Received: ${line}`);
}); Если terminal равно true для данного экземпляра, то поток output получит лучшую совместимость, если он определит свойство output.columns и излучит событие 'resize' на output, если или когда колонки изменятся (process.stdout делает это автоматически, если это TTY).
При создании readline.Interface с использованием stdin в качестве входных данных программа не завершится, пока не получит EOF (Ctrl+D на Linux/macOS, Ctrl+Z, за которым следует Return на Windows). Если вы хотите, чтобы ваша программа завершилась без ожидания ввода пользователя, вы можете unref() стандартный поток ввода:
process.stdin.unref();
Использование функции completer
Функция completer принимает в качестве аргумента текущую строку, введенную пользователем, и возвращает объект Array с 2 записями:
- Объект
Arrayс совпадающими элементами для автозаполнения. - Подстрока, которая использовалась для совпадения.
Например: [[substr1, substr2, ...], originalsubstring].
function completer(line) {
const completions = '.help .error .exit .quit .q'.split(' ');
const hits = completions.filter((c) => c.startsWith(line));
// Show all completions if none found
return [hits.length ? hits : completions, line];
} Функция completer может вызываться асинхронно, если она принимает два аргумента:
function completer(linePartial, callback) {
callback(null, [['123'], linePartial]);
}
readline.cursorTo(stream, x[, y][, callback])
-
stream<stream.Writable> -
x<число> -
y<число> -
callback<Функция> Вызывается после завершения операции. - Возвращает: <булево>
false, еслиstreamжелает, чтобы вызывающий код ожидал, пока событие'drain'будет излучено, прежде чем продолжать запись дополнительных данных; в противном случаеtrue.
Метод readline.cursorTo() перемещает курсор в указанную позицию в заданном потоке stream.
readline.emitKeypressEvents(stream[, interface])
-
stream<stream.Readable> -
interface<readline.Interface>
Метод readline.emitKeypressEvents() заставляет заданный поток Readable начать излучать события 'keypress' , соответствующие полученному вводу.
Необязательно, interface указывает экземпляр readline.Interface , для которого отключение автозаполнения происходит при обнаружении скопированного вставленного ввода.
Если stream является TTY, то он должен быть в режиме raw.
Это автоматически вызывается любым экземпляром readline при его создании, если stream является терминалом. Закрытие экземпляра readline не останавливает input от излучения событий 'keypress' .
readline.emitKeypressEvents(process.stdin); if (process.stdin.isTTY) process.stdin.setRawMode(true);
readline.moveCursor(stream, dx, dy[, callback])
-
stream<stream.Writable> -
dx<число> -
dy<число> -
callback<Функция> Вызывается после завершения операции. - Возвращает: <логическое значение>
falseеслиstreamхочет, чтобы вызывающий код ожидал, пока событие'drain'будет излучено перед продолжением записи дополнительных данных; в противном случаеtrue.
Метод readline.moveCursor() перемещает курсор относительно его текущего положения в данном TTY stream.
Пример: Мини-консоль
Следующий пример демонстрирует использование класса readline.Interface для реализации небольшой командной строки:
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: 'OHAI> '
});
rl.prompt();
rl.on('line', (line) => {
switch (line.trim()) {
case 'hello':
console.log('world!');
break;
default:
console.log(`Say what? I might have heard '${line.trim()}'`);
break;
}
rl.prompt();
}).on('close', () => {
console.log('Have a great day!');
process.exit(0);
}); Пример: Чтение файла потока построчно
Распространенный случай использования readline — это потребление входного файла построчно. Самый простой способ сделать это — использовать API fs.ReadStream, а также цикл for await...of:
const fs = require('fs');
const readline = require('readline');
async function processLineByLine() {
const fileStream = fs.createReadStream('input.txt');
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
// Note: we use the crlfDelay option to recognize all instances of CR LF
// ('\r\n') in input.txt as a single line break.
for await (const line of rl) {
// Each line in input.txt will be successively available here as `line`.
console.log(`Line from file: ${line}`);
}
}
processLineByLine(); В качестве альтернативы можно использовать событие 'line':
const fs = require('fs');
const readline = require('readline');
const rl = readline.createInterface({
input: fs.createReadStream('sample.txt'),
crlfDelay: Infinity
});
rl.on('line', (line) => {
console.log(`Line from file: ${line}`);
}); В настоящее время цикл for await...of может быть немного медленнее. Если важны как async / await поток, так и скорость, можно применить смешанный подход:
const { once } = require('events');
const { createReadStream } = require('fs');
const { createInterface } = require('readline');
(async function processLineByLine() {
try {
const rl = createInterface({
input: createReadStream('big-file.txt'),
crlfDelay: Infinity
});
rl.on('line', (line) => {
// Process the line.
});
await once(rl, 'close');
console.log('File processed.');
} catch (err) {
console.error(err);
}
})(); Сочетания клавиш TTY
| Сочетание клавиш | Описание | Примечания |
|---|---|---|
| Ctrl+Shift+Backspace | Удалить строку влево | Не работает в Linux, Mac и Windows |
| Ctrl+Shift+Delete | Удалить строку вправо | Не работает в Mac |
| Ctrl+C | Вызвать SIGINT или закрыть экземпляр readline | |
| Ctrl+H | Удалить влево | |
| Ctrl+D | Удалить вправо или закрыть экземпляр readline, если текущая строка пуста/EOF | Не работает в Windows |
| Ctrl+U | Удалить от текущей позиции до начала строки | |
| Ctrl+K | Удалить от текущей позиции до конца строки | |
| Ctrl+A | Перейти к началу строки | |
| Ctrl+E | Перейти к концу строки | |
| Ctrl+B | Назад на один символ | |
| Ctrl+F | Вперед на один символ | |
| Ctrl+L | Очистить экран | |
| Ctrl+N | Следующий элемент истории | |
| Ctrl+P | Предыдущий элемент истории | |
| Ctrl+Z | Перемещает запущенный процесс в фоновый режим. Введите fg и нажмите Enter, чтобы вернуться. | Не работает в Windows |
| Ctrl+W или Ctrl +Backspace | Удалить назад до границы слова | Ctrl+Backspace Не работает в Linux, Mac и Windows |
| Ctrl+Delete | Удалить вперед до границы слова | Не работает в Mac |
| Ctrl+Left arrow или Meta+B | Слово влево | Ctrl+Left arrow Не работает в Mac |
| Ctrl+Right arrow или Meta+F | Слово вправо | Ctrl+Right arrow Не работает в Mac |
| Meta+D или Meta +Delete | Удалить слово вправо | Meta+Delete Не работает в Windows |
| Meta+Backspace | Удалить слово влево | Не работает в Mac |
© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/dist/latest-v16.x/docs/api/readline.html