如何在 Node.js 中使用流处理文件

x33g5p2x  于2021-10-27 转载在 其他  
字(14.7k)|赞(0)|评价(0)|浏览(525)

介绍

计算中的流概念通常描述以稳定、连续的流传输数据。您可以使用流连续读取或写入源,从而无需一次将所有数据放入内存中。

使用流提供了两个主要优点。一是您可以有效地使用您的内存,因为您不必在开始处理之前将所有数据加载到内存中。另一个优点是使用流是省时的。您几乎可以立即开始处理数据,而不是等待整个有效负载。这些优点使流成为 I/O 操作中大数据传输的合适工具。文件是包含一些数据的字节集合。由于文件是 Node.js 中的常见数据源,因此流可以提供一种在 Node.js 中处理文件的有效方式。

Node.js 在 stream 模块中提供了一个流 API,这是一个核心的 Node.js 模块,用于处理流。所有 Node.js 流都是 EventEmitter 类的实例(有关更多信息,请参阅在 Node.js 中使用事件发射器)。它们发出不同的事件,您可以在数据传输过程中以不同的时间间隔侦听。本机 stream 模块提供了一个由不同函数组成的接口,用于侦听可用于读取和写入数据、管理传输生命周期和处理传输错误的事件。

Node.js 中有四种不同类型的流。他们是:

  • 可读流:您可以从中读取数据的流。
  • 可写流:您可以写入数据的流。
  • 双工流:您可以读取和写入的流(通常同时进行)。
  • 转换流:一种双工流,其中输出(或可写流)取决于输入(或可读流)的修改。

文件系统模块 (fs) 是一个原生的 Node.js 模块,用于操作文件和导航本地文件系统。它提供了几种方法来执行此操作。其中两个方法实现了流式 API。它们提供了使用流读取和写入文件的接口。使用这两种方法,您可以创建可读和可写的文件流。

在本文中,您将使用 fs.createReadStreamfs.createWriteStream 函数读取和写入文件。您还将使用一个流的输出作为另一个流的输入并实现自定义转换流。通过执行这些操作,您将学习使用流来处理 Node.js 中的文件。为了演示这些概念,您将编写一个命令行程序,其中的命令复制基于 Linux 的系统中的 cat 功能、将终端的输入写入文件、复制文件以及转换文件的内容。文件。

先决条件

要完成本教程,您需要:

  • Node.js 安装在您的开发机器上。本教程使用版本 14.10.0。有关在各种平台上的安装,请参阅我们的教程系列如何安装 Node.js 并创建本地开发环境。
  • Node.js 的基本知识,您可以在我们的如何在 Node.js 中编码系列中找到这些知识。
  • Node.js fs 模块的基础知识,可以在如何使用 Node.js 中的 fs 模块处理文件中找到。

步骤 1 — 设置文件处理命令行程序

在这一步中,您将使用基本命令编写命令行程序。这个命令行程序将演示您将在本教程后面学习的概念,您将在教程中使用这些命令和您将创建的函数来处理文件。

首先,创建一个文件夹以包含此程序的所有文件。在您的终端中,创建一个名为 node-file-streams 的文件夹:

mkdir node-file-streams

使用 cd 命令,将您的工作目录更改为新文件夹:

cd node-file-streams

接下来,在您喜欢的文本编辑器中创建并打开一个名为 mycliprogram 的文件。本教程使用 GNU nano,一个终端文本编辑器。要使用 nano 创建和打开文件,请键入以下命令:

nano mycliprogram

在您的文本编辑器中,添加以下代码以指定 shebang,存储来自 Node.js 进程的命令行参数数组,并存储应用程序应具有的命令列表。

节点文件流/mycliprogram

#!/usr/bin/env node

const args = process.argv;
const commands = ['read', 'write', 'copy', 'reverse'];

第一行包含一个shebang,它是程序解释器的路径。添加这一行告诉程序加载器使用 Node.js 解析这个程序。

