Node 实现 CSV 行数限制预览

背景

在中后台场景经常会使用 CSV 文件,本文面向的 CSV 文件的预览和截断场景。即数据来源可能是一个很大的 CSV,但我们只需要一小部分数据进行预览/操作,如果采用传统的方式,将数据全部下载然后加载到内存中可能会导致内存溢出浪费带宽的情况。

为此很多时候需要对 CSV 的读取进行限制,如限制读取前 1W 行数据。

本文使用的工具包已开源 https://github.com/onechunlin/csv-row-limit

前置知识

本文例子基于 Node 版本 v16.13.0

在正式讲如何进行 CSV 行数限制之前,你需要对 Node 的 BufferStreamreadline 模块有一定的了解。

Buffer

Buffer 是一种类似于数组的数据结构,用于处理二进制数据。可以简单的将 Buffer 视为整数数组,每个整数代表一个数据字节(Unicode 码)

const buf = Buffer.from('Hey!')
console.log(buf[0]) //72
console.log(buf[1]) //101
console.log(buf[2]) //121

这些数字是 Unicode 码,用于标识 buffer 位置中的字符(H => 72、e => 101、y => 121)。

Stream(流)

流是为 Node.js 应用程序提供动力的基本概念之一。它是一种以高效的方式处理读/写文件、网络通信、或任何类型的端到端的信息交换。

在传统的方式中,当告诉程序读取文件时,这会将文件从头到尾读入内存,然后进行处理。

normal.gif

使用流,则可以逐个片段地读取并处理,而无需全部保存在内存中。

stream.gif

使用示例

一个典型的例子是从磁盘读取文件。使用 Node.js 的 fs 模块,可以读取文件,并在与 HTTP 服务器建立新连接时通过 HTTP 提供文件:

const http = require('http')
const fs = require('fs')

const server = http.createServer(function(req, res) {
   
  fs.readFile(__dirname + '/data.txt', (err, data) => {
   
    res.end(data)
  })
})
server.listen(3000)

readFile() 读取文件的全部内容,并在完成时调用回调函数。回调中的 res.end(data) 会返回文件的内容给 HTTP 客户端。

如果文件很大,则该操作会花费较多的时间。 以下是使用流编写的相同内容:

const http = require('http')
const fs = require('fs')

const server = http.createServer((req, res) => {
   
  const stream = fs.createReadStream(__dirname + '/data.txt')
  stream.pipe(res)
})
server.listen(3000)

当要发送的数据块已获得时就立即开始将其流式传输到 HTTP 客户端,而不是等待直到文件被完全读取。

上面的示例使用了 stream.pipe(res) 这行代码:在文件流上调用 pipe() 方法。它获取来源流,并将其通过管道传输到目标流。在 HTTP 请求中,req可读流res可写流,所以这里实现了边读文件边向 HTTP 响应里写数据,大大提高了大文件读取时效率。

readline 模块

从版本 7 开始,Node.js 提供了 readline 模块来执行以下操作:每次一行地从可读流(例如 process.stdin 流,在程序执行期间该流就是终端输入)获取输入,并输出到可写流(例如 process.stdout 流,在程序执行期间该流就是终端输出)。

const readline = require('readline').createInterface({
   
  input: process.stdin,
  output: process.stdout
})

readline.question(`你叫什么名字?`, name => {
   
  console.log(`你好 ${
     name}!`)
  readline.close()
})

这段代码会询问用户名,当输入了文本并且用户按下回车键时,则会发送问候语。

readline-result.gif

每当 input 流接收到行尾输入(\n、\r 或 \r\n)时,则会触发 line 事件。 这通常发生在用户按下 回车 或 返回 时。

如果从流中读取了新数据并且该流在没有最终行尾标记的情况下结束,也会触发 line 事件。大白话讲就是如果最后一行不是空行,也会触发 line 事件。

readline.on('line', (row) => {
   
  console.log(`Received: ${
     row}`);
});

了解了 BufferStreamreadline 模块之后我们就可以实现我们限制读取 CSV 行数的需求了。

代码实现

数据 Mock

进行 CSV 代码读取实现之前,我们先来造一个 100W 行数据的 CSV 文件,这里为了简单只造一列数据:0 - 10 亿之间的随机数。

const csvStr = new Array(1000000).fill(0).reduce((prev) => {
   
  /**
   * ~~ 为双取反位运算,作用为给数值取整,作用和 Math.floor 类似,因为
   * 是位运算速度比 Math.floor 快,但是日常还是建议使用 Math.floor
   */
  prev += `${
     ~~(Math.random() * 1000000000)}\n`;
  return prev;
}, "");

fs.writeFile(
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值