Node.Jsで文字列からストリームを作成する方法は?


177

ファイルまたはストリームを入力として受け取るライブラリya-csvを使用していますが、文字列があります。

その文字列をノードでストリームに変換するにはどうすればよいですか?

回答:


27

ノード10.17以降、stream.Readableには、from任意の反復可能オブジェクト(配列リテラルを含む)からストリームを簡単に作成するメソッドがあります。

const { Readable } = require("stream")

const readable = Readable.from(["input string"])

readable.on("data", (chunk) => {
  console.log(chunk) // will be called once with `"input string"`
})

少なくとも10.17と12.3の間では、文字列自体が反復可能であるため、機能しますが、文字Readable.from("input string")ごとに1つのイベントを発行します。Readable.from(["input string"])配列内のアイテムごとに1つのイベント(この場合は1つのアイテム)を発行します。

また、後のノード(おそらく12.3ですが、ドキュメントには関数が変更されたと記載されているため)では、文字列を配列でラップする必要はありません。

https://nodejs.org/api/stream.html#stream_stream_readable_from_iterable_options


stream.Readable.fromによると、Readable.from(string)またはReadable.from(buffer)を呼び出すと、パフォーマンス上の理由から、他のストリームのセマンティクスと一致するように文字列またはバッファーが反復処理されません。
abbr

私の悪い。この関数は10.7で追加され、最初に説明した方法で動作しました。しばらくして、文字列を配列にラップする必要がなくなりました(12.3以降、各文字を個別に反復する必要がなくなりました)。
Fizker

186

以下のよう@substackが私を修正#node、新しいストリームAPIノードV10では、これが容易になります:

const Readable = require('stream').Readable;
const s = new Readable();
s._read = () => {}; // redundant? see update below
s.push('your text here');
s.push(null);

…その後、それを自由にパイプするか、または意図した消費者に渡すことができます。

これは、再開機能のワンライナーほどきれいではありませんが、余分な依存関係を回避します。

更新:これまでのv0.10.26からv9.2.1ではpush、REPLプロンプトから直接呼び出すと、not implemented設定しなかった場合は例外でクラッシュし_readます。関数またはスクリプト内ではクラッシュしません。緊張しますnoop


6
ドキュメントから(リンク):「すべての読み取り可能なストリーム実装は_read、基になるリソースからデータをフェッチするメソッドを提供する必要があります。」
Felix Rabe 14年

2
最初にrequire( 'stream')する必要がある@eye_mew
Jim Jones

8
なぜnullストリームのバッファにプッシュするのですか?
dopatraman

5
@dopatraman nullは、すべてのデータの読み取りが終了し、ストリームを閉じることをストリームに
通知

2
このようにしてはいけないようです。ドキュメントを引用:「このreadable.push()メソッドは、読み取り可能なインプリメンターによってのみ呼び出され、readable._read()メソッド内からのみ呼び出されることを意図しています。」
Axel Rauschmayer

127

Jo Lissの履歴書回答は使用しないでください。それはほとんどの場合に機能しますが、私の場合、それは私に4または5時間の良いバグ発見を失いました。これを行うためにサードパーティのモジュールは必要ありません。

新しい回答

var Readable = require('stream').Readable

var s = new Readable()
s.push('beep')    // the string you want
s.push(null)      // indicates end-of-file basically - the end of the stream

これは完全に準拠した読み取り可能なストリームである必要があります。ストリームを適切に使用する方法について詳しくは、こちらをご覧ください。

OLD ANSWER:ネイティブのPassThroughストリームを使用するだけです。

var stream = require("stream")
var a = new stream.PassThrough()
a.write("your string")
a.end()

a.pipe(process.stdout) // piping will work as normal
/*stream.on('data', function(x) {
   // using the 'data' event works too
   console.log('data '+x)
})*/
/*setTimeout(function() {
   // you can even pipe after the scheduler has had time to do other things
   a.pipe(process.stdout) 
},100)*/

a.on('end', function() {
    console.log('ended') // the end event will be called properly
})

'close'イベントは発行されないことに注意してください(これはストリームインターフェイスでは必要ありません)。


2
@Finn引数がない場合、javascriptで括弧は必要ありません
BT

2018年は「var」を使用しないでください。しかしconst
stackdave

30

streamモジュールの新しいインスタンスを作成し、必要に応じてそれをカスタマイズするだけです。

var Stream = require('stream');
var stream = new Stream();

stream.pipe = function(dest) {
  dest.write('your string');
  return dest;
};

stream.pipe(process.stdout); // in this case the terminal, change to ya-csv

または

var Stream = require('stream');
var stream = new Stream();

stream.on('data', function(data) {
  process.stdout.write(data); // change process.stdout to ya-csv
});

stream.emit('data', 'this is my string');

13
このコードは、ストリームの規則に違反します。pipe()少なくとも宛先ストリームを返すことになっています。
greim 2014年

2
このコードを使用する場合、終了イベントは呼び出されません。これは、一般的に使用できるストリームを作成する良い方法ではありません。
BT

12

編集: ガースの答えはおそらくより良いです。

以前の回答テキストは以下に保存されています。


文字列をストリームに変換するには、一時停止したストリームを使用できます。

through().pause().queue('your string').end()

例:

var through = require('through')

// Create a paused stream and buffer some data into it:
var stream = through().pause().queue('your string').end()

// Pass stream around:
callback(null, stream)

// Now that a consumer has attached, remember to resume the stream:
stream.resume()

zeMircoのソリューションを私のユースケースで機能させることができませんでしたが、resumerかなりうまくいきました。ありがとう!
mpen 2013

