createReadStream несколько раз на одном и том же fd

В предыдущем вопросе казалось, что единственный способ выполнить чтение с произвольным доступом из файла в node.js — это использовать fs.createReadStream() с необязательными полями fd, start и end.

Это отлично работало в моих простейших тестах. Но в моем проекте мне нужно многократно читать с разных смещений бинарного файла. Это странным образом не удалось, поэтому я придумал минимальный тестовый пример:

var fs = require('fs');

fs.open('test.txt', 'r', function (err, fd) {
  if (err) {
    console.error('error opening file: ' + err);
  } else {
    fs.createReadStream(null, {fd: fd, start: 2, end: 5}).on('error', function (err) {
        throw e;
      }).on('close', function () {
        console.log('outer close');
      }).on('data', function (data) {
        console.log('outer data', data);
      }).on('end', function () {
        console.log('outer end');

        fs.createReadStream(null, {fd: fd, start: 0, end: 3}).on('error', function (err) {
            throw e;
          }).on('close', function () {
            console.log('inner close');
          }).on('data', function (data) {
            console.log('inner data', data);
          }).on('end', function () {
            console.log('inner end');

            // more code to execute after both reads
          });
      });
  }
});

Внутреннее событие end никогда не принимается. (Внешний close принимается непоследовательно, но мне не нужно прикреплять к нему код.)

Я уже реализовывал этот проект на Perl и даже на JavaScript как расширение для Firefox, но в Node.js это оказалось сложно. Это также проверка того, могу ли я начать использовать node.js в качестве языка сценариев общего назначения.


person hippietrail    schedule 24.12.2012    source источник


Ответы (1)


Проблема в том, что внешний ReadStream закроет fd после его использования, поэтому повторное использование его во втором ReadStream не удастся. Новейшая нестабильная версия Node на самом деле имеет autoClose вариантов для ReadStreams, но это еще не часть стабильной версии.

Настоящий ответ заключается в том, что информация, предоставленная вам в предыдущем вопросе, неверна. createReadStream реализован с использованием всех общедоступных API-интерфейсов, поэтому он не может делать ничего такого, чего не могли бы сделать и вы. В этом случае вы можете просто использовать fs.read с аргументом position.

var fs = require('fs');                                                         

fs.open('test.txt', 'r', function (err, fd) {                                   
  if (err) {                                                                    
    console.error('error opening file: ' + err);                                
  } else {                                                                      
    fs.read(fd, new Buffer(4), 0, 4, 2, function(err, bytesRead, data){        
      if (err) throw err;                                                       
      console.log('outer data', data);                                          

      fs.read(fd, new Buffer(3), 0, 3, 0, function(err, bytesRead, data2){   
        if (err) throw err;                                                     
        console.log('inner data', data2);                                       
        fs.close(fd);                                                           

        // more code to execute after both reads                                
      });                                                                       
    });                                                                         
  }                                                                             
});   
person loganfsmyth    schedule 24.12.2012
comment
Спасибо @loganfsmyth! Я ожидал такого варианта в fs.read(), но каким-то образом, когда я читал документы для него, мне удалось объединить параметры offset и position и только заметил, что offset смещен в буфере, где начнется чтение! Было бы здорово, если бы вы также ответили на предыдущий вопрос. - person hippietrail; 24.12.2012
comment
fs.read хорошо справляется с этой проблемой, но моя следующая проблема требовала как произвольного доступа, так и чтения строки за раз. Чтение строки намного легче достигается с помощью потоков, но поиск может быть выполнен только с помощью fs.read. Я не могу найти решение, позволяющее смешивать их с простым кодом. - person hippietrail; 02.01.2013
comment
@hippietrail Как потоки облегчают поиск новых строк? Потоки реализованы с помощью fs.read, поэтому вы сможете воспроизвести любую логику, которая у него есть. - person loganfsmyth; 02.01.2013
comment
Потоки не упрощают поиск новой строки, они упрощают сохранение неразрывной многобайтовой последовательности. Вы говорите, что я должен воспроизвести логику в Streams для моего собственного кода чтения строк с fs.read? Инженеры-программисты стараются не изобретать велосипед. Я посмотрел, и код для обработки последовательностей UTF-8 и т. д. действительно не особенно тривиален. Для меня это знак против node.js на данный момент как языка сценариев общего назначения. Я знаю, что это не было целью узла, но во многих других отношениях у него есть потенциал делать то, что делают Perl/Python/Ruby, но лучше. - person hippietrail; 03.01.2013
comment
Многобайтовая логика @hippietrail UTF8 уже доступна в модуле узла String Decoder. nodejs.org/api/string_decoder.html - person loganfsmyth; 03.01.2013
comment
Да, проблема в том, что когда вы fs.read используете два буфера фиксированной длины, один может заканчиваться первой половиной последовательности UTF-8, а следующий может начинаться с другой половины. Таким образом, вы не можете использовать строковый декодер для каждой половины отдельно. Вам придется проанализировать первые и последние несколько октетов в каждом блоке, чтобы увидеть, какую подстроку декодировать без искажений или как объединить их в новые буферы только с полными символами UTF-8 перед их декодированием. - person hippietrail; 04.01.2013
comment
@hippietrail Документы не очень ясны, но это то, что делает StringDecoder. Если вы передадите ему буфер, который заканчивается неполным символом, он вернет все допустимые символы до этого и буферизует неполный символ, чтобы добавить его перед тем, что передается в write() next. - person loganfsmyth; 04.01.2013
comment
О, спасибо за подсказку! Я сделаю своей хакерской задачей наивысший приоритет, чтобы проверить это! - person hippietrail; 04.01.2013
comment
Вы правы, документы и даже источник для StringDecoder совершенно непрозрачны. Я не думаю, что понял бы его полезность без вашей помощи. У меня сложилось впечатление, что часть английского языка в источнике была написана не носителями языка. Упоминание CESU-8 тоже немного настораживает. - person hippietrail; 05.01.2013