02_内置模块_fs模块

node当中的基础API学习

1、fs

1.1、概述

  • fs是node官方提供的,用来专门操作文件模块,他提供了一系列的方法和属性,用来满足用户的需求

    1. 例如 => fs.readFile(),可以读取指定文件的内容
    2. fs.writeFile(),可以用来,向指定文件写入内容
  • 如果要在JavaScript代码块当中,使用fs来操纵文件,则需要使用如下的方式来导入他

    • image-20220623235426899
    • require导入了fs模块,使用fs变量来接受,接受到这个导入文件的变量就是fs的对象
    • 可以通过该对象调用该模块当中提供的接口和方法
  • 那么这个fs是怎么来的?

    • 安装node的时候,已经将这些内置模块安装到我们设置的本地路径当中了,我们可以使用这些本地模块的内置功能,接口,方法
    • node会自动帮我们查找 fs 这个模块
  • 当我们需要使用fs的时候,就使用require来导入我们的fs模块即可

1.2、fs.readFile()语法格式

image-20220623235957948

1、示例代码

  • 以utf-8的格式,读取指定文件的内容,打印err和dataStr的值
  • 新建demo,当前文件目录结构如下
    • image-20220624004616625

image-20220624004628291

// 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、查看打印结果

image-20220624004737262

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

image-20220624005059956

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

image-20220624005219702

image-20220624005239650

3、打印错误结果对象err

image-20220624005513236

image-20220624005604908

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

image-20220624005932363

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

image-20220624010537352

1.4、fs.writeFile语法格式

向指定文件写入内容

image-20220628113655656

  • readFile类似

  • 参数解析

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

1、原码解析

image-20220628224407179

  • 异步地将数据写入文件,如果该文件已经存在,则替换该文件。
  • @参数路径指向文件的路径。如果提供了一个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 => 测试文件覆盖

这是我准备的一个已存在的文件夹,现在我要进行覆盖测试,即=>写入的内容覆盖掉之前的内容

image-20220628230015065

代码编写

// 1、 导入node当中的fs模块
const fs = require("fs")

// 2、使用fs的fileWrite语法,对文件数据进行写入

fs.writeFile("./text/fileWrite.txt","这是我写入的数据,如果该文件夹存在,那么我会覆盖掉之前写入的数据",(err)=>{
    // 打印错误信息对象
    console.log(err);
    // 当前错误信息对象为空
    if(!err){
        console.log('已完成写入 => ok');
    }
   
})

运行测试

image-20220628225202565

image-20220628225218798

  • 通过测试发现,文件当中写入的内容已经被覆盖掉了
  • 接下来我们来测试一下,文件不存在,使用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");
    }
})

测试结果

image-20220628225733476

image-20220628225807272

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

4、盘符不存在,文件新建不了的情况

image-20220628230156051

// 3.测试盘符不存在的情况

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

测试结果

image-20220628230239837

  • 如果当你要写入的文件,他的盘符都不存在的时候,那么err就会被实例化为一个错误对象
  • 在当中我们可以看到这个错误对象当中的各类参数

2、fs模块案例练习 =>导入成绩

2.1、概述

  • 使用fs模块,对文件夹成绩.txt的文件当中的考试数据,进行整理,整理到我们的成绩-ok.txt文件夹中
  • 整理前的成绩.txt格式如下
    • 下面有每个人的成绩,并且每个人的成绩后面有一个空格进行分割
    • image-20220628230801639
  • 整理后得到的 成绩-ok.txt格式如下
    • 存在换行符,并且成绩的左边用 中文的冒号分隔
    • image-20220628231128160

2.2、案例设计

  1. 首先我们肯定要导入fs模块
  2. 使用fs模块下的readFile函数进行文件的读取
  3. 对读取到的文件内容进行字符串的分割和处理
  4. 将处理完毕的数据,使用writeFile语法对文件进行写入,这里我们采用 => 写入文件不存在,让writeFile对我们的文件进行新建的方式

2.3、数据准备

直接粘贴即可

小红=99 小白=100 小黄=70 小黑=66 小绿=88

在我们的文件夹text当中新建我们的 => 成绩.txt文件夹

image-20220628232246751