在命令行上运行 Node.js 脚本时,Node.js 进程运行时会传递几个命令行参数。您可以使用 argv 属性或 Node.js process 访问这些参数。 argv 属性是一个数组,其中包含传递给 Node.js 脚本的命令行参数。在第二行中,将该属性分配给名为 args 的变量。

接下来,创建一个 getHelpText 函数来显示如何使用该程序的手册。将以下代码添加到您的 mycliprogram 文件中:
节点文件流/mycliprogram

...
const getHelpText = function() {
    const helpText = `
    simplecli is a simple cli program to demonstrate how to handle files using streams.
    usage:
        mycliprogram <command> <path_to_file>

        <command> can be:
        read: Print a file's contents to the terminal
        write: Write a message from the terminal to a file
        copy: Create a copy of a file in the current directory
        reverse: Reverse the content of a file and save its output to another file.

        <path_to_file> is the path to the file you want to work with.
    `;
    console.log(helpText);
}

getHelpText 函数打印出你创建的多行字符串ted 作为程序的帮助文本。帮助文本显示程序所需的命令行参数或参数。

接下来,您将添加控制逻辑来检查 args 的长度并提供适当的响应:
节点文件流/mycliprogram

...
let command = '';

if(args.length < 3) {
    getHelpText();
    return;
}
else if(args.length > 4) {
    console.log('More arguments provided than expected');
    getHelpText();
    return;
}
else {
    command = args[2]
    if(!args[3]) {
        console.log('This tool requires at least one path to a file');
        getHelpText();
        return;
    }
}

在上面的代码片段中,您创建了一个空字符串 command 来存储从终端接收到的命令。第一个 if 块检查 args 数组的长度是否小于 3。如果小于 3,则表示在运行程序时没有传递其他附加参数。在这种情况下,它将帮助文本打印到终端并终止。

else if 块检查 args 数组的长度是否大于 4。如果是,那么程序接收到的参数比它需要的要多。程序将打印一条与帮助文本一起显示此效果的消息并终止。

最后,在 else 块中,将第三个元素或 args 数组的第二个索引中的元素存储在 command 变量中。该代码还会检查 args 数组中是否存在第四个元素或索引 = 3 的元素。如果该项目不存在,它会向终端打印一条消息,指示您需要一个文件路径才能继续。

保存文件。然后运行应用程序:

./mycliprogram

您可能会收到类似于以下输出的 permission denied 错误:

Output-bash: ./mycliprogram: Permission denied

要修复此错误,您需要为文件提供执行权限,您可以使用以下命令执行此操作:

chmod +x mycliprogram

再次重新运行该文件。输出将类似于以下内容:

Outputsimplecli is a simple cli program to demonstrate how to handle files using streams.
usage:
    mycliprogram <command> <path_to_file>

    read: Print a file's contents to the terminal
    write: Write a message from the terminal to a file
    copy: Create a copy of a file in the current directory
    reverse: Reverse the content of a file and save it output to another file.

最后,您将部分实现之前创建的 commands 数组中的命令。打开 mycliprogram 文件并添加以下代码:

节点文件流/mycliprogram

...
switch(commands.indexOf(command)) {
    case 0:
        console.log('command is read');
        break;
    case 1:
        console.log('command is write');
        break;
    case 2:
        console.log('command is copy');
        break;
    case 3:
        console.log('command is reverse');
        break;
    default:
        console.log('You entered a wrong command. See help text below for supported functions');
        getHelpText();
        return;
}

每当您输入在 switch 语句中找到的命令时,程序都会为该命令运行适当的 case 块。对于这个部分实现,您将命令的名称打印到终端。如果该字符串不在您上面创建的命令列表中,程序将打印出一条带有帮助文本的消息。然后程序将终止。

保存文件,然后使用 read 命令和任意文件名重新运行程序:

./mycliprogram read test.txt

输出将类似于以下内容:

Outputcommand is read

您现在已经成功创建了一个命令行程序。在以下部分中,您将使用 createReadStream() 在应用程序中将 cat 功能复制为 read 命令。

步骤 2 — 使用 createReadStream() 读取文件

