Node.js path 模块
Node.js fs 模块 里读写文件都离不开路径,但路径在不同操作系统上写法不一样。Windows 用反斜杠 \ 分隔,比如 data\notes.txt,Mac 和 Linux 用正斜杠 /,比如 data/notes.txt。这一篇解决路径怎么统一的问题,path 模块就是干这个的,它也是 Node.js 内置模块。
为什么不要手动拼接路径
新手常犯的错误是直接拿字符串拼路径。
// 错误示范,别这么写
const dir = 'data';
const file = dir + '\\' + 'notes.txt'; // Windows 写法
const file2 = dir + '/' + 'notes.txt'; // Mac 写法
问题有两个。第一个是分隔符不兼容,写死 \\ 的代码在 Mac 上跑会得到错误路径。第二个是拼接容易错位,dir 末尾可能带斜杠也可能不带,拼出来的路径要么多一个斜杠要么少一个。正确做法是用 path.join。
path.join 与 path.resolve
join 规范拼接
const path = require('path');
console.log(path.join('data', 'notes.txt'));
// Windows 输出 data\notes.txt,Mac 和 Linux 输出 data/notes.txt
join 会根据当前操作系统自动选择分隔符,自动处理多余的斜杠。join('data', '/notes.txt') 也能得到正确结果,不会出现双斜杠。
resolve 解析绝对路径
const path = require('path');
// 基于当前工作目录解析出绝对路径
console.log(path.resolve('data', 'notes.txt'));
// 输出类似 C:\Users\aoweisi\Desktop\Study\data\notes.txt
resolve 把相对路径解析成绝对路径,起点是当前工作目录,也就是运行 node 命令时所在的目录。
| 方法 | 返回结果 | 典型场景 |
|---|---|---|
join |
规范化后的路径 | 拼接相对路径,配合 fs 读写文件 |
resolve |
基于工作目录的绝对路径 | 需要绝对路径时,比如传给子进程 |
解析路径
const path = require('path');
const p = 'C:\\Users\\aoweisi\\Desktop\\Study\\hello.js';
// 注意这是 Windows 写法,字符串里反斜杠要转义
console.log(path.basename(p)); // hello.js 文件名
console.log(path.basename(p, '.js')); // hello 去掉扩展名
console.log(path.dirname(p)); // C:\Users\aoweisi\Desktop\Study 所在目录
console.log(path.extname(p)); // .js 扩展名
const parts = path.parse(p);
console.log(parts);
// 一次拿到全部信息,含 root、dir、base、ext、name 五个字段
parse 返回一个对象,字段包括 root 根路径,dir 目录,base 完整文件名,ext 扩展名,name 去掉扩展名的文件名。
__dirname 与 __filename
Node.js 提供两个全局变量,直接拿当前文件的位置。
// app.js
console.log(__dirname); // 当前文件所在目录
console.log(__filename); // 当前文件的完整路径
这两个变量在 CommonJS 模块里可用,在 ES 模块里用不了,会直接报错。模块系统的两种写法区别见 Node.js 模块系统。
常见坑
第一,path.join 和 path.resolve 都不会检查路径是否存在,它们只是纯字符串处理,检查存在要用 fs 的 stat 或 access。第二,在字符串里写 Windows 路径要记得双写反斜杠 \\,单写会被当成转义符,比如 '\n' 是换行不是路径。第三,path.resolve 的基准是当前工作目录,不是文件所在目录,工作目录变了结果就变了。需要稳定基准时用 __dirname 配合。
const path = require('path');
// 以当前文件所在目录为基准,不受工作目录影响
const p = path.resolve(__dirname, 'data', 'notes.txt');
console.log(p);
实践
写一个 parsePath.js 工具函数,接收路径,返回目录、文件名、扩展名。
// parsePath.js
const path = require('path');
function parsePath(p) {
const parsed = path.parse(p);
return {
dir: parsed.dir,
file: parsed.base,
name: parsed.name,
ext: parsed.ext
};
}
// 测试 Windows 写法
const win = 'C:\\Users\\aoweisi\\Desktop\\Study\\a.txt';
console.log('Windows 路径', parsePath(win));
// 测试 POSIX 写法
const posix = '/home/aoweisi/data/b.js';
console.log('POSIX 路径', parsePath(posix));
运行。
node parsePath.js
// 输出
// Windows 路径 { dir: 'C:\\Users\\aoweisi\\Desktop\\Study', file: 'a.txt', name: 'a', ext: '.txt' }
// POSIX 路径 { dir: '/home/aoweisi/data', file: 'b.js', name: 'b', ext: '.js' }
有意思的地方在于,parse 不关心字符串里斜杠的方向,'C:\\Users\\aoweisi\\a.txt' 和 'C:/Users/aoweisi/a.txt' 解析结果完全一样。路径写成正斜杠在 Windows 上也通用,Node.js 会自动处理。
配合 Node.js fs 模块 使用,列出目录里所有 .txt 文件的完整代码。
// listTxt.js
const fs = require('fs').promises;
const path = require('path');
async function main(dir) {
const names = await fs.readdir(dir);
for (const name of names) {
if (path.extname(name) === '.txt') {
console.log(path.resolve(dir, name));
}
}
}
main('data').catch(err => console.error('失败', err.message));
路径问题解决后,下一步就是让程序通过网络被别人访问,进入 Node.js http 模块。