简体   繁体   English

如何避免 Deno TextLineStream 中的最后一个空行

[英]How to avoid last blank line in Deno TextLineStream

I run the following code,我运行以下代码,

import { TextLineStream } from "https://deno.land/std@0.136.0/streams/mod.ts";
const cmd1 = new Deno.Command("echo", {args:["foo\n\nbar"], stdout: "piped"}).spawn();
for await (const chunk of cmd1.stdout.pipeThrough(new TextDecoderStream).pipeThrough(new TextLineStream)) {
    console.log("result: ", chunk);
}

got得到

result:  foo
result:
result:  bar
result:

want

result:  foo
result:
result:  bar

How can I detect the end of a stream and avoid a blank line at the end?如何检测流的结尾并避免结尾出现空行?
Thank you for your help.谢谢您的帮助。

The type of cmd1.stdout in your question is ReadableStream<Uint8Array> .您问题中cmd1.stdout的类型是ReadableStream<Uint8Array> After piping this through the other transform stream classes, the resulting value is ReadableStream<string> .通过其他转换流类进行管道传输后,结果值为ReadableStream<string> When using a for await...of loop, each yielded value is simply a line ( string ), so you don't have an intrinsic reference for determining the end of stream within the loop in order to make decisions.当使用for await...of循环时,每个产生的值只是一行 ( string ),因此您没有内部参考来确定循环内流的结尾以便做出决定。 (This would still be true even if you were to manually iterate the stream's AsyncIterableIterator<string> .) Once the stream ends and the loop is done, it's already too late to go back and un-handle the final (empty) line. (即使您要手动迭代流的AsyncIterableIterator<string> ,这仍然是正确的。)流结束并且循环完成后,返回并取消处理最后的(空)行已经太晚了。

However, you can deal with this by storing each previous line in a variable outside the loop, and using that previous line inside the loop (instead of the current line).但是,您可以通过将前一行存储在循环外的变量中并在循环内使用前一行(而不是当前行)来处理此问题。 Then, after the loop ends, check to see whether or not the final previous line is empty — if it isn't, just use it like you did the other lines — if it is, then you can just ignore it.然后,在循环结束后,检查上一行的最后一行是否为空——如果不是,就像使用其他行一样使用它——如果是,那么你可以忽略它。

Below I've included a self-contained example that you can run on any platform without permissions and see the same results — it's based on the data in your question and compares the exact input you showed with a couple of similar inputs with different kinds of endings for juxtaposition.下面我提供了一个独立的示例,您可以在没有许可的情况下在任何平台上运行并看到相同的结果——它基于您问题中的数据,并将您显示的确切输入与几个具有不同类型的类似输入进行比较并列的结局。 It also includes line numbers as a bonus — they are helpful as a reference mechanism when comparing against the default behavior of the TextLineStream .它还包括行号作为奖励——在与TextLineStream的默认行为进行比较时,它们作为参考机制很有帮助。 The interesting code is in the RefinedTextLineStream class, and you can simply use it in your own code like this to see the desired output that you described:有趣的代码在RefinedTextLineStream类中,您可以像这样在自己的代码中简单地使用它来查看您描述的所需输出:

 const stream = cmd1.stdout.pipeThrough(new TextDecoderStream()).pipeThrough(new TextLineStream()).pipeThrough(new RefinedTextLineStream()); for await (const [line] of stream) { console.log("result: ", line); }

Here's the reproducible example:这是可重现的例子:

so-74905946.ts : so-74905946.ts

import { TextLineStream } from "https://deno.land/std@0.170.0/streams/text_line_stream.ts";

type LineWithNumber = [line: string, lineNumber: number];

/** For use with Deno's std library TextLineStream */
class RefinedTextLineStream extends TransformStream<string, LineWithNumber> {
  #lineNumber = 0;
  #previous = "";

  constructor() {
    super({
      transform: (textLine, controller) => {
        if (this.#lineNumber > 0) {
          controller.enqueue([this.#previous, this.#lineNumber]);
        }
        this.#lineNumber += 1;
        this.#previous = textLine;
      },
      flush: (controller) => {
        if (this.#previous.length > 0) {
          controller.enqueue([this.#previous, this.#lineNumber]);
        }
      },
    });
  }
}

const exampleInputs: [name: string, input: string][] = [
  ["input from question", "foo\n\nbar\n"],
  ["input with extra final line feed", "foo\n\nbar\n\n"],
  ["input without final line feed", "foo\n\nbar"],
];

for (const [name, input] of exampleInputs) {
  console.log(`\n${name}: ${JSON.stringify(input)}`);

  const [textLineStream1, textLineStream2] = new File([input], "untitled")
    .stream()
    .pipeThrough(new TextDecoderStream())
    .pipeThrough(new TextLineStream())
    .tee();

  console.log("\ndefault:");
  let lineNumber = 0;
  for await (const line of textLineStream1) {
    lineNumber += 1;
    console.log(lineNumber, line);
  }

  console.log("\nskipping empty final line:");
  const stream = textLineStream2.pipeThrough(new RefinedTextLineStream());
  for await (const [line, lineNumber] of stream) console.log(lineNumber, line);
}

Running in the terminal:在终端运行:

% deno --version
deno 1.29.1 (release, x86_64-apple-darwin)
v8 10.9.194.5
typescript 4.9.4

% deno run so-74905946.ts  

input from question: "foo\n\nbar\n"

default:
1 foo
2 
3 bar
4 

skipping empty final line:
1 foo
2 
3 bar

input with extra final line feed: "foo\n\nbar\n\n"

default:
1 foo
2 
3 bar
4 
5 

skipping empty final line:
1 foo
2 
3 bar
4 

input without final line feed: "foo\n\nbar"

default:
1 foo
2 
3 bar

skipping empty final line:
1 foo
2 
3 bar

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM