如何在Node.js中使用流处理文件
作为 Write for DOnations 计划的一部分,作者选择了 Girls Who Code 来接受捐赠。
介绍
计算中的流的概念通常描述以稳定、连续的流传输数据。 您可以使用流连续读取或写入源,从而无需一次将所有数据放入内存中。
使用流提供了两个主要优点。 一是您可以有效地使用内存,因为您不必在开始处理之前将所有数据加载到内存中。 另一个优点是使用流是省时的。 您几乎可以立即开始处理数据,而不是等待整个有效负载。 这些优势使流成为 I/O 操作中大数据传输的合适工具。 文件是包含一些数据的字节集合。 由于文件是 Node.js 中的常见数据源,因此流可以提供一种有效的方式来处理 Node.js 中的文件。
Node.js 在 stream
模块(一个核心 Node.js 模块)中提供了流 API,用于处理流。 所有 Node.js 流都是 EventEmitter
类的实例(有关此内容的更多信息,请参阅 在 Node.js 中使用事件发射器)。 它们发出不同的事件,您可以在数据传输过程中以不同的时间间隔收听。 原生的 stream
模块提供了一个由不同函数组成的接口,用于监听可用于读取和写入数据、管理传输生命周期和处理传输错误的事件。
Node.js 中有四种不同的流。 他们是:
- 可读流:可以从中读取数据的流。
- 可写流:可以写入数据的流。
- 双工流:您可以读取和写入的流(通常同时)。
- 转换流:输出(或可写流)依赖于输入(或可读流)的修改的双工流。
文件系统模块 (fs
) 是一个原生 Node.js 模块,用于操作文件和浏览本地文件系统。 它提供了几种方法来做到这一点。 其中两个方法实现了流 API。 它们提供了一个使用流读取和写入文件的接口。 使用这两种方法,您可以创建可读和可写的文件流。
在本文中,您将使用 fs.createReadStream
和 fs.createWriteStream
函数读取和写入文件。 您还将使用一个流的输出作为另一个流的输入并实现自定义转换流。 通过执行这些操作,您将学习使用流处理 Node.js 中的文件。 为了演示这些概念,您将编写一个命令行程序,其中的命令复制基于 Linux 的系统中的 cat
功能,将输入从终端写入文件,复制文件,并转换文件。
先决条件
要完成本教程,您需要:
- Node.js 安装在您的开发机器上。 本教程使用版本 14.10.0。 各种平台的安装,请看我们的系列教程【X63X】如何安装Node.js并创建本地开发环境【X132X】。
- Node.js的基础知识,你可以在我们的系列如何在Node.js中编码中找到。
- Node.js
fs
模块的基础知识,您可以在 如何使用 Node.js 中的 fs 模块处理文件中找到。
如果您想在浏览器中使用终端来试验本教程中的命令,请单击以下 Launch an Interactive Terminal! 按钮开始使用。
启动交互式终端!
第 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
函数打印出您创建的多行字符串作为程序的帮助文本。 帮助文本显示程序期望的命令行参数或参数。
接下来,您将添加控制逻辑来检查 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
类型。 为简洁起见,上面的终端输出只显示了两个块,省略号表示此处显示的块之间有几个缓冲区。 文件越大,缓冲区或块的数量就越多。
要以人类可读的格式返回数据,您将通过将所需编码类型的字符串值作为第二个参数传递给 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
函数返回一个可写文件流,您可以将数据写入该文件流。 与上一步中的可读流一样,此可写流发出一组事件,如 error
、finish
和 pipe
。 此外,它还提供 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
) 并写入指定的 [X177X ] 字符串到标准输出 (stdout
)。 您还调用了 prompt()
函数将配置的 prompt
消息写入新行并允许用户提供额外的输入。
然后,您在 rl
接口上将两个事件侦听器链接在一起。 第一个监听每次输入流接收到行尾输入时发出的 line
事件。 这个输入可以是换行符(\n
),回车符(\r
),或者两个字符一起(\r\n
),通常在按下在您的计算机上按 ENTER
或返回 ↵
键。 因此,任何时候在终端中键入时按下这些键中的任何一个,都会发出 line
事件。 回调函数接收包含单行输入 line
的字符串。
您修剪了这条线并检查它是否是单词 exit
。 如果没有,程序将在 line
中添加一个换行符,并使用 .write()
函数将 sentence
写入 filePath
。 然后调用 prompt 函数来提示用户输入另一行文本。 如果line
为exit
,程序调用rl
接口的close
函数。 close
函数关闭 rl
实例并释放标准输入 (stdin
) 和输出 (stdout
) 流。
此函数将我们带到您在 rl
实例上侦听的第二个事件:close
事件。 当您调用 rl.close()
时会发出此事件。 将数据写入流后,您必须在流上调用 end
函数来告诉您的程序不应再将数据写入可写流。 这样做将确保数据完全刷新到您的输出文件。 因此,当您键入单词 exit
时,您将关闭 rl
实例并通过调用 end
函数停止可写流。
为了向用户提供程序已成功将终端中的所有文本写入指定的 filePath
的反馈,您在 writableStream
上侦听了 finish
事件。 在回调函数中,您向终端记录了一条消息,以在写入完成时通知用户。 最后,您在 100 毫秒后退出进程,以便为 finish
事件提供足够的时间来提供反馈。
最后,要在 mycliprogram
中调用此函数,请将 switch
语句中 case 1
块中的 console.log
语句替换为新的 [X149X ] 函数,如下所示:
节点文件流/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
命令行参数运行程序时调用的新函数。 复制方法将使用 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
函数可确保在使用 copy
命令运行 mycliprogram
程序时,需要的文件路径,执行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
模块和用于压缩和解压缩文件的 zlib
模块和 gzip
。
您将使用 Transform()
抽象类实现自定义转换流。 您创建的转换流将逐行反转文件的内容,这将演示如何使用转换流根据需要修改文件的内容。
在 mycliprogram
文件中,您将添加一个 reverse
函数,当用户传递 reverse
命令行参数时程序将调用该函数。
首先,您需要在其他导入下方的文件顶部导入 Transform()
类。 添加突出显示的行,如下所示:
mycli程序
#!/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
文件并使用以下命令安装 polyfill npm install readable-stream
到你的工作目录,以便应用 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
函数接收三个参数:data
、encoding
类型和 callback
函数。 在此函数中,您将数据转换为字符串、拆分字符串、反转结果数组的内容,然后将它们重新连接在一起。 此过程向后而不是向前重写数据。
接下来,您使用两个 pipe()
函数将 readStream
连接到 reverseStream
,最后连接到 writeStream
。 最后,您侦听了 finish
事件,以在文件内容完全反转时提醒用户。
您会注意到上面的代码使用另一种语法来侦听 finish
事件。 您没有在新行上监听 writeStream
的 finish
事件,而是将 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 模块以及执行输入/输出操作的各种 yarn
和 npm
包,因为它们提供了一种有效的数据处理方式。 在本文中,您使用了各种基于流的函数来处理 Node.js 中的文件。 您使用 read
、write
、copy
和 reverse
命令构建了一个命令行程序。 然后,您在相应命名的函数中实现了这些命令中的每一个。 为了实现这些函数,您使用了 fs
模块中的 createReadStream
、createWriteStream
、pipe
等函数,createInterface
函数。 X145X] 模块,最后是抽象的 Transform()
类。 最后,您将这些函数拼凑在一个小型命令行程序中。
作为下一步,您可以扩展您创建的命令行程序以包含您可能希望在本地使用的其他文件系统功能。 一个很好的例子可能是编写个人工具将数据从 .tsv
流源转换为 .csv
或尝试复制您在本文中使用的 wget
命令从 GitHub 下载文件.
您编写的命令行程序自己处理命令行参数并使用简单的提示符来获取用户输入。 您可以通过遵循 如何处理 Node.js 脚本中的命令行参数 和 如何使用 Inquirer.js 创建交互式命令行提示来了解有关构建更健壮和可维护的命令行应用程序的更多信息。
此外,Node.js 提供了有关您的用例可能需要的各种 Node.js 流模块 类、方法和事件的大量文档。