命令行应用程序中的 read 命令将从文件系统中读取文件并将其打印到终端,类似于基于 Linux 的终端中的 cat 命令。在本节中,您将使用 fs 模块中的 createReadStream() 实现该功能。

createReadStream 函数创建一个可读的流,它发出您可以监听的事件,因为它继承自 EventsEmitter 类。 data 事件就是这些事件之一。可读流每次读取一条数据时,都会发出 data 事件,释放一条数据。当与回调函数一起使用时,它会使用该数据或 chunk 调用回调,并且您可以在该回调函数中处理该数据。在这种情况下,您希望在终端中显示该块。

首先,将文本文件添加到您的工作目录以便于访问。在本节和随后的一些节中,您将使用一个名为 lorem-ipsum.txt 的文件。它是一个包含使用 Lorem Ipsum Generator 生成的约 1200 行 lorem ipsum 文本的文本文件,它托管在 GitHub 上。在您的终端中,输入以下命令将文件下载到您的工作目录:

wget https://raw.githubusercontent.com/do-community/node-file-streams/999e66a11cd04bc59843a9c129da759c1c515faf/lorem-ipsum.txt

要在命令行应用程序中复制 cat 功能,您需要导入 fs 模块,因为它包含您需要的 createReadStream 函数。为此,请打开 mycliprogram 文件并在 shebang 之后立即添加以下行:

节点文件流/mycliprogram

#!/usr/bin/env node

const fs = require('fs');

接下来,您将在 switch 语句下创建一个名为 read() 的函数,该函数带有一个参数:您要读取的文件的文件路径。此函数将从该文件创建一个可读流并侦听该流上的 data 事件。

节点文件流/mycliprogram

...
function read(filePath) {
    const readableStream = fs.createReadStream(filePath);

    readableStream.on('error', function (error) {
        console.log(`error: ${error.message}`);
    })

    readableStream.on('data', (chunk) => {
        console.log(chunk);
    })
}

该代码还通过侦听 error 事件来检查错误。发生错误时,会向终端打印错误消息。

最后,您应该将第一个案例块 case 0 中的 console.log() 替换为 read() 函数,如下面的代码块所示:
节点文件流/mycliprogram

...
switch (command){
    case 0:
        read(args[3]);
        break;
    ...
}

保存文件以保留新更改并运行程序:

./mycliprogram read lorem-ipsum.txt

输出将类似于以下内容:

Output<Buffer 0a 0a 4c 6f 72 65 6d 20 69 70 73 75 6d 20 64 6f 6c 6f 72 20 73 69 74 20 61 6d 65 74 2c 20 63 6f 6e 73 65 63 74 65 74 75 72 20 61 64 69 70 69 73 63 69 ... >
...
<Buffer 76 69 74 61 65 20 61 6e 74 65 20 66 61 63 69 6c 69 73 69 73 20 6d 61 78 69 6d 75 73 20 75 74 20 69 64 20 73 61 70 69 65 6e 2e 20 50 65 6c 6c 65 6e 74 ... >

根据上面的输出,可以看到数据是分块或分片读取的,这些数据片的类型是Buffer。为简洁起见,上面的终端输出只显示了两个chunk,省略号表示chun之间有几个缓冲区ks 显示在这里。文件越大,缓冲区或块的数量就越多。

要以人类可读的格式返回数据,您将通过将所需编码类型的字符串值作为第二个参数传递给 createReadStream() 函数来设置数据的编码类型。在 createReadStream() 函数的第二个参数中,添加以下突出显示的代码以将编码类型设置为 utf8
节点文件流/mycliprogram

...
const readableStream = fs.createReadStream(filePath, 'utf8')
...

重新运行程序将在终端中显示文件的内容。该程序从 lorem-ipsum.txt 文件中逐行打印文件中出现的 lorem ipsum 文本。

OutputLorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean est tortor, eleifend et enim vitae, mattis condimentum elit. In dictum ex turpis, ac rutrum libero tempus sed...

...

