我们使用Node.js处理大型文件和原始数据集。



这篇文章是全栈软件工程师Paid Nidrinhouse撰写的原始文章的翻译。 它的主要专业是JavaScript,但是Paige还研究其他语言和框架。 他与读者分享他的经验。 顺便说一下,这篇文章对初学者将很有趣。

最近,我面临着一个令我感兴趣的任务-有必要从美国联邦选举委员会的大量非结构化文件中提取某些数据。 我没有对原始数据进行过多的工作,因此我决定接受挑战并承担这项任务。 作为解决此问题的工具,我选择了Node.js。

Skillbox建议: 前端开发人员职业在线课程。

我们提醒您: 对于所有“哈勃”读者来说,使用“哈勃”促销代码注册任何Skillbox课程时均可享受10,000卢布的折扣。

任务描述了四点:
  • 程序应计算文件中的总行数。
  • 每八列包含一个人的名字。 您需要加载此数据并使用文件中包含的所有名称创建一个数组。 必须显示第432和43,243名。
  • 第五列包含志愿者的捐赠日期。 计算每个月有多少总捐款,并打印总结果。
  • 每八列包含一个人的名字。 通过仅选择名字而不选择名字来创建数组。 找出最常发现的名字和次数?

(原始任务可以在此链接上查看 。)

您需要使用的文件是2.55 GB的常规.txt。 还有一个包含主文件各部分的文件夹(您可以在其上调试程序,而不必分析整个庞大的数组)。

Node.js上的两种可能的解决方案


原则上,处理大文件不会使JavaScript专家感到恐惧。 另外,这是Node.js的主要功能之一。 有几种读取和写入文件的解决方案。

熟悉的是fs.readFile()。 它允许您读取整个文件,并将其放入内存,然后使用Node。

另一种选择是fs.createReadStream(),该函数传递的数据类似于使用其他语言(例如,Python或Java)进行组织的方式。

我选择的解决方案


由于我需要计算总行数并解析数据以解析名称和日期,因此我决定停止第二种选择。 在这里,我可以使用rl.on('line',...)函数从行中获取必要的数据。

Node.js CreateReadStream()和ReadFile()代码

下面是我使用Node.js和fs.createReadStream()函数编写的代码。



最初,我需要进行所有设置,意识到导入数据需要Node.js函数,例如fs(文件系统),readline和stream。 接下来,我能够与readLine.createInterface()一起创建上游和下游。 生成的代码使得可以逐行解析文件,并获取必要的数据。

此外,我添加了一些变量和注释来处理特定数据。 它们是lineCount,dupeNames和名称,donation和firstNames的数组。

在rl.on('line',...)函数中,我能够逐行设置文件解析。 因此,我为每行输入lineCount变量。 我使用JavaScript split()方法通过将名称添加到我的名称数组中来解析名称。 接下来,我仅分隔没有姓氏的名称,同时突出显示例外,例如双名的存在,名称中间的缩写等。 接下来,我将年和日期与数据列分开,将所有这些都转换为YYYY-MM格式,并将dateDonationCount添加到数组中。

在rl.on('close',...)函数中,我使用console.log中接收到的信息对添加到数组的数据进行了所有转换。

需要lineCount和名称来确定第432和43,243的名称;此处无需进行任何转换。 但是,确定数组中最常用的名称以及确定捐赠数量是更加复杂的任务。

为了识别最常用的名称,我必须为每个名称(键)和对Object.entries的引用数创建一个值对的对象。 (值),然后使用ES6函数将其全部转换为数组数组。 之后,对名称进行排序并确定重复最多的任务就不再困难了。

通过捐赠,我做了大约相同的技巧:我创建了一个值对对象和logDateElements()函数,该函数允许我使用ES6插值显示每个月的键和值。 然后,我创建了新的Map(),将dateDonations对象转换为metamarray,并使用logDateElements()遍历每个数组。 (结果并不像开始时那样简单。)

但是它确实有效,我能够读取一个相对较小的400 MB文件,突出显示了必要的信息。

在那之后,我尝试了fs.createReadStream()-我在fs.readFile()上实现了任务,以查看区别。 这是代码:



您可以在此处查看整个解决方案。

使用Node.js的结果


事实证明该解决方案正在工作。 我将路径添加到readFileStream.js文件,并...看到节点服务器崩溃,并显示JavaScript堆内存不足错误。



事实证明,尽管一切正常,但是此解决方案尝试将文件的全部内容传输到内存,而2.55 GB的容量是不可能的。 Node可以同时使用1.5 GB内存运行,仅此而已。

因此,我没有做出任何决定。 它花了一个新的文件,甚至可以处理如此大量的文件。

新解决方案


事实证明,有必要使用流行的NPM模块EventStream。

研究了文档之后,我能够理解需要做什么。 这是程序代码的第三个版本。



该模块的文档指出,应在txt文件每一行的末尾使用\ n字符将数据流划分为单独的元素。

基本上,我唯一需要更改的是名称响应。 我无法将1.3亿个名称放入数组中-再次出现内存不足错误。 我通过计算第432位和第43,243位名称并将它们添加到我自己的数组中来解决了这个问题。 情况不是问条件,而是谁说你不能创造力?

第二轮。我们在工作中尝试该程序


是的,所有相同的文件,容量为2.55 GB,我们不由自主地遵循结果。



成功!

事实证明,仅Node.js不适合解决此类问题,其功能有所限制。 但是使用模块扩展它们,您可以使用如此大的文件。

Skillbox建议:

Source: https://habr.com/ru/post/zh-CN427415/


All Articles