Wie erstelle ich Streams aus einem String in Node.Js?

177

Ich verwende eine Bibliothek, ya-csv , die entweder eine Datei oder einen Stream als Eingabe erwartet, aber ich habe eine Zeichenfolge.

Wie konvertiere ich diese Zeichenfolge in einen Stream in Node?

Pathikrit
quelle

Antworten:

27

Ab Knoten 10.17 verfügt stream.Readable über eine fromMethode zum einfachen Erstellen von Streams aus beliebigen iterierbaren Elementen (einschließlich Array-Literalen):

const { Readable } = require("stream")

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

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

Beachten Sie, dass mindestens zwischen 10.17 und 12.3 eine Zeichenfolge selbst iterierbar ist, also Readable.from("input string")funktioniert, aber ein Ereignis pro Zeichen ausgibt . Readable.from(["input string"])gibt ein Ereignis pro Element im Array aus (in diesem Fall ein Element).

Beachten Sie auch, dass in späteren Knoten (wahrscheinlich 12.3, da in der Dokumentation angegeben ist, dass die Funktion damals geändert wurde) die Zeichenfolge nicht mehr in ein Array eingeschlossen werden muss.

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

Fizker
quelle
Laut stream.Readable.from werden beim Aufrufen von Readable.from (Zeichenfolge) oder Readable.from (Puffer) die Zeichenfolgen oder Puffer aus Leistungsgründen nicht iteriert, um mit der Semantik der anderen Streams übereinzustimmen.
abbr
Mein Fehler. Die Funktion wurde in 10.7 hinzugefügt und verhält sich wie ursprünglich beschrieben. Seit einiger Zeit müssen Zeichenfolgen nicht mehr in Arrays eingeschlossen werden (seit 12.3 werden nicht mehr alle Zeichen einzeln wiederholt).
Fizker
186

Da @substack mich in #node korrigiert hat, erleichtert die neue Streams-API in Node v10 dies:

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

… Danach können Sie frei pfeifen oder auf andere Weise an Ihren beabsichtigten Verbraucher weitergeben.

Es ist nicht so sauber wie der Resumer - Einzeiler , aber es vermeidet die zusätzliche Abhängigkeit.

( Update: In Version 0.10.26 bis Version 9.2.1 stürzt ein Aufruf pushdirekt von der REPL-Eingabeaufforderung mit einer not implementedAusnahme ab, wenn Sie ihn nicht festgelegt haben _read. Er stürzt nicht in einer Funktion oder einem Skript ab. Wenn Sie inkonsistent sind nervös, gehören die noop.)

Garth Kidd
quelle
6
Aus den Dokumenten (Link) : "Alle lesbaren Stream-Implementierungen müssen eine _readMethode zum Abrufen von Daten aus der zugrunde liegenden Ressource bereitstellen ."
Felix Rabe
2
@eye_mew müssen Sie zuerst benötigen ('stream')
Jim Jones
8
Warum schieben Sie nullin den Puffer des Streams?
Dopatraman
5
@dopatraman nullteilt dem Stream mit, dass er alle Daten gelesen und den Stream geschlossen hat
chrishiestand
2
Sieht so aus, als ob Sie es nicht so machen sollten. Zitieren der Dokumente : "Die readable.push()Methode soll nur von lesbaren Implementierern und nur innerhalb der readable._read()Methode aufgerufen werden."
Axel Rauschmayer
127

Verwenden Sie nicht die Antwort von Jo Liss. Es wird in den meisten Fällen funktionieren, aber in meinem Fall hat es mir gute 4 oder 5 Stunden Fehlersuche verloren. Hierfür sind keine Module von Drittanbietern erforderlich.

NEUE ANTWORT :

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

Dies sollte ein vollständig kompatibler lesbarer Stream sein. Sehen Sie hier für weitere Informationen auf , wie man richtig Ströme verwenden.

ALTE ANTWORT : Verwenden Sie einfach den nativen PassThrough-Stream:

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
})

Beachten Sie, dass das Ereignis 'close' nicht ausgegeben wird (was von den Stream-Schnittstellen nicht benötigt wird).

BT
quelle
2
@ Finn Sie brauchen die Parens nicht in Javascript, wenn es keine Argumente gibt
BT
benutze "var" 2018 nicht! aber const
stackdave
30

Erstellen Sie einfach eine neue Instanz des streamModuls und passen Sie sie Ihren Anforderungen an:

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

oder

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');
Zemirco
quelle
13
Dieser Code verstößt gegen Stream-Konventionen. pipe()soll zumindest den Zielstrom zurückgeben.
Greim
2
Das Endereignis wird nicht aufgerufen, wenn Sie diesen Code verwenden. Dies ist keine gute Möglichkeit, einen Stream zu erstellen, der allgemein verwendet werden kann.
BT
12

Edit: Garths Antwort ist wahrscheinlich besser.

Mein alter Antworttext bleibt unten erhalten.


Um einen String in einen Stream zu konvertieren, können Sie eine Pause durch Strom:

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