...Quisque nisi diam, viverra vel aliquam nec, aliquet ut nisi. Nullam convallis dictum nisi quis hendrerit. Maecenas venenatis lorem id faucibus venenatis. Suspendisse sodales, tortor ut condimentum fringilla, turpis erat venenatis justo, lobortis egestas massa massa sed magna. Phasellus in enim vel ante viverra ultricies.

上面的输出显示了打印到终端的文件内容的一小部分。当您将终端输出与 lorem-ipsum.txt 文件进行比较时,您将看到内容与文件相同,并且采用相同的格式,就像使用 cat 命令一样。

在本节中,您在命令行程序中实现了 cat 功能,以读取文件内容并使用 createReadStream 函数将其打印到终端。在下一步中,您将使用 createWriteStream() 根据来自终端的输入创建一个文件。

步骤 3 — 使用 createWriteStream() 写入文件

在本节中,您将使用 createWriteStream() 将来自终端的输入写入文件。 createWriteStream 函数返回一个可以写入数据的可写文件流。与上一步中的可读流一样,这个可写流发出一组事件,如 errorfinishpipe。此外,它还提供了 write 函数,用于将数据以块或位的形式写入流。 write 函数接受 chunk,它可以是字符串、Buffer<Uint8Array> 或任何其他 JavaScript 值。如果块是字符串,它还允许您指定编码类型。

要将终端的输入写入文件,您将在命令行程序中创建一个名为 write 的函数。在此函数中,您将创建一个从终端接收输入(直到用户终止它)并将数据写入文件的提示。

首先,您需要导入 mycliprogram 文件顶部的 readline 模块。 readline 模块是一个原生的 Node.js 模块,您可以使用它从一个可读流中接收数据,例如标准输入 (stdin) 或您的终端,一次一行。打开您的 mycliprogram 文件并添加突出显示的行:
节点文件流/mycliprogram

#!/usr/bin/env node

const fs = require('fs');
const readline = require('readline');

然后,在 read() 函数下面添加以下代码。

节点文件流/mycliprogram

...
function write(filePath) {
    const writableStream = fs.createWriteStream(filePath);

    writableStream.on('error',  (error) => {
        console.log(`An error occured while writing to the file. Error: ${error.message}`);
    });
}

在这里,您正在使用 filePath 参数创建可写流。此文件路径将是 write 字之后的命令行参数。如果出现任何问题(例如,如果您提供不存在的 filePath),您也会监听错误事件。

接下来,您将编写从终端接收消息的提示,并使用您之前导入的 readline 模块将其写入指定的 filePath。要创建 readline 界面、提示并侦听 line 事件,请更新 write 函数,如块中所示:
节点文件流/mycliprogram

...
function write(filePath) {
    const writableStream = fs.createWriteStream(filePath);

    writableStream.on('error',  (error) => {
        console.log(`An error occured while writing to the file. Error: ${error.message}`);
    });

    const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
        prompt: 'Enter a sentence: '
    });

    rl.prompt();

    rl.on('line', (line) => {
        switch (line.trim()) {
            case 'exit':
                rl.close();
                break;
            default:
                sentence = line + '\n'
                writableStream.write(sentence);
                rl.prompt();
                break;
        }
    }).on('close', () => {
        writableStream.end();
        writableStream.on('finish', () => {
            console.log(`All your sentences have been written to ${filePath}`);
        })
        setTimeout(() => {
            process.exit(0);
        }, 100);
    });
}

您创建了一个 readline 接口 (rl),它允许程序从终端逐行读取标准输入 (stdin) 并写入指定的 [[$97$] ] 字符串到标准输出 (stdout)。您还调用了 prompt() 函数将配置的 prompt 消息写入新行并允许用户提供额外的输入。

然后在 rl 接口上将两个事件侦听器链接在一起。第一个监听每次输入流收到行尾输入时发出的 line 事件。此输入可以是换行符 (\n)、回车符 (\r) 或两个字符一起 (\r\n),通常在您按下时出现ENTER 或返回 键在您的计算机上。因此,任何时候在终端中键入时按下这些键中的任何一个,都会发出 line 事件。回调函数接收一个包含单行输入 line 的字符串。

