news 2026/9/29 7:14:57

learnyounode filtered_ls 练习:用 `fs.readdir` 与 `path.extname` 实现异步目录文件过滤

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
learnyounode filtered_ls 练习:用 `fs.readdir` 与 `path.extname` 实现异步目录文件过滤
  • 教程
  • CLI

【免费下载链接】learnyounode

Learn You The Node.js For Much Win! An intro to Node.js via a set of self-guided workshops.

项目地址:https://gitcode.com/gh_mirrors/le/learnyounode
点击查看免费下载

learnyounode 的Filtered LS(filtered_ls)是入门 Node.js 文件系统异步编程的经典实战练习。本练习要求你编写一个接收「目录路径」与「文件扩展名」两个命令行参数的小程序,只输出目录中扩展名匹配的文件,且必须使用异步 I/O。读完本文,你将掌握process.argv的参数解析、fs.readdir异步回调、path.extname的扩展名判断,并能看懂 learnyounode 自带的自动化校验原理,写出可通过verify的正确解法。

练习目标与任务拆解

filtered_ls 的完整任务描述位于 exercises/filtered_ls/problem.ru.md(俄文版)与 exercises/filtered_ls/problem.md(英文版),核心要求可以拆解为四条:

  1. 输入两个命令行参数:第一个参数是目录路径(形如/path/to/dir/),第二个参数是需要过滤的扩展名(例如txt)。
  2. 过滤规则:只输出目录中以.+ 该扩展名结尾的文件。例如传入txt,就只列出.txt文件。注意第二个参数不会自带点号前缀,需要自行拼接'.' + ext。
  3. 逐行输出:每个匹配文件单独占一行,输出到控制台。
  4. 强制异步:必须使用异步 I/O(如fs.readdir),使用fs.readdirSync之类的同步方法会导致校验不通过。

英文原版中还额外强调了一个初学者常踩的坑:process.argv数组的前两个值被 Node 保留用于系统信息(process.argv[0]是 Node 可执行文件路径,process.argv[1]是脚本路径),因此你自定义的第一个参数要从process.argv[2]开始取——中文描述版未单独列出,但这是所有版本通用的约束。

核心前置知识

1. 用process.argv读取参数

Node 把启动脚本时传入的所有参数放进process.argv。由于前两项被系统占用,练习所需的目录和扩展名分别落在process.argv[2]和process.argv[3]:

const folder = process.argv[2] // 目录路径 const ext = '.' + process.argv[3] // 扩展名,手动补上点号

2.fs.readdir:异步读取目录列表

练习提示明确给出了fs.readdir()的用法:第一个参数是路径名,第二个参数是回调函数,回调签名固定为:

function callback (err, list) { /* ... */ }

其中list是目录下所有文件名的字符串数组。异步风格要求把后续处理全部写进回调,错误通过回调的第一个参数err暴露。fs模块的完整文档在 docs-nodejs/fs.html,其中fs.readdir一节对应本练习。

3.path.extname:提取文件扩展名

判断文件名是否匹配扩展名,最稳妥的方式不是字符串endsWith,而是用path模块的extname方法——它返回文件名(含点号)的扩展名部分,例如path.extname('learnyounode.txt')返回'.txt',与补好点号的过滤条件可以直接全等比较。path模块文档见 docs-nodejs/path.html。

参考解法:官方solution.js全量实现

仓库在 exercises/filtered_ls/solution/solution.js 给出了官方参考实现,正是练习提示所引导思路的直接落地:

'use strict' const fs = require('fs') const path = require('path') const folder = process.argv[2] const ext = '.' + process.argv[3] fs.readdir(folder, function (err, files) { if (err) return console.error(err) files.forEach(function (file) { if (path.extname(file) === ext) { console.log(file) } }) })

逐行解读:

  • ext = '.' + process.argv[3]在比较前为扩展名补点,与path.extname的返回格式对齐;
  • 回调第一行先做错误处理,出错时把错误打到console.error并提前返回,避免用未定义的files继续执行;
  • files.forEach遍历目录列表,path.extname(file) === ext命中即console.log(file)逐行输出;
  • 全程只有fs.readdir这一个文件系统调用,且是异步形式,天然满足「必须异步 I/O」的硬性要求。

运行方式(本仓库定位为教学引导工具,实际练习时把下面的filtered-ls.js换成你自己的文件):

# 假设要列出当前目录下所有 .md 文件 node filtered-ls.js . md

另一种常见写法:filter+forEach链式过滤

官方测试样例 test/filtered_ls/valid_01.js 提供了等价的链式写法,同样通过校验,可作为风格参考:

const fs = require('fs') const path = require('path') fs.readdir(process.argv[2], function (err, list) { if (err) { console.log(err) } list.filter(function (file) { return path.extname(file) === '.' + process.argv[3] }).forEach(function (file) { console.log(file) }) })

