六维教程

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.joinpath.resolve 都不会检查路径是否存在,它们只是纯字符串处理,检查存在要用 fs 的 stataccess。第二,在字符串里写 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 模块

上一篇
Node.js fs 模块
下一篇
Node.js http 模块