您修剪了该行并检查它是否是单词 exit。如果没有,程序将向 line 添加一个新行字符,并使用 .write() 函数将 sentence 写入 filePath。然后调用 prompt 函数来提示用户输入另一行文本。如果 lineexit,则程序调用 rl 接口上的 close 函数。 close 函数关闭 rl 实例并释放标准输入 (stdin) 和输出 (stdout) 流。

此函数将我们带到您在 rl 实例上侦听的第二个事件:close 事件。当您调用 rl.close() 时会发出此事件。将数据写入流后,您必须调用流上的 end 函数来告诉您的程序它不应再将数据写入可写流。这样做将确保数据完整ly 刷新到您的输出文件。因此,当您键入单词 exit 时,您将关闭 rl 实例并通过调用 end 函数来停止可写流。

为了向用户提供程序已成功将所有文本从终端写入指定的 filePath 的反馈,您监听了 writableStream 上的 finish 事件。在回调函数中,您将一条消息记录到终端以在写入完成时通知用户。最后,您在 100 毫秒后退出流程,以便为 finish 事件提供足够的时间来提供反馈。

最后,要在您的 mycliprogram 中调用此函数,请将 switch 语句中 case 1 块中的 console.log 语句替换为新的 [[$138$] ] 函数,如下所示:
节点文件流/mycliprogram

...
switch (command){
    ...

    case 1:
        write(args[3]);
        break;

    ...
}

保存包含新更改的文件。然后使用 write 命令在终端中运行命令行应用程序。

./mycliprogram write output.txt

Enter a sentence 提示符下,添加您想要的任何输入。输入几个条目后,键入 exit

输出将与此类似(显示您的输入而不是突出显示的行):

OutputEnter a sentence: Twinkle, twinkle, little star
Enter a sentence: How I wonder what you are
Enter a sentence: Up above the hills so high
Enter a sentence: Like a diamond in the sky
Enter a sentence: exit
All your sentences have been written to output.txt

使用您之前创建的 read 命令检查 output.txt 以查看文件内容。

./mycliprogram read output.txt

终端输出应包含您在命令中键入的所有文本,除了 exit。根据上面的输入,output.txt 文件有以下内容:

OutputTwinkle, twinkle, little star
How I wonder what you are
Up above the hills so high
Like a diamond in the sky

在此步骤中,您使用流写入文件。接下来,您将在命令行程序中实现复制文件的功能。

步骤 4 — 使用 pipe() 复制文件

在此步骤中,您将使用 pipe 函数创建使用流的文件副本。虽然还有其他方法可以使用流复制文件,但首选使用 pipe,因为您不需要管理数据流。

例如,使用流复制文件的一种方法是为文件创建一个可读流,在 data 事件上监听流,然后将流事件中的每个 chunk 写入一个文件副本的可写流。下面的代码片段显示了一个示例:
示例.js

const fs = require('fs');
const readableStream = fs.createReadStream('lorem-ipsum.txt', 'utf8');
const writableStream = fs.createWriteStream('lorem-ipsum-copy.txt');

readableStream.on('data', () => {
    writableStream.write(chunk);
});

writableStream.end();

这种方法的缺点是您需要管理可读流和可写流上的事件。

使用流复制文件的首选方法是使用 pipe。管道将水从水箱(输出)等水源输送到水龙头或水龙头(输入)。类似地,您可以使用 pipe 将数据从输出流定向到输入流。 (如果您熟悉基于 Linux 的 bash shell,管道 | 命令会将数据从一个流定向到另一个流。)

Node.js 中的管道提供了从源读取数据并将其写入其他地方的能力,而无需像使用第一种方法那样管理数据流。与之前的方法不同,您不需要管理可读流和可写流上的事件。因此,它是在使用流的命令行应用程序中实现复制命令的首选方法。

mycliprogram 文件中,您将添加一个新函数,当用户使用 copy 命令行参数运行程序时调用。 copy 方法将使用 pipe() 从输入文件复制到文件的目标副本。在 write 函数之后创建 copy 函数,如下所示:
节点文件流/mycliprogram