2.4、代码设计

  • 简单说下设计思路
    1. 导入fs模块
    2. 准备一个空数组和空字符串
    3. 使用readFile读取我们的成绩.txt文本文件
    4. 对读取出来的结果,采用字符串分割,以 空格 作为分割条件
    5. 分割完毕的结果使用数组接收
    6. 对数组进行迭代,迭代的过程当中,将迭代项使用 等号 分割
    7. 分割完毕以后会得到一个小数组,这个数组是被等号分割开的两项,例如小红=100 => 小红,100
    8. 接下来对索引0(小红),进行字符串的添加,添加一个 中文冒号
    9. 索引1(100),进行字符串添加,添加换行符 (在windows系统中换行标志为; \r\n)
    10. 将组合完毕的数据进行字符串拼接,修改我们当前数组
    11. 最后迭代修改完毕的数组,将数组当中的循环项拼接为一条字符串信息
// 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("已完成文件的写入");
    })
})

image-20220629150751271

文件新建成功,并且按照我们的方式成功写入了对应的要求

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("文件写入成功!");
    })


})

image-20220629150745245

3、fs模块,路径动态拼接问题

3.1、概述

  • 使用fs模块的时候,如果提供的路径是以 "./" 或者 "../"这样的相对路径,当我们切换目录的时候,就很容易出现文件动态拼接错误的问题,什么是文件动态拼接错误呢?
  • 因为在代码运行的时候,使用node指令,那么他的执行目录就是当前node这个命令所处的目录
  • 当我们要对这个操作目录进行文件读取修改的时候,例如我要操作./files/text.txt,目录,使用node指令他就会帮我自动将二者路径拼接,即(node所处目录 + 相对路径目录(./files/text.txt))
  • 这个时候我们如果切换文件目录,那么这个相对路径就失效了,动态拼接就会出现问题

3.2、案例演示(知识回顾)

  • 很久没有写过代码了,这里决定重温下内容

    1. 首先我们需要在js文件当中引入 fs模块
    2. 接着开始我们的业务逻辑,需求分析
    3. 我们需要使用fs模块提供的API == readFile(),通过它来读取文件
    4. readFile当中填入三个参数(文件路径,读取方式,回调函数)
    5. 最后编写 成功回调 和 失败回调的函数内容
  • 代码演示

    • image-20220927205838428

    • // 导入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、测试部分

  1. 首先我们在当前项目的文件夹下打开 powershell == (shift + 鼠标右键打开
    • image-20220927205955951
    • image-20220927210238654
  2. 当前powershell目录如下
    • image-20220927210310117
    • 接着运行我们的js文件
    • image-20220927210342615
  3. 查看运行结果
    • image-20220927210428692
    • 读取成功

3.4、切换目录进行测试

  1. 现在我们切换回 上一级目录
    • image-20220927210517135
  2. 现在开始读取文件,这个时候的文件路径要加上 demo2_使用readFile语法 的前缀
    • image-20220927210611056
    • 接下来我们看看运行结果
  3. 运行结果
    • image-20220927210728892
    • no such file or directory 意为 没有这样的文件或目录
    • 而我们需要访问的,readFile读取的文件路径应该是 == D:\技能学习\node\demo2_使用readFile语法\text\成绩.txt
    • 实际上通过node访问得到的结果是 == D:\技能学习\node\text\成绩.txt
  4. 对比
    • image-20220927211010180**
    • 看出来了吗?为什么会这样?

3.5、解析

  • 实际情况就像开篇,概述提到的内容一样

  • 当我们的文件采用 ./或者../开头的 “相对路径的时候”

  • 当我们切换目录进行访问的时候,是很容易出现问题的

  • 原因

    1. 代码在运行的时候,会以 node所在的文件目录
    2. 动态拼接../或者./的内容
  • 所以才会出现这种情况

    • image-20220927211816690
  • 总结

    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、如何解决

  1. 如果出现动态路径拼接错误,那么采用 绝对路径的方式可以解决这个问题
    • 使用绝对路径确实不会出现这个问题了,但随之而来的是另一个衍生问题
      1. 移植性非常差
      2. 不利于维护
    • image-20220927213244005

3.7、升级,使用__dirname

  • 双下划线 + dirname 可以表示当前这个js文件所处的文件夹位置
  • 我们来打印一下
    • image-20220928003710531
  • 这样我们就解决了这个问题,再来测试一下
    • image-20220928003830777
  • 我们返回上一级目录再试试
    • image-20220928003955910
posted @ 2022-10-02 22:55  澜璨  阅读(240)  评论(0)    收藏  举报