两种写法实质相同:先用Array.prototype.filter把不匹配的文件剔除,再对结果逐行打印。选哪种取决于个人风格,功能与校验结果一致。

校验机制源码解析:异步与同步如何被检测

学习本练习时最值得深挖的,是 learnyounode 到底如何判定「用了异步而没用同步」。答案藏在三处源码里。

1. 测试目录与测试数据

exercises/filtered_ls/exercise.js 的 setup 阶段会在系统临时目录创建_learnyounode_<pid>测试目录,并把 exercises/filtered_ls/file-list.json 中列出的 15 个文件写入其中——注意列表中既有learnyounode.txt、w00t.txt这样的.txt文件,也有api.html、README.md,以及md、dat、wrrrrongdat这种没有扩展名或扩展名不完整的文件,专门用来检验你的过滤逻辑是否准确:

[ "learnyounode.dat", "learnyounode.txt", "learnyounode.sql", "api.html", "README.md", "CHANGELOG.md", "LICENCE.md", "md", "data.json", "data.dat", "words.dat", "w00t.dat", "w00t.txt", "wrrrrongdat", "dat" ]

同时 setup 通过submissionArgs.unshift('md')和submissionArgs.unshift(testDir)为你的程序注入两个参数:目录路径和'md'扩展名。

2. 运行时包装:记录每一次fs调用

exercises/my_first_io/wrap.js 会以 phantom wrapper 的形式注入子进程,把fs、fs.promises(fsp)和util的所有方法包一层代理:每次调用都通过ctx.$captureStack捕获调用栈,若栈顶调用方正是参赛主程序,就把方法名记入ctx.fsCalls[m]。这样,你的程序**实际调用了哪些fs方法(无论同步还是异步)**会被完整记录。

3. verify 判定逻辑:同步即失败

exercise.js的 verify 处理器遍历exercise.wrapData.fsCalls中的方法名:

  • 方法名以Sync结尾(如readdirSync)→ 触发fail,提示使用了同步方法;
  • 否则(异步方法)→ 触发pass;
  • 如果既没有同步也没有异步调用 → 触发fail,提示完全没用到fs(对应 test/filtered_ls/invalid_02.js 的「未使用 fs」场景);
  • 最终结论是usedAsync && !usedSync:用了异步且没碰同步才算通过。

这也解释了为什么 test/filtered_ls/invalid_01.js(用fs.readdirSync的版本)必然被判定为失败——它的方法名命中/Sync$/正则。

常见错误与调试清单

错误类型表现原因与对策
用readdirSyncverify 报fail.sync同步方法名以Sync结尾,被校验器直接识别;改为fs.readdir(folder, callback)
过滤条件漏掉点号输出为空或匹配错误第二个参数不带.,需先'.' + ext再与path.extname(file)全等比较
用字符串拼接/endsWith判断可能误伤wrrrrongdat、dat等无扩展名文件扩展名比较应基于path.extname的规范返回
忘记err处理目录不存在时崩溃或打印异常回调首行if (err) return console.error(err)
完全没调用fsverify 报fail.unused练习要求必须使用异步 I/O,空实现无法通过

验证命令

练习自带校验器,编写完filtered-ls.js后执行:

$ learnyounode verify filtered-ls.js

verify会触发上文的完整链路:构建测试目录 → 注入两个参数 → 包装fs调用 → 比对结果与调用记录。若你的程序输出与预期文件列表一致、且只使用了异步方法,即判定通过。

小结

filtered_ls 表面上是「列文件 + 过滤扩展名」,实际上是一次对 Node.js 异步 I/O 心智模型的完整考验:从process.argv的参数定位,到fs.readdir的回调式错误处理,再到path.extname的规范化扩展名比较。通过阅读 solution.js、exercise.js 与 wrap.js,你不仅能写出正确解法,还能理解 learnyounode「用包装器记录fs调用、以方法名正则判定同步/异步」的自动化校验机制——这为后续my_first_io、make_it_modular等更复杂的练习打下了扎实基础。

  • 教程
  • CLI

【免费下载链接】learnyounode

Learn You The Node.js For Much Win! An intro to Node.js via a set of self-guided workshops.

项目地址:https://gitcode.com/gh_mirrors/le/learnyounode
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 7:13:47

Cadence Sigrity仿真实战:从TDR到PI的高速信号完整性分析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 7:11:36

Vue 3前端加密实战:六种加密方式原理与工程落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 7:10:34

回文数高精度加法与进制转换:字符串模拟30步解题全解析

看到题目名里的“回文数”&#xff0c;可能有人觉得这题简单&#xff1a;不就是判断一个数字正着读反着读一样吗&#xff1f;但等你真打开洛谷P1015或者信息学奥赛一本通1309&#xff0c;看到题目给的进制可以是2到16&#xff0c;数字最长能到100位&#xff0c;还要在30步内反复…

作者头像 李华