...
function copy(filePath) {
    const inputStream = fs.createReadStream(filePath)
    const fileCopyPath = filePath.split('.')[0] + '-copy.' + filePath.split('.')[1]
    const outputStream = fs.createWriteStream(fileCopyPath)

    inputStream.pipe(outputStream)

    outputStream.on('finish', () => {
        console.log(`You have successfully created a ${filePath} copy. The new file name is ${fileCopyPath}.`);
    })
}

在复制函数中,您使用 fs.createReadStream() 创建了一个输入或可读流。您还为目标生成了一个新名称,输出了文件的副本,并使用 fs.createWriteStream() 创建了一个输出或可写流。然后,您使用 .pipe() 将数据从 inputStream 传送到 outputStream。最后,您侦听了 finish 事件并在成功的文件副本上打印出一条消息。

回想一下,要关闭可写流,您必须在流上调用 end() 函数。当管道流时,当可读流 (inputStream) 发出 end 事件时,会在可写流 (outputStream) 上调用 end() 函数。可写流的 end() 函数发出 finish 事件,您侦听此事件以指示您已完成复制文件。

要查看此函数的运行情况,请打开 mycliprogram 文件并更新 switch 语句的 case 2 块,如下所示:
节点文件流/mycliprogram

...
switch (command){
    ...

    case 2:
        copy(args[3]);
        break;

    ...
}

switch 语句的 case 2 块中调用 copy 函数可确保当您使用 mycliprogram 命令运行 $179$ 程序时,所需的文件路径,copy 函数被执行。

运行 mycliprogram

./mycliprogram copy lorem-ipsum.txt

输出将类似于以下内容:

OutputYou have successfully created a lorem-ipsum-copy.txt copy. The new file name is lorem-ipsum-copy.txt.

node-file-streams 文件夹中,您将看到一个名为 lorem-ipsum-copy.txt 的新添加文件。

您已使用 pipe 成功地将复制功能添加到您的命令行程序中。在下一步中,您将使用流来修改文件的内容。

步骤 5 — 使用 Transform() 反转文件的内容

在本教程的前三个步骤中,您已经完成了使用 fs 模块的流。在本节中,您将使用本机 stream 模块中的 Transform() 类来修改文件流,该模块提供了一个转换流。您可以使用转换流来读取数据、操作数据并提供新数据作为输出。因此,输出是输入数据的“转换”。使用转换流的 Node.js 模块包括用于加密的 crypto 模块和用于压缩和解压缩文件的带有 gzipzlib 模块。

您将使用 Transform() 抽象类实现自定义转换流。您创建的转换流将逐行反转文件的内容,这将演示如何使用转换流根据需要修改文件的内容。

mycliprogram 文件中,您将添加一个 reverse 函数,当用户传递 reverse 命令行参数时,程序将调用该函数。

首先,您需要在其他导入下方的文件顶部导入 Transform() 类。添加突出显示的行,如下所示:
mycliprogram

#!/usr/bin/env node
...
const stream = require('stream');
const Transform = stream.Transform || require('readable-stream').Transform;

在早于 v0.10 的 Node.js 版本中,缺少 Transform 抽象类。因此,上面的代码块包含 readable-streams polyfill,以便该程序可以与早期版本的 Node.js 一起使用。如果 Node.js 版本是 > 0.10,则程序使用抽象类,如果不是,则使用 polyfill。

注意: 如果您使用的是 Node.js 版本 < 0.10,则必须运行 npm init -y 以创建 package.json 文件并使用 $206$ 到您要应用的 polyfill 的工作目录。

接下来,您将在 copy 函数下创建 reverse 函数。在该函数中,您将使用 filePath 参数创建一个可读流,为反向文件生成一个名称,并使用该名称创建一个可写流。然后创建 reverseStream,这是 Transform() 类的一个实例。当您调用 Transform() 类时,您传入了一个包含一个函数的对象。这个重要的函数是 transform 函数。

