02_内置模块_fs模块
node当中的基础API学习
1、fs
1.1、概述
-
fs是node官方提供的,用来专门操作文件的模块,他提供了一系列的方法和属性,用来满足用户的需求
- 例如 => fs.readFile(),可以读取指定文件的内容
- fs.writeFile(),可以用来,向指定的文件写入内容
-
如果要在JavaScript代码块当中,使用fs来操纵文件,则需要使用如下的方式来导入他

- require导入了fs模块,使用fs变量来接受,接受到这个导入文件的变量就是fs的对象
- 可以通过该对象调用该模块当中提供的接口和方法
-
那么这个fs是怎么来的?
- 在安装node的时候,已经将这些内置模块安装到我们设置的本地路径当中了,我们可以使用这些本地模块的内置功能,接口,方法
- node会自动帮我们查找 fs 这个模块
-
当我们需要使用fs的时候,就使用require来导入我们的fs模块即可
1.2、fs.readFile()语法格式

1、示例代码
- 以utf-8的格式,读取指定文件的内容,打印err和dataStr的值
- 新建demo,当前文件目录结构如下

// 1、引入fs模块来操作文件
const fs = require('fs')
// 2、调用fs的readFile方法,传入参数
/**
* 参数解析
* path:要读取的文件路径,为当前目录下的text目录下的readFile.txt文件
* options:可选参数,这里是指读取参数的内容格式为(一般默认为utf-8)
* callback: 调用该方法的回调函数,会携带两个参数内容
* err:读取错误会实例化该对象
* dataStr:如果当前读取的是文本文件内容,则会打印当前对象的文本内容
*/
fs.readFile("./text/readFile.txt",'utf-8',(err,dataStr)=>{
// 打印失败结果
console.log(err)
console.log('------------')
// 打印成功读取结果
console.log(dataStr);
})
2、查看打印结果

输入node + 待执行的js文件命令来运行我们的js文件

可以看到,如果文件路径能够被读取到,那么err对象就为空,但是如果不指定编码格式,那么打印出来的就是另一番风景


3、打印错误结果对象err


1.3、判断文件读取成功or失败

- 这里单独说一下,err如果为null的话在if语句当中默认判断为false

1.4、fs.writeFile语法格式
向指定文件写入内容

-
与readFile类似
-
参数解析
- file:必选参数,需要指定一个文件存储路径的字符串
- data:必选参数,你要向这个指定文件当中写入什么样的内容
- options:可选参数,文件以什么样的字符编码写入
- callback:回调函数,必选参数,文件写入完成后的回调函数,无论写入成功还是失败都会调用该函数
-
根据测试和原码分析,该回调函数的内容是无参的,但是如果发生其他错误,那么err对象会被实例化,这个地方很有意思,是怎么写出来的?
1、原码解析

- 异步地将数据写入文件,如果该文件已经存在,则替换该文件。
- @参数路径指向文件的路径。如果提供了一个URL,那么它必须使用
文件:协议。 - 如果提供了一个文件描述符,则该底层文件将不会被自动关闭。
- @参数化数据要写入的数据。如果提供了缓冲区或Uint8数组之外的内容,该值将被强制为字符串。
/**
* Asynchronously writes data to a file, replacing the file if it already exists.
* @param path A path to a file. If a URL is provided, it must use the `file:` protocol.
* If a file descriptor is provided, the underlying file will _not_ be closed automatically.
* @param data The data to write. If something other than a Buffer or Uint8Array is provided, the value is coerced to a string.
*/
export function writeFile(path: PathOrFileDescriptor, data: string | NodeJS.ArrayBufferView, callback: NoParamCallback): void;
2、测试代码1 => 测试文件覆盖
这是我准备的一个已存在的文件夹,现在我要进行覆盖测试,即=>写入的内容覆盖掉之前的内容

代码编写
// 1、 导入node当中的fs模块
const fs = require("fs")
// 2、使用fs的fileWrite语法,对文件数据进行写入
fs.writeFile("./text/fileWrite.txt","这是我写入的数据,如果该文件夹存在,那么我会覆盖掉之前写入的数据",(err)=>{
// 打印错误信息对象
console.log(err);
// 当前错误信息对象为空
if(!err){
console.log('已完成写入 => ok');
}
})
运行测试


- 通过测试发现,文件当中写入的内容已经被覆盖掉了
- 接下来我们来测试一下,文件不存在,使用writeFile语法是否可以新建文件
3、文件不存在,写入数据的时候是否会新建文件?
示例代码
// 1.引入node的fs模块
const fs = require("fs")
// 2.测试不存在的文件夹,在写入的时候是否会新建一个文件夹
fs.writeFile("./text/fileWrite1.txt","如果err为空,那么表示我写入成功,并且文件被新建出来",(err)=>{
console.log(err);
// err为空则写入成功
if(!err){
console.log("新文件创建成功,写入内容成功 => ok");
}
})
测试结果