Beispiel:

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()
Jo Liss
quelle
Ich konnte die Lösung von zeMirco für meinen Anwendungsfall nicht zum Laufen bringen, funktionierte aber resumerrecht gut. Vielen Dank!
Mpen
Der @substack-Resumer-Vorschlag hat bei mir sehr gut funktioniert. Vielen Dank!
Garth Kidd
2
Resumer ist großartig, aber das "Automatische Wiederaufnahme des Streams bei nextTick" kann Überraschungen bringen, wenn Sie erwarten, dass Sie den Stream an unbekannte Verbraucher weitergeben können! Ich hatte einen Code, der einen Inhaltsstrom an eine Datei weiterleitete, wenn das Speichern von Metadaten in der Datenbank erfolgreich war. Das war ein lauernder Fehler, der erfolgreich war, als der Datenbankschreibvorgang sofort Erfolg hatte! Ich habe später Dinge überarbeitet, die sich in einem asynchronen Block befinden, und der Stream war nie lesbar. Lektion: Wenn Sie nicht wissen, wer Ihren Stream verbrauchen wird, halten Sie sich an die Technik through (). Pause (). Queue ('string'). End ().
Jolly Roger
1
Ich habe ungefähr 5 Stunden damit verbracht, meinen Code zu debuggen, weil ich den Resumer-Teil dieser Antwort verwendet habe. Es wäre toll, wenn Sie möchten .. entfernen Sie es
BT
10

Dafür gibt es ein Modul: https://www.npmjs.com/package/string-to-stream

var str = require('string-to-stream')
str('hi there').pipe(process.stdout) // => 'hi there' 
Lori
quelle
1
Ist das ein Wortspiel auf "es gibt eine App dafür"? ;)
masterxilo
1
Der Link im Kommentar ist nützlich: npmjs.com/package/string-to-stream
Dem Pilafian
Zu Ihrer Information Ich habe versucht, diese Bibliothek zum Schreiben von JSON auf Google Drive zu verwenden, aber es würde bei mir nicht funktionieren. Schrieb hier einen Artikel darüber: medium.com/@dupski/… . Auch als Antwort unten hinzugefügt
Russell Briggs
6

im Kaffeeskript:

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

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

benutze es:

new StringStream('text here').pipe(stream1).pipe(stream2)
xinthink
quelle
6

Eine andere Lösung besteht darin, die Lesefunktion an den Konstruktor von Readable zu übergeben (siehe lesbare Optionen für den Doc- Stream ).

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

Sie können nach der Verwendung an Rohr zum Beispiel

Philippe T.
quelle
Was ist der Zweck der Rückgabe am Ende?
Kirill Reznikov
"immer etwas (oder nichts) zurückgeben", dieses Beispiel aus der Dokumentation.
Philippe T.
Wenn in JS eine Funktion keine Rückgabe hat, entspricht dies Ihrer leeren Rückgabe. Könnten Sie bitte einen Link angeben, wo Sie ihn gefunden haben?
Kirill Reznikov
du solltest recht haben. Ich sagte das mehr für Best Practice. Ich möchte nichts zurückgeben, es ist kein Fehler. Also entferne ich die Linie.
Philippe T.
5

Ich hatte es satt, dies alle sechs Monate neu lernen zu müssen, und habe gerade ein npm-Modul veröffentlicht, um die Implementierungsdetails zu abstrahieren:

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

Dies ist der Kern des Moduls:

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;

strist das string, was beim Aufruf an den Konstruktor übergeben werden muss und vom Stream als Daten ausgegeben wird. optionssind die typischen Optionen, die gemäß der Dokumentation an einen Stream übergeben werden können .

Laut Travis CI sollte es mit den meisten Versionen von Node kompatibel sein.

Chris Allen Lane
quelle
2
Als ich dies anfangs veröffentlichte, habe ich den relevanten Code nicht angegeben, von dem mir gesagt wurde, dass er verpönt ist.
Chris Allen Lane
2

Hier ist eine ordentliche Lösung in 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...')
Russell Briggs
quelle
1

JavaScript ist vom Typ Ente. Wenn Sie also nur die API eines lesbaren Streams kopieren , funktioniert dies einwandfrei. In der Tat können Sie wahrscheinlich die meisten dieser Methoden nicht implementieren oder sie einfach als Stubs belassen. Alles, was Sie implementieren müssen, ist das, was die Bibliothek verwendet. Sie können die vorgefertigte EventEmitterKlasse von Node auch zum Behandeln von Ereignissen verwenden, sodass Sie dies nicht selbst implementieren müssen addListener.

So können Sie es in CoffeeScript implementieren:

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'

Dann könnten Sie es so verwenden:

stream = new StringStream someString
doSomethingWith stream
stream.send()
icktoofay
quelle
Ich TypeError: string is not a function at String.CALL_NON_FUNCTION (native)new StringStream(str).send()
verstehe
Nur weil JavaScript Enten-Typisierung verwendet, heißt das nicht, dass Sie das Rad neu erfinden sollten. Node bietet bereits eine Implementierung für Streams. Erstellen Sie einfach eine neue Instanz stream.Readablewie von @Garth Kidd vorgeschlagen.
Sukima
4
@Sukima: stream.Readable existierte nicht, als ich diese Antwort schrieb.
icktoofay