@substackレジューサーの提案は私にとって非常にうまくいきました。ありがとう!
ガース・キッド14

2
再開機能は素晴らしいですが、「nextTickでストリームを自動的に再開する」ことで、不明なコンシューマーにストリームを渡すことができると予想される場合は、驚きが生じる可能性があります。メタデータのdbの保存が成功した場合、コンテンツストリームをファイルにパイプするコードがいくつかありました。それは潜んでいるバグであり、db writeがすぐに成功を返したときにたまたま成功しました!後で、非同期ブロック内に配置するようにリファクタリングしましたが、ブームが発生し、ストリームを読み取ることができませんでした。レッスン:誰がストリームを消費するのかわからない場合は、through()。pause()。queue( 'string')。end()テクニックを使用してください。
ジョリーロジャー

1
この回答の再開部分を使用したため、コードのデバッグに約5時間かかりました。削除できます。削除してください
BT

10

そのためのモジュールがあります:https : //www.npmjs.com/package/string-to-stream

var str = require('string-to-stream')
str('hi there').pipe(process.stdout) // => 'hi there' 

1
これは「そのためのアプリがある」という冗談ですか?;)
masterxilo 2018年

1
コメントのリンクは便利なリンクです:npmjs.com/package/string-to-stream
Dem Pilafian

ちなみに、このライブラリを使用してJSONをgoogleドライブに書き込んでみましたが、うまくいきませんでした。これに関する記事をここに書いた:medium.com/@dupski/…。以下の回答としても追加
Russell Briggs

6

コーヒースクリプトで:

class StringStream extends Readable
  constructor: (@str) ->
    super()

  _read: (size) ->
    @push @str
    @push null

これを使って:

new StringStream('text here').pipe(stream1).pipe(stream2)

6

別の解決策は、Readableのコンストラクターにread関数を渡すことです(cf doc stream readeable options

var s = new Readable({read(size) {
    this.push("your string here")
    this.push(null)
  }});

例としてs.pipeを使用した後


最後に戻る目的は何ですか?
Kirill Reznikov

「常に何かを返す(または何も返さない)」、これはドキュメントの例です。
フィリップT.

JSでは、関数に戻りがない場合、それは空の戻りと同じです。見つけたリンクを教えていただけませんか?
Kirill Reznikov

あなたは正しいはずです。私はベストプラクティスのためにもっと言った。何も返さないのは間違いではありません。だから私は行を削除します。
フィリップT.

5

これを6か月ごとに再学習する必要があることにうんざりしていたので、実装の詳細を抽象化するnpmモジュールを公開しました。

https://www.npmjs.com/package/streamify-string

これはモジュールのコアです:

const Readable = require('stream').Readable;
const util     = require('util');

function Streamify(str, options) {

  if (! (this instanceof Streamify)) {
    return new Streamify(str, options);
  }

  Readable.call(this, options);
  this.str = str;
}

util.inherits(Streamify, Readable);

Streamify.prototype._read = function (size) {

  var chunk = this.str.slice(0, size);

  if (chunk) {
    this.str = this.str.slice(size);
    this.push(chunk);
  }

  else {
    this.push(null);
  }

};

module.exports = Streamify;

strstring呼び出し時にコンストラクターに渡す必要があるであり、ストリームによってデータとして出力されます。ドキュメントoptionsに従ってストリームに渡される典型的なオプションです。

Travis CIによれば、ほとんどのバージョンのノードと互換性があるはずです。


2
私がこれを最初に投稿したとき、眉をひそめていると言われた関連コードを含めませんでした。
クリスアレンレーン

2

TypeScriptで整頓されたソリューションを次に示します。

import { Readable } from 'stream'

class ReadableString extends Readable {
    private sent = false

    constructor(
        private str: string
    ) {
        super();
    }

    _read() {
        if (!this.sent) {
            this.push(Buffer.from(this.str));
            this.sent = true
        }
        else {
            this.push(null)
        }
    }
}

const stringStream = new ReadableString('string to be streamed...')

1

JavaScriptはダックタイプであるため、読み取り可能なストリームのAPIをコピーするだけで問題なく動作します。実際、おそらくこれらのメソッドのほとんどを実装できないか、単にそれらをスタブのままにしておくことはできません。実装する必要があるのは、ライブラリが使用するものだけです。Nodeの事前に構築されたEventEmitterクラスを使用してイベントを処理することもできるため、addListener自分で実装する必要はありません。

CoffeeScriptで実装する方法は次のとおりです。

class StringStream extends require('events').EventEmitter
  constructor: (@string) -> super()

  readable: true
  writable: false

  setEncoding: -> throw 'not implemented'
  pause: ->    # nothing to do
  resume: ->   # nothing to do
  destroy: ->  # nothing to do
  pipe: -> throw 'not implemented'

  send: ->
    @emit 'data', @string
    @emit 'end'

次に、次のように使用できます。

stream = new StringStream someString
doSomethingWith stream
stream.send()

私はこれを手に入れました: TypeError: string is not a function at String.CALL_NON_FUNCTION (native) 私がそれを次のように使用するとnew StringStream(str).send()
pathikrit

JavaScriptがダックタイピングを使用しているからといって、ホイールを再発明する必要があるわけではありません。Nodeはすでにストリームの実装を提供しています。stream.Readable@Garth Kiddが提案するような新しいインスタンスを作成するだけです。
スキマ

4
@スキマ:この回答を書いたときにはstream.Readable 存在していませんでした
icktoofay 2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.