- 如果盘符存在的情况下,文件新建是合理的情况
- 那么当我们要写入的这个文件不存在的时候,使用writeFile语法会为我们新建一个文件夹,并写入内容
- 那么当盘符不存在的时候呢?你根本都不能新建文件夹的时候呢?
4、盘符不存在,文件新建不了的情况

// 3.测试盘符不存在的情况
fs.writeFile("F:/text/fileWrite.txt","测试盘符不存在",(err)=>{
console.log(err);
if(!err){
console.log("盘符不存在我也可以写入成功?");
}
})
测试结果

- 如果当你要写入的文件,他的盘符都不存在的时候,那么err就会被实例化为一个错误对象
- 在当中我们可以看到这个错误对象当中的各类参数
2、fs模块案例练习 =>导入成绩
2.1、概述
- 使用fs模块,对文件夹成绩.txt的文件当中的考试数据,进行整理,整理到我们的成绩-ok.txt文件夹中
- 整理前的成绩.txt格式如下
- 下面有每个人的成绩,并且每个人的成绩后面有一个空格进行分割

- 整理后得到的 成绩-ok.txt格式如下
- 存在换行符,并且成绩的左边用 中文的冒号分隔

2.2、案例设计
- 首先我们肯定要导入fs模块
- 使用fs模块下的readFile函数进行文件的读取
- 对读取到的文件内容进行字符串的分割和处理
- 将处理完毕的数据,使用writeFile语法对文件进行写入,这里我们采用 => 写入文件不存在,让writeFile对我们的文件进行新建的方式
2.3、数据准备
直接粘贴即可
小红=99 小白=100 小黄=70 小黑=66 小绿=88
在我们的文件夹text当中新建我们的 => 成绩.txt文件夹

2.4、代码设计
- 简单说下设计思路
- 导入fs模块
- 准备一个空数组和空字符串
- 使用readFile读取我们的成绩.txt文本文件
- 对读取出来的结果,采用字符串分割,以 空格 作为分割条件
- 分割完毕的结果使用数组接收
- 对数组进行迭代,迭代的过程当中,将迭代项使用 等号 分割
- 分割完毕以后会得到一个小数组,这个数组是被等号分割开的两项,例如小红=100 => 小红,100
- 接下来对索引0(小红),进行字符串的添加,添加一个 中文冒号
- 对索引1(100),进行字符串添加,添加换行符 (在windows系统中换行标志为; \r\n)
- 将组合完毕的数据进行字符串拼接,修改我们当前数组
- 最后迭代修改完毕的数组,将数组当中的循环项拼接为一条字符串信息
// 1.导入我们的fs模块
const fs = require("fs")
// 2.准备读取的学生成绩数组,用于存储
var studentGrades = [];
var strGroup = ""; // 上述数组的结合
// 3.readFile语法对 成绩.txt 文件进行读取
fs.readFile("./text/成绩.txt","utf-8",(err,dataStr)=>{
// 判断文件内容是否读取成功
if(err){
console.log(err);
return;
}
// 读取成功,对字符串内容进行分割
// 将分割完毕的数据存入到我们的学生成绩数组当中
studentGrades = dataStr.split(" ");
// 成绩数组索引记录
var index = 0;
// 迭代当前成绩数组
studentGrades.forEach(v=>{
// 去除等号,例如 小红=100变为 小红:100
// 这时候需要做两手操作,拆分小红和100
var gradesText = v.split("=")
// 拆分完毕应该变为 小红 100
gradesText[0] = gradesText[0] + ":" // 加上中文冒号
// 为成绩加上换行符
gradesText[1] = gradesText[1] + "\r\n";
// 对迭代项下标进行赋值修改
studentGrades[index] = gradesText[0] + gradesText[1]
index++;
})
// 对数组当中的文字描述进行组合
studentGrades.forEach(v=>{
strGroup+=v
})
// 组合完毕,调用writeFile对指定文件进行创建和写入
fs.writeFile("./text/成绩-ok.txt",strGroup,(err)=>{
if(err){
console.log(err.message);
return
}
// 文件写入成功
console.log("已完成文件的写入");
})
})