copy 函数下方,添加以下代码块以添加 reverse 函数。
节点文件流/mycliprogram

...
function reverse(filePath) {
    const readStream = fs.createReadStream(filePath);
    const reversedDataFilePath = filePath.split('.')[0] + '-reversed.'+ filePath.split('.')[1];
    const writeStream = fs.createWriteStream(reversedDataFilePath);

    const reverseStream = new Transform({
        transform (data, encoding, callback) {
            const reversedData = data.toString().split("").reverse().join("");
            this.push(reversedData);
            callback();
        }
    });

    readStream.pipe(reverseStream).pipe(writeStream).on('finish', () => {
        console.log(`Finished reversing the contents of ${filePath} and saving the output to ${reversedDataFilePath}.`);
    });
}

transform 函数接收三个参数:dataencoding 类型和一个 callback 函数。在此函数中,您将数据转换为字符串、拆分字符串、反转结果数组的内容,并将它们重新连接在一起。此过程向后而不是向前重写数据。

接下来,您使用两个 pipe() 函数将 readStream 连接到 reverseStream,最后连接到 writeStream。最后,您侦听了 finish 事件以在文件内容完全反转时提醒用户。

您会注意到上面的代码使用了另一种语法来侦听 finish 事件。您没有在新行上监听 writeStreamfinish 事件,而是将 on 函数链接到第二个 pipe 函数。您可以在流上链接一些事件侦听器。在这种情况下,这样做与在 writeStream 上调用 on('finish') 函数具有相同的效果。

最后,将 switch 语句的 case 3 块中的 console.log 语句替换为 reverse()
节点文件流/mycliprogram

...
switch (command){
    ...

    case 3:
        reverse(args[3]);
        break;

    ...
}

要测试此功能,您将使用另一个包含按字母顺序排列的国家/地区名称的文件 (countries.csv)。您可以通过运行以下命令将其下载到您的工作目录。

wget https://raw.githubusercontent.com/do-community/node-file-streams/999e66a11cd04bc59843a9c129da759c1c515faf/countries.csv

然后你可以运行 mycliprogram

./mycliprogram reverse countries.csv

输出将类似于以下内容:

OutputFinished reversing the contents of countries.csv and saving the output to countries-reversed.csv.

countries-reversed.csv 的内容与 countries.csv 的内容进行比较以查看转换。现在每个名字都倒着写,名字的顺序也颠倒了(“阿富汗”写成“natsinahgfA”,最后出现,“津巴布韦”写成“ewbabmiZ”,最先出现)。

您已成功创建自定义转换流。您还创建了一个命令行程序,其中包含使用流进行文件处理的函数。

结论

流用于本地 Node.js 模块和各种执行输入/输出操作的 yarnnpm 包,因为它们提供了处理数据的有效方法。在本文中,您使用了各种基于流的函数来处理 Node.js 中的文件。您使用 readwritecopyreverse 命令构建了一个命令行程序。然后您在相应命名的函数中实现了这些命令中的每一个。为了实现这些功能,您使用了 fs 模块中的 createReadStreamcreateWriteStreampipe 等函数,以及 createInterface 模块中的 $250$ 函数。 [$251$]] 模块,最后是抽象的 Transform() 类。最后,您在一个小的命令行程序中将这些函数拼凑在一起。

下一步,您可以扩展您创建的命令行程序,以包含您可能希望在本地使用的其他文件系统功能。一个很好的例子可能是编写一个个人工具将数据从 .tsv 流源转换为 .csv 或在很想复制您在本文中使用的 wget 命令来从 GitHub 下载文件。

您编写的命令行程序本身处理命令行参数,并使用简单的提示来获取用户输入。 您可以按照如何处理 Node.js 脚本中的命令行参数和如何使用 Inquirer.js 创建交互式命令行提示,了解有关构建更强大且可维护的命令行应用程序的更多信息。

此外,Node.js 提供了关于您的用例可能需要的各种 Node.js stream module 类、方法和事件的大量文档。

相关文章