узел.js: readSync из stdin?

можно ли синхронно читать из stdin в узле.Яш? Потому что я пишу brainfuck для компилятора JavaScript в JavaScript (просто для удовольствия). Brainfuck поддерживает операцию чтения, которая должна быть реализована синхронно.

Я попытался это:

const fs = require('fs');
var c = fs.readSync(0,1,null,'utf-8');
console.log('character: '+c+' ('+c.charCodeAt(0)+')');

но это только производит этот вывод:

fs:189
  var r = binding.read(fd, buffer, offset, length, position);
              ^
Error: EAGAIN, Resource temporarily unavailable
    at Object.readSync (fs:189:19)
    at Object.<anonymous> (/home/.../stdin.js:3:12)
    at Module._compile (module:426:23)
    at Module._loadScriptSync (module:436:8)
    at Module.loadSync (module:306:10)
    at Object.runMain (module:490:22)
    at node.js:254:10

10 ответов


Я понятия не имею, когда это появилось, но это полезный шаг вперед:http://nodejs.org/api/readline.html

var readline = require('readline');

var rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  terminal: false
});

rl.on('line', function (cmd) {
  console.log('You just typed: '+cmd);
});

теперь я могу читать строку за раз из stdin. Счастливый день.


вы пробовали:

fs=require('fs');
console.log(fs.readFileSync('/dev/stdin').toString());

однако он будет ждать, пока весь файл будет прочитан, и не вернется на \n, как scanf или cin.


поиграв с этим немного, я нашел ответ:

process.stdin.resume();
var fs = require('fs');
var response = fs.readSync(process.stdin.fd, 100, 0, "utf8");
process.stdin.pause();

response будет массивом с двумя индексами, первый из которых-данные, введенные в консоль, а второй-длина данных, включая символ новой строки.

Это было довольно легко определить, когда вы console.log(process.stdin) который перечисляет все свойства, включая один помеченный fd что, конечно, является именем первого параметра для fs.readSync()

наслаждайтесь! : D


обновленная версия ответа Маркуса Поупа, что работает с узла.с JS версии v0.10.4:

обратите внимание:

  • В общем, узла интерфейсы трансляция все еще в движении (каламбур наполовину предназначен) и по-прежнему классифицируются как 2 - Unstable по состоянию на node.js v0.10.4.
  • разные платформы ведут себя немного по-разному; я посмотрел на OS X 10.8.3 и Windows 7: основное различие:синхронно чтение интерактивные stdin вход (путем ввода в терминал строка за строкой)работает только на Windows 7.

вот обновленный код, читать синхронно с stdin в 256 байт, пока вход:

var fs = require('fs');
var BUFSIZE=256;
var buf = new Buffer(BUFSIZE);
var bytesRead;

while (true) { // Loop as long as stdin input is available.
    bytesRead = 0;
    try {
        bytesRead = fs.readSync(process.stdin.fd, buf, 0, BUFSIZE);
    } catch (e) {
        if (e.code === 'EAGAIN') { // 'resource temporarily unavailable'
            // Happens on OS X 10.8.3 (not Windows 7!), if there's no
            // stdin input - typically when invoking a script without any
            // input (for interactive stdin input).
            // If you were to just continue, you'd create a tight loop.
            throw 'ERROR: interactive stdin input not supported.';
        } else if (e.code === 'EOF') {
            // Happens on Windows 7, but not OS X 10.8.3:
            // simply signals the end of *piped* stdin input.
            break;          
        }
        throw e; // unexpected exception
    }
    if (bytesRead === 0) {
        // No more stdin input available.
        // OS X 10.8.3: regardless of input method, this is how the end 
        //   of input is signaled.
        // Windows 7: this is how the end of input is signaled for
        //   *interactive* stdin input.
        break;
    }
    // Process the chunk read.
    console.log('Bytes read: %s; content:\n%s', bytesRead, buf.toString(null, 0, bytesRead));
}

Я нашел библиотеку, которая должна быть в состоянии выполнить то, что вам нужно: https://github.com/anseki/readline-sync


важно: меня только что проинформировал узел.автор JS, которые .fd является недокументированным и служит средством для внутренней отладки. Поэтому код не должен ссылаться на это и должен вручную открывать дескриптор файла с помощью fs.open/openSync.

В Узел.js 6, Также стоит отметить, что создание экземпляра Buffer через его конструктор с new устарело, из-за своей небезопасной природы. Следует использовать Buffer.alloc вместо этого:

'use strict';

const fs = require('fs');

// small because I'm only reading a few bytes
const BUFFER_LENGTH = 8;

const stdin = fs.openSync('/dev/stdin', 'rs');
const buffer = Buffer.alloc(BUFFER_LENGTH);

fs.readSync(stdin, buffer, 0, BUFFER_LENGTH);
console.log(buffer.toString());
fs.closeSync(stdin);

кроме того, следует открывать и закрывать файловый дескриптор только при необходимости; делать это каждый раз, когда вы хотите прочитать из stdin, приводит к ненужным накладным расходам.


Я написал небольшой дополнительный модуль C++, который делает Синхронное чтение на клавиатуре (https://npmjs.org/package/kbd).


я использовал этот обходной путь на узле 0.10.24 / linux:

var fs = require("fs")
var fd = fs.openSync("/dev/stdin", "rs")
fs.readSync(fd, new Buffer(1), 0, 1)
fs.closeSync(fd)

этот код ожидает нажатия клавиши ENTER. Он считывает один символ из строки, если пользователь вводит его перед нажатием ENTER. Другие символы останутся в буфере консоли и будут считываться при последующих вызовах readSync.


function read_stdinSync() {
    var b = new Buffer(1024)
    var data = ''

    while (true) {
        var n = fs.readSync(process.stdin.fd, b, 0, b.length)
        if (!n) break
        data += b.toString(null, 0, n)
    }
    return data
}

Я написал модуль читать по одной строке за раз из файла или stdin. Модуль называется line-reader которые предоставляет ES6 *Generator function для итерации по одной строке за раз. вот пример кода (в TypeScript) из readme.md.

import { LineReader } from "line-reader"

// FromLine and ToLine are optional arguments
const filePathOrStdin = "path-to-file.txt" || process.stdin
const FromLine: number = 1 // default is 0
const ToLine: number = 5 // default is Infinity
const chunkSizeInBytes = 8 * 1024 // default is 64 * 1024

const list: IterableIterator<string> = LineReader(filePathOrStdin, FromLine, ToLine, chunkSizeInBytes)

// Call list.next to iterate over lines in a file
list.next()

// Iterating using a for..of loop
for (const item of list) {
   console.log(item)
}

помимо вышеуказанного кода, Вы также можете взглянуть на на РЕПО.

Примечание:
линия-ридер модуль не читает все вещи в памяти, вместо этого он использует генератор функция для генерации линий async или sync.