一个问题引发的“血案”
最近在学习node.js的fs模块时候,遇到了一个很奇怪的问题,读取当前目录下的一个文件,死活读取不到。废话不多说,直接看代码。
var fs = require('fs')
fs.readFile('./油炸日子.txt', function (err, data) {
console.log(data)
console.log(err)
})
当前的目录是这样的
├── node学习笔记
├── node基础知识
├── 1.path和几种常见的文文件路径.js
└── 油炸日子.txt
然后这时我们运行代码,看看输出的啥
console.log(data) // undefined
console.log(err) // { Error: ENOENT: no such file or directory, open './油炸日子.txt' errno: -2, code: 'ENOENT', syscall: 'open', path: './油炸日子.txt' }
是不是很奇怪,路径明明是对的啊,为什么找不到文件?这时我们先放下这个问题,先来了解下node中几种常见的文件路径,然后你就理解了。
node中几种常见的文件路径
node中常见的路径有这几类:__dirname、__filename、process.cwd()、./、../,前三个路径都是绝对路径后面两个是相对路径
,我们先来看看这个几个路径的都代表什么。
console.log(__dirname)
console.log(__filename)
console.log(process.cwd())
//这里为了方便对比,使用path.resole把./和../转化成绝对路径,下面内容会讲解path
console.log(path.resolve('./'))
console.log(path.resolve('../'))
输出的结果:
/Users/zxy/Documents/Note/代码类/node学习笔记/node基础知识
/Users/zxy/Documents/Note/代码类/node学习笔记/node基础知识/1.path和几种常见的文件路径.js
/Users/zxy/Documents/Note/代码类/node学习笔记
/Users/zxy/Documents/Note/代码类/node学习笔记
/Users/zxy/Documents/Note/代码类
看到这里是不是之前找不到文件的问题就明白了,path.resole('./)输出的路径是node学习笔记,但是node学习笔记文件夹里面确实没有油炸日子.txt文件,很自然就会报错。
下面我们来根据输出的内容判断上面几种路径分别代表什么意思:
__dirname: 返回当前执行文件所在上一级文件夹的位置。比如:当前执行文件的上一级文件夹是`node基础知识`
__filename: 返回当前执行文件的位置
process.cwd(): 返回执行node命令时所在文件夹位置。这里可能比较不好理解,为什么执node命令的文件夹不是`node基础知识`而是它上一级的`node学习笔记`,你可以这样理解:比如你在编辑器中打开了文件夹`node学习笔记`,然后运行了他里面`node基础知识`文件夹里面的js文件,那么node命令就输出到`node学习笔记`为止。简而言之就是:你在编辑器中打开了哪个文件夹并执行了node,那么这个文件夹就是执行node命令所在的位置。
./ : 同process.cwd()
看到这里是不是对上面找不到文件的报错更加理解了。文件路径我们了解了,接下来我们再来聊聊和路径必不可分的path模块吧。
node中的path模块
我们按照node中文网的api挨个了解下。node中文网关于path模块的方法已经写得很详细了,小伙伴们可以花点时间去看下。
获取文件的名称
path.basenaem(path,ext)
- path string类型,必填,传入非字符串类型会报错。
- ext string类型 可选填,文件的扩展名。传入非字符串类型会报错。
- 返回 string类型
path.basename('/Users/zxy/Documents/Note/代码类/node学习笔记/node基础知识/1.path和几种常见的文件路径.js')
// 返回:1.path和几种常见的文件路径.js
path.basename('/Users/zxy/Documents/Note/代码类/node学习笔记/node基础知识/1.path和几种常见的文件路径.js','.js')
//返回:1.path和几种常见的文件路径
path.basename('/Users/zxy/Documents/Note/代码类/node学习笔记/node基础知识/1.path和几种常见的文件路径.js','.html')
//返回:1.path和几种常见的文件路径.js
// 如果传入的ext和文件的扩展名匹配则不返回扩展名,否则返回。
获取文件的路径
path.dirname(path)
- path string类型
- 返回 string类型 path.dirname(path) 方法会返回 path 的目录名
console.log(path.dirname('/Users/zxy/Documents/Note/代码类/node学习笔记/node基础知识/1.path和几种常见的文件路径.js'))
// /Users/zxy/Documents/Note/代码类/node学习笔记/node基础知识
获取文件的扩展名
path.extname(path)
- path string类型
- 返回 string类型 path.extname() 方法会返回 path 的扩展名,即 path 的最后一部分中从最后一次出现 .(句点)字符直到字符串结束。 如果在 path 的最后一部分中没有 .,或者如果 path 的基本名称(参见 path.basename())除了第一个字符以外没有 .,则返回空字符串。 官方的例子已经很详细了,我这里就照搬过来了,小伙伴们一起来看下。
path.extname('index.html');
// 返回: '.html'
path.extname('index.coffee.md');
// 返回: '.md'
path.extname('index.');
// 返回: '.'
path.extname('index');
// 返回: ''
path.extname('.index');
// 返回: ''
path.extname('.index.md');
// 返回: '.md'
获取规范化的路径格式
path.normalize(path)
- path string类型
- 返回 string类型 path.normalize() 方法规范化给定的 path,解析 '..' 和 '.' 片段。
linux系统下
path.normalize('/Users/zxy//Documents//Note/代码类//node学习笔记/node基础知识/1.path和几种常见的文件路径.js/')
// /Users/zxy/Documents/Note/代码类/node学习笔记/node基础知识/1.path和几种常见的文件路径.js
// 这里会把多余的/变成一个/
window系统下
path.normalize('C:\\temp\\\\foo\\bar\\..\\')
// C:\\temp\\foo\\
路径组合
简单组合
path.join([...paths])
- ...paths string类型 路径片段的序列。
- 返回: string类型 path.join() 方法会将所有给定的 path 片段连接到一起(使用平台特定的分隔符作为定界符),然后规范化生成的路径。 长度为零的 path 片段会被忽略。 如果连接后的路径字符串为长度为零的字符串,则返回 '.',表示当前工作目录。
console.log(path.join('油', '炸', '日', '子'))
// 油/炸/日/子
console.log(path.join('油', '炸', '日', '子','..'))
// 油/炸/日
智能组合
path.resolve([...paths])
- ...paths string类型 路径或路径片段的序列。
- 返回: string类型 path.resolve() 方法会将路径或路径片段的序列解析为绝对路径。。层级关系是从左到右的.
1、给定的路径序列会从右到左进行处理,后面的每个 path 会被追加到前面,直到构造出绝对路径。
2、生成的路径会被规范化,并且尾部的斜杠会被删除(除非路径被解析为根目录)。
3、零长度的 path 片段会被忽略。
4、如果没有传入 path 片段,则 path.resolve() 会返回当前工作目录的绝对路径。
5、path.resolve将以/开始的路径片段作为根目录,在此之前的路径将会被丢弃,就像是在terminal中使用cd命令一样。而path.join只是简单的将该路径片段进行拼接
例如path.resolve('./a','/b'),类似执行了如下命令
cd ./a
cd /b
./是当前目录、../是父级目录、/是根目录 接下来我们看几个例子
// 当前工作目录与当前文件路径(F:/1/2/task6/test/dist)有区别
path.resolve(); // F:/1/2/task6/test 当前工作目录的绝对路径
path.resolve('./a'); // F:/1/2/task6/test/a
path.resolve('../a'); // F:/1/2/task6/a
path.resolve('.'); // F:/1/2/task6/test
path.resolve('..'); // F:/1/2/task6
path.resolve('/')); // F:/
path.resolve('./a','../c/d'); // F:/1/2/task6/test/c/d
path.resolve('./a','./c/d'); // F:/1/2/task6/test/a/c/d
path.resolve('/a','../c/d'); // F:c/d
path.resolve('/a','./c/d'); // F:/a/c/d
path.resolve('./a','/b','./c/d'); // F:/b/c/d
path.resolve('a','b','c/d'); // F:/1/2/task6/test/a/b/c/d
path.resolve('./a','./b','c/d'); // F:/1/2/task6/test/a/b/c/d
path.resolve('./a','/b','c/d'); // F:/b/c/d
path.resolve('./a/b','..','c/d'); // F:/1/2/task6/test/a/c/d
path.resolve('./a','..','c/d'); // F:/1/2/task6/test/c/d
路径和对象相互转换
将路径转换成对象
path.parse(path)
- path string类型
- 返回
- root(根名) string类型
- dir(目录名) string类型
- base(带扩展名的文件名) string类型
- ext(仅扩展名) string类型
- name(仅文件名) string类型 path.parse() 方法会返回一个对象,其属性表示 path 的有效元素。 尾部的目录分隔符会被忽略
console.log(path.parse('/Users/zxy/Documents/Note/代码类/node学习笔记/node基础知识/1.path和几种常见的文件路径.js'))
/* { root: '/',
dir: '/Users/zxy/Documents/Note/代码类/node学习笔记/node基础知识',
base: '1.path和几种常见的文件路径.js',
ext: '.js',
name: '1.path和几种常见的文件路径' }
*/
将对象转换成路径
path.format(pathObject)
- pathObject Object类型
- root(根名) string类型
- dir(目录名) string类型
- base(带扩展名的文件名) string类型
- ext(仅扩展名) string类型
- name(仅文件名) string类型
- 返回 string path.format() 方法从对象返回路径字符串。 与 path.parse() 相反。 当为 pathObject 提供属性时,注意以下组合,其中一些属性优先于另一些属性:
- 如果提供了 pathObject.dir,则忽略 pathObject.root。
- 如果 pathObject.base 存在,则忽略 pathObject.ext 和 pathObject .name。
console.log(path.format({
root: '/',
dir: '/Users/zxy/Documents/Note/代码类/node学习笔记/node基础知识',
base: '1.path和几种常见的文件路径.js',
ext: '.js',
name: '1.path和几种常见的文件路径'
}))
// /Users/zxy/Documents/Note/代码类/node学习笔记/node基础知识/1.path和几种常见的文件路径.js
相对路径
path.relative(from, to)
- form string类型
- to string类型
- 返回 string类型 path.relative() 方法根据当前工作目录返回 from 到 to 的相对路径。
1.如果 from 和 to 各自解析到相同的路径(分别调用 path.resolve() 之后),则返回零长度的字符串。
2.如果将零长度的字符串传入 from 或 to,则使用当前工作目录代替该零长度的字符串。
3.若from和to不是字符串,则会报错。
console.log(path.relative('/Users/zxy/Documents/Note/代码类/node学习笔记/node基础知识', '/Users/zxy/Documents/Note/代码类/node学习笔记'))
// ..
console.log(path.relative('/Users/zxy/Documents/Note/代码类/node学习笔记/node基础知识', '/Users/zxy/Documents/Note/代码类'))
// ../..
// console.log(path.relative('/Users/zxy/Documents/Note/代码类/node学习笔记/node基础知识', '/Users/zxy/Documents/Note/代码类/node学习笔记/node基础知识'))
// ''(空字符串)
可以发现 我们如果在from路径下打开命令行,cd执行path.relative返回的字符串,那么将到达to路径
回到最初的问题
在当前目录
├── node学习笔记
├── node基础知识
├── 1.path和几种常见的文文件路径.js
└── 油炸日子.txt
怎么才能在1.path和几种常见的文文件路径.js文件中读取到油炸日子.txt?
看完上面内容的小伙伴,再次看到这个问题应该就很简单了吧。直接上打代码
解法一
var fs = require('fs')
var path = require('path')
fs.readFile(`${__dirname}` + '/油炸日子.txt', 'utf8', function (err, data) {
console.log(data)
})
解法二
fs.readFile(path.resolve('./node基础知识/油炸日子.txt'), 'utf8', function (err, data) {
console.log(data)
})
或者
fs.readFile(path.resolve(__dirname, '油炸日子.txt'), 'utf8', function (err, data) {
console.log(data)
})
总结
path模块常用的也可以说大部分方法都已经分享给小伙伴了,如果想要更进一步了解其他剩下的方法,请移步node中文网,喜欢小编的小伙伴可以去油炸日子的githubstar一下也是可以的哦,也可以去油炸日子的博客查看更多小编的文章,谢谢小伙伴们的支持。