作为脚本语言,学习文件操作很有价值,不是缩进党,所以与 python/ruby 无缘,但 python 确实用的越来越多,接触多了还是不喜欢…废话别扯多了,js 的 IO 操作
path 相关
__dirname __filename
这两个参数是在 require 的时候传入的,node 将 js 包装成一个function(exports, require, module, __filename, __dirname)
来传参执行,__dirname
表示脚本 js 文件所在目录,__filename
表示文件名
前几天在 v2ex 上看到一个问题,python 中的
1 | if __name__ == "main" |
如何模拟,因为我之前写的 node 基础里面有,require 的 parent children 属性,被 require 时 parent 不为空
1 | if (!module.parent) { |
各大神支招,当 js 直接被执行,不是被 require 的时候有
1 | process.mainModule = require.main = module; |
于是方法多了去了…乱扯几句吧
process.cwd()
当前工作目录
process.chdir()切换目录,这个是同步执行的,可以 try catch
process.execPath
表示 node 可执行文件的位置,如 windows 上是 node.exe 位置
path 模块
1 | > path |
path 属性
path.sep 表示路径分隔符,win 上是\
即 backslash,其他系统/
forwardslash
path.delimiter 表示界定符,即 PATH 环境变量中每个文件夹的分隔符,在 win 上是;
,其他系统是:
获取 path 的各个部分
扩展名 path.extname()
1 | > path.extname("d:/dir/abc.txt") |
注意扩展名带有.
号
文件名 path.basename
1 | > txt = "d:/dir/abc.txt" |
- basename 传一个参数,将最后一部分返回,用分隔符分隔
\\
或/
- basename 传两个参数,后面是它的扩展名,会将扩展名从文件名中去处
如果传递的扩展名不带.
号,则剩下的文件名带.
号 - basename 还可以用于文件夹,简单地取最后一部分
文件夹 path.dirname
1 | > txt |
返回文件或目录的父文件夹路径
正常化 path(normalization)
把绝对路径 相对路径 混杂的变成绝对路径
1 | > path.normalize("/foo/bar/.././bar/../../baz") |
path.join 也可以混杂,相对路径,绝对路径,还有 path.resolve 作用也是解决路径混杂
相对路径 path.relative
path.relative(from,to)
1 | > path.relative('d:/dir/abc','d:/abc') |
就是以第一个参数为起点,第二个参数为终点的相对路径
file 相关 fs module
fs 模块的方法都有 异步版本 与 同步版本(添加 Sync)后缀,虽然异步很美好,但是同步更好理解,遵循一个原则就好
As a general rule, code that can be called
multiple times simultaneously should be asynchronous
– pro nodejs for developers
simultaneously 同时地,就是这段代码可能同时执行好几次,典型的 request - server 就是,应该为异步代码,其他如启动读读配置文件什么的还是可以用用 Sync 的代码的
文件(夹)是否存在 fs.exists
打上文件夹是因为可以用来判断文件夹是否存在,而且path.exists
这个方法也可以调用,但是已经作废了,改到 fs.exists 里面了
fs.exists(path,callback) //callback(exists)
fs.existsSync(path)
注意这个回调没有 err 的,存在 true,不存在 false,没理由 err
查看文件状态 Statstics
fs.stat
fs.Stats fs.statSync(path)
返回的 fs.Stats 类型成员
字段
方法,这些方法是同步执行,返回 true or false 值
fs.stat 变种
- fs.lstat,如果参数文件是个 link 的话,fs.stat 只看这个 link 的 statistic 数据,而 lstat 会查看这个 link 指向的文件的数据
- fs.fstat,这个第一个参数是 file descriptor 而不是 string
打开/关闭文件 fs.open/close
1 | fs.open(path,flag,callback)//callback(err,fd) fd=file descriptor |
可选参数 mode 默认 0666
关于 flag
带 x 的是 exclusive,独占模式
使用 w+,可读可写,文件不存在,创建,已存在,清空原有内容
fs.read()/write()
提供 C#类似 Stream 的功能,可自定义文件读取位置什么的
1 | fs.open("d:/dir/abc.txt","w+",function(err,fd){ |
- fd : 文件描述符
- buffer : Buffer 数组
- offset : 从 buffer 的那个位置开始放置
- length : 要读取得长度
- from : 表示要从文件哪里开始读,null 表示文件开头
同步版本返回已读取的字节数
fs.readFile/writeFile
就是 open read 的封装了,相当于 C#里的System.IO.File.ReadAllText()
不用管文件打开关闭那些闲杂事了
1 | data = fs.readFileSync(path,flag) |
encoding 可选,默认”utf8”,第三个参数
默认 flag,w+
重命名 fs.rename
1 | fs.rename(old,new,function(err){ }) |
删除文件 fs.unlink
1 | fs.unlink(path,function(err){ ... }) |
当文件不存在则会报错…
新建文件夹 fs.mkdir
1 | fs.mkdir("abcd",function(err){ ... }); |
只能新建最底层的文件夹,比如新建d:/not/exists/sub
就会出错,文件夹已存在出错,可以使用 fs-extra 来新建多重文件夹
读取文件夹内容 fs.readdir
注意 readdir,dir 小写
1 | path.readdir(path,function(err,stats)) |
得到的是 fs.Stats 数组,可以使用 isFile()/isDirectory 来判断文件还是文件夹
删除文件夹 fs.rmdir
1 | fs.rmdir(path,function(err){ ... }) |
只能删除非空文件夹,有内容的话,删除会报错,可以自己实现递归删除的…
watch 目录,fs.watch
找不到合适的中文,就用 watch 吧
1 | FSWatcher watcher = fs.watch(path,{ persistent : true } , function(event,path){ |
返回的是 FSWatcher 类型实例,可以监听 change 事件
1 | watcher.on("change",function(){ ... }) |
跟 watch 方法传递的参数一致,event 有change|rename
,但我测试仅仅创建一个文件,就有这么多事件
1 | [change] : Desktop |
推荐使用 chokidar 这个 library,hexo 也在用
使用watcher.close()
关闭 watcher