文件新建成功,并且按照我们的方式成功写入了对应的要求
2.5、代码修改
- 教程当中的使用了数组的另外几个API,这是我没有见过的,这里记录一下
- 利用数组的
- replace,将指定内容替换为某某某
- join:将数组拼接为字符串,每个循环项之间用xx分割,如果不写分割项,默认使用逗号或者undefined进行分割
// 1、导入fs模块
const fs = require("fs")
// 2、使用readFile读取文件内容
fs.readFile("./text/成绩.txt","utf-8",(err,dataStr)=>{
// 文件读取失败
if(err){
return console.log(err.message);
}
// 3、文件读取成功,对字符串内容进行分割,以空格进行分割
var studentGrades = dataStr.split(" ")
// 空数组,用于接收替换完毕的数组数据
const newArr = [];
// 4、分割完毕,对数组当中的的等号进行替换
studentGrades.forEach(v=>{
// 末尾添加,等号替换为中文冒号
newArr.push(v.replace("=",":"))
})
// 5、对新数组进行拼接,join,使用换行符进行分割
var groupStr = newArr.join("\r\n");
// 6、调用writeFileAPI进行拼接
fs.writeFile("./text/成绩-ok.txt",groupStr,(err)=>{
// 文件写入失败
if(err){
return console.log(err.message);
}
// 写入成功
console.log("文件写入成功!");
})
})

3、fs模块,路径动态拼接问题
3.1、概述
- 使用fs模块的时候,如果提供的路径是以 "./" 或者 "../"这样的相对路径,当我们切换目录的时候,就很容易出现文件动态拼接错误的问题,什么是文件动态拼接错误呢?
- 因为在代码运行的时候,使用node指令,那么他的执行目录就是当前node这个命令所处的目录
- 当我们要对这个操作目录进行文件读取修改的时候,例如我要操作./files/text.txt,目录,使用node指令他就会帮我自动将二者路径拼接,即(node所处目录 + 相对路径目录(./files/text.txt))
- 这个时候我们如果切换文件目录,那么这个相对路径就失效了,动态拼接就会出现问题
3.2、案例演示(知识回顾)
-
很久没有写过代码了,这里决定重温下内容
- 首先我们需要在js文件当中引入 fs模块
- 接着开始我们的业务逻辑,需求分析
- 我们需要使用fs模块提供的API == readFile(),通过它来读取文件
- 在readFile当中填入三个参数(文件路径,读取方式,回调函数)
- 最后编写 成功回调 和 失败回调的函数内容
-
代码演示
-

-
// 导入node当中的fs模块 const fs = require("fs") // 使用fs模块的readFile函数对文件进行读取 /** * 回顾知识 * 参数1:读取的文件路径 * 参数2:非必填,文件读取格式 * 参数3:callback */ fs.readFile("./text/成绩.txt","utf-8",(err,dataStr)=>{ // 回顾知识,err为空对象的情况下,在if语句当中默认为false if(err){ console.log(err); return; } console.log(dataStr); })
-
3.3、测试部分
- 首先我们在当前项目的文件夹下打开 powershell == (shift + 鼠标右键打开)
- 当前powershell目录如下

- 接着运行我们的js文件

- 查看运行结果

- 读取成功
3.4、切换目录进行测试
- 现在我们切换回 上一级目录
- 现在开始读取文件,这个时候的文件路径要加上 demo2_使用readFile语法 的前缀

- 接下来我们看看运行结果
- 运行结果

- no such file or directory 意为 没有这样的文件或目录
- 而我们需要访问的,readFile读取的文件路径应该是 == D:\技能学习\node\demo2_使用readFile语法\text\成绩.txt
- 实际上通过node访问得到的结果是 == D:\技能学习\node\text\成绩.txt
- 对比
**- 看出来了吗?为什么会这样?
3.5、解析
-
实际情况就像开篇,概述提到的内容一样
-
当我们的文件采用 ./或者../开头的 “相对路径的时候”
-
当我们切换目录进行访问的时候,是很容易出现问题的
-
原因
- 代码在运行的时候,会以 node所在的文件目录
- 动态拼接出 ../或者./的内容
-
所以才会出现这种情况
-
总结
1、当前node代码执行位置,+./成绩.txt动态拼接
2、当前js代码是在demo2....文件夹下
3、js代码中readFile("./成绩.txt")
4、这里的./指的就是与demo2同级的文件text文件夹5、所以./成绩.txt = text/成绩.txt
6、所以,根据1+5最终得到D:\技能学习node\text\成绩.txt
3.6、如何解决
- 如果出现动态路径拼接错误,那么采用 绝对路径的方式可以解决这个问题
- 使用绝对路径确实不会出现这个问题了,但随之而来的是另一个衍生问题
- 移植性非常差
- 不利于维护

- 使用绝对路径确实不会出现这个问题了,但随之而来的是另一个衍生问题
3.7、升级,使用__dirname
- 双下划线 + dirname 可以表示当前这个js文件所处的文件夹位置
- 我们来打印一下
- 这样我们就解决了这个问题,再来测试一下
- 我们返回上一级目录再试试









浙公网安备 33010602011771号