使用fs模块,首先需要导入:
1const fs = require('fs')
读取文件
下方是一个通过fs.readFile()读取文件的示例:
1// 1. 导入fs模块,来操作文件
2const fs = require('fs')
3/**
4 * 2. 调用 fs.readFile() 方法读取文件
5 * - 参数1(必须):读取文件的存放路径
6 * - 参数2(可选):读取文件时采用的编码格式,默认指定utf8
7 * - 参数3(必须):回调函数,拿到读取失败(err)和成功的结果(dataStr)
8 */
9fs.readFile('test.txt', 'utf-8', (err, dataStr) => {
10 /**
11 * 2.1 打印失败的结果
12 * - 如果读取成功,则 err 的值为 null,dataStr的值为 文件的内容
13 * - 如果读取失败,则 err 的值为 错误对象,dataStr的值为 undefined
14 */
15 console.log(err)
16 console.log('-------------')
17 // 2.2 打印成功的结果
18 console.log(dataStr)
19})
fs.readFile()用于异步读取文件。当fs.readFile()读取到文件之后,会将信息传递给err和dataStr对象。err对象可用于读取成功与否的判断,并且记录了读取失败时的信息;dataStr对象记录了读取到的文件内容。
判断文件是否读取成功:
1const fs = require('fs')
2
3let fileName = 'test.txt'
4fs.readFile(fileName, 'utf-8', (err, dataStr) => {
5 if (err) {
6 return console.log('readFileErr: ' + err.message)
7 }
8 console.log('Read ' + fileName + ' success!')
9 console.log(dataStr)
10})
fs模块还有一个readFileSync()方法,用于同步地文件读取,它与readFile()异步地文件读取区别如下:
-
readFile():1const { readFile } = require('fs') 2 3console.log('Program started...'); 4 5let fileName = 'test.txt' 6readFile(fileName, 'utf-8', (err, dataStr) => { 7 console.log('-------------'); 8 if (err) { 9 return console.log('readFileErr: ' + err.message) 10 } 11 console.log('Read ' + fileName + ' success!') 12 console.log(dataStr) 13 console.log('-------------'); 14}) 15 16console.log('Program ended...');输出结果:
1Program started... 2Program ended... 3------------- 4Read test.txt success! 5Hello Node.js! 6-------------可以发现异步地文件读取,不会立即执行回调函数,而是直接执行下一条指令,直到文件读取成功才执行回调。
-
readFileSync():1const { readFileSync } = require("fs"); 2 3// readFileSync() 不能注册回调函数 4var fileData = readFileSync('test.txt') 5if (fileData) { 6 console.log('-------------'); 7 console.log('Readed Success!'); 8 // 没有指定字符集的话是返回一个 Buffer 对象,所以要将其转为 String 9 var fileContent = String(fileData) 10 console.log(fileContent); 11 console.log('-------------'); 12} else { 13 console.log('Readed Error!'); 14} 15 16console.log('Program ended...');输出:
1Program started... 2------------- 3Readed Success! 4Hello Node.js! 5------------- 6Program ended...
写入内容
1// 1. 导入fs.writeFile() 方法
2const { writeFile } = require('fs');
3
4/**
5 * 2. 调用 fs.wirteFile() 方法,写入文件的内容
6 * - 参数1(必须):文件的存放路径
7 * - 参数2(必须):要写入文件的内容
8 * - 参数3(可选):设置文件模式
9 * - 参数4(必须):回调函数
10 */
11writeFile('test1.txt', 'Writes new content', (err) => {
12 // 2.1 如果文件写入成功,则 err == null
13 // 2.3 如果文件写入失败,则 err == 错误对象
14 console.log(err)
15})
注:要触发文件不存在,
err返回错误对象的前提是路径也不存在,否则fs.wirteFile()默认会创建一个新文件并写入。例如你当前目录下不存在test这个目录的话,可以将路径改成test/test.txt来让err返回错误对象。
判断文件写入是否成功:
1const { writeFile } = require("fs");
2
3writeFile('test.txt', 'Writes new content...', (err) => {
4 if (err) {
5 return console.log('writeFileErr: ' + err.message)
6 }
7 console.log('Writing Success!')
8})
fs.writeFile()也有与fs.readFile()类似的同步写入函数fs.writeFileSync(),作用与fs.readFileSync()类似。
路径动态拼接问题
在使用fs模块操作文件时,如果提供的操作路径是以./或../开头的相对路径时,很容易出现路径动态拼接错误的问题。
演示路径动态拼接问题:
1const { readFile } = require('fs');
2
3readFile('./test.txt', 'utf-8', (err, data) => {
4 if (err) {
5 console.log(err.message);
6 }
7 console.log('Reading Success!');
8})
9console.log(data);
切换到上层目录,执行该JS,会发现报错:
1$ cd ..
2$ node js/path-problem.js
3ENOENT: no such file or directory, open './test.txt'
这是因为代码在运行的时候,会以执行Node命令时所处的目录,动态拼接出被操作文件的完整路径。
在使用fs模块操作文件时,可以通过__dirname直接提供完整的路径,不要提供以./或../开头的相对路径,从而防止路径动态拼接的问题。
修改如下:
1const { readFile } = require('fs');
2
3/**
4 * 出现路径拼接问题,是因为使用了 './' 或 '../' 之类的相对路径
5 * Node在运行JS时,会将这类相对路径进行处理,使用当前JS文件所在的目录拼接在打开的文件路径上
6 * __dirname 表示当前JS文件所处的目录
7 */
8readFile(__dirname + '/test.txt', 'utf-8', (err, data) => {
9 if (err) {
10 console.log(err.message);
11 return
12 }
13 console.log('Reading Success!');
14 console.log(data);
15})
再次执行,文件可以成功打开。
Path 模块处理路径
path模块是Node.js官方提供的、用来处理路径的模块。它提供了一系列的方法和属性,用来满足用户对路径的处理需求。
常用的方法有:
path.join():将多个路径片段拼接成一个完整的路径字符串。path.basename():从路径字符串中,将文件名解析出来。path.extname():从路径字符串中,将文件名扩展名解析出来。
使用之前需要先导入path模块:
1const path = require('path')
path.join():
1const path = require('path');
2
3// path.join() 将参数列表中的字符串进行路径拼接,并返回拼接结果
4const pathStr = path.join('/dir1', 'dir2/dir3', '../', './dir4', 'dir5')
5console.log(pathStr);
输出结果如下:
1/dir1/dir2/dir4/dir5
注:
path.join()方法仅用于路径拼接,相当于路径字符串的处理,不对路径的存在与否进行检查。
利用path.join()替代+号拼接路径:
1const { readFile } = require('fs');
2const { join } = require('path');
3
4readFile(join(__dirname, 'test.txt'), 'utf-8', (err, data) => {
5 if (err) {
6 console.log(err.message);
7 }
8 console.log(data);
9})
path.basename():
1const path = require('path');
2
3const fpath = '/var/local/html/blog/index.html' // 文件存放路径
4
5/**
6 * 根据路径解析文件(目录)名
7 */
8var fullName = path.basename(fpath)
9var nameWithoutExt = path.basename(fpath, '.html')
10
11console.log(fullName);
12console.log(nameWithoutExt);
输出结果如下:
1index.html
2index
path.basename(path[, ext])参数和返回值:
-
path(必选):表示要解析的路径字符串。 -
ext(可选):表示文件扩展名(后缀)。经过测试后你会发现,
path.basename()只是将最后一段字符串解析出来,然后将其后缀中与ext相同的部分去除后返回。 -
返回值:路径中的最后一部分(有可能是文件名,也有可能是目录名)。当指定扩展名时,返回的有可能是不包含扩展名的名称。
与path.join()类似path.basename()仅仅是做关于路径的字符串解析,并不关心实际上是否存在这样的路径。path.basename()解析到的不仅是文件名,还可以是目录名。并且path.basename()在解析后缀时,如果当前路径的basename没有相应的后缀,path.basename()会将整个basename返回。
path.extname():
1const path = require('path');
2
3const fpath = '/var/local/html/blog/index.html' // 文件存放路径
4
5var extName = path.extname(fpath)
6console.log(extName);
输出结果为:
1.html
案例
案例1
使用fs文件系统模块,将文件中的考试数据,整理到新的文件中。
创建一个score.txt文件,内容如下:
1小红=99 小白=100 小黄=70 小黑=66 小绿=88
编写JS:
1// 1. 导入 fs
2const fs = require('fs')
3
4// 2. 读取文件内容
5fs.readFile('score.txt', 'utf-8', (err, dataStr) => {
6 // 3. 判断是否读取成功
7 if (err) {
8 return console.log('readFileErr: ' + err.message);
9 }
10 // console.log('Reading Success!');
11 // console.log(dataStr);
12
13 // 4.1 按照空格分割
14 const arrOld = dataStr.split(' ')
15 // console.log(arrOld);
16 // 4.2 循环分割后的数组,对每项数据进行替换操作
17 const arrNew = []
18 arrOld.forEach(item => {
19 arrNew.push(item.replace('=', ': '))
20 })
21 // console.log(arrNew);
22 // 4.3 把新数组中的每项进行合并,得到新字符串
23 const newStr = arrNew.join('\r\n')
24 // console.log(newStr);
25
26 // 5. 把处理完的成绩,写入新文件
27 fs.writeFile('score-ok.txt', newStr, (err) => {
28 if (err) {
29 return console.log('writeFileErr: ' + err.message);
30 }
31 console.log('Writing Success!');
32 })
33})
处理完的结果大致如下:
1小红: 99
2小白: 100
3小黄: 70
4小黑: 66
5小绿: 88
案例2
使用fs和path模块将下方HTML文件拆分成对应的html、css和js文件,并写入到clock目录下:
1<!DOCTYPE html>
2<html lang="en">
3
4<head>
5 <meta charset="UTF-8">
6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 <meta http-equiv="X-UA-Compatible" content="ie=edge">
8 <title>index首页</title>
9 <style>
10 html,
11 body {
12 margin: 0;
13 padding: 0;
14 height: 100%;
15 background-image: linear-gradient(to bottom right, red, gold);
16 }
17
18 .box {
19 width: 400px;
20 height: 250px;
21 background-color: rgba(255, 255, 255, 0.6);
22 border-radius: 6px;
23 position: absolute;
24 left: 50%;
25 top: 40%;
26 transform: translate(-50%, -50%);
27 box-shadow: 1px 1px 10px #fff;
28 text-shadow: 0px 1px 30px white;
29
30 display: flex;
31 justify-content: space-around;
32 align-items: center;
33 font-size: 70px;
34 user-select: none;
35 padding: 0 20px;
36
37 /* 盒子投影 */
38 -webkit-box-reflect: below 0px -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(0%, transparent), to(rgba(250, 250, 250, .2)));
39 }
40 </style>
41</head>
42
43<body>
44 <div class="box">
45 <div id="HH">00</div>
46 <div>:</div>
47 <div id="mm">00</div>
48 <div>:</div>
49 <div id="ss">00</div>
50 </div>
51
52 <script>
53 window.onload = function () {
54 // 定时器,每隔 1 秒执行 1 次
55 setInterval(() => {
56 var dt = new Date()
57 var HH = dt.getHours()
58 var mm = dt.getMinutes()
59 var ss = dt.getSeconds()
60
61 // 为页面上的元素赋值
62 document.querySelector('#HH').innerHTML = padZero(HH)
63 document.querySelector('#mm').innerHTML = padZero(mm)
64 document.querySelector('#ss').innerHTML = padZero(ss)
65 }, 1000)
66 }
67
68 // 补零函数
69 function padZero(n) {
70 return n > 9 ? n : '0' + n
71 }
72 </script>
73</body>
74
75</html>
html-handle.js:
1// 1.1 导入 fs 和 path 模块
2const fs = require('fs');
3const path = require('path');
4
5// 1.2 定义正则表达式
6const regStyle = /<style>[\s\S]*<\/style>/
7const regScript = /<script>[\s\S]*<\/script>/
8
9// 1.3 设置目录名并创建目录
10const DIR_NAME = 'clock'
11const DIR_FULL_PATH = path.join(__dirname, DIR_NAME)
12if (!fs.existsSync(DIR_FULL_PATH)) {
13 fs.mkdirSync(DIR_FULL_PATH)
14}
15
16// 2.1 读取文件
17fs.readFile(path.join(__dirname, 'index.html'), 'utf-8', (err, data) => {
18 // 2.2 读取失败时
19 if (err) {
20 return console.log(err.message);
21 }
22 // 2.3 读取成功后,调用对应的3个方法解析出css,js和html文件
23 resolveCSS(data)
24 resolveJS(data)
25 resolveHTML(data)
26})
27
28// 3.1 处理 CSS 样式文件
29function resolveCSS(htmlStr) {
30 // 3.2 使用正则提取页面的 <style></style>标签
31 const regStr = regStyle.exec(htmlStr)
32 // 3.3 将提取出来的字符串,进行替换
33 // console.log(regStr);
34 const newCSS = regStr[0].replace('<style>', '').replace('</style>', '')
35 // console.log(newCSS);
36 // 3.4 将提取的样式写入到新文件中
37 var fullPath = path.join(__dirname, DIR_NAME, 'index.css')
38 fs.writeFile(fullPath, newCSS, (err) => {
39 if (err) {
40 return console.log(err.message);
41 }
42 console.log('Writing css successful!');
43 })
44}
45
46// 4.1 处理 JS 样式文件
47function resolveJS(htmlStr) {
48 // 4.2 使用正则提取页面的 <script></script>标签
49 const regStr = regScript.exec(htmlStr)
50 // 4.3 将提取出来的字符串,进行替换
51 const newJS = regStr[0].replace('<script>', '').replace('</script>', '')
52 // 4.4 将提取的样式写入到新文件中
53 var fullPath = path.join(__dirname, DIR_NAME, 'index.js')
54 fs.writeFile(fullPath, newJS, (err) => {
55 if (err) {
56 return console.log(err.message);
57 }
58 console.log('Writing js successful!');
59 })
60}
61
62// 5. 处理 html 文件
63function resolveHTML(htmlStr) {
64 // 5.1 将内嵌的 <style> 和 <script> 替换为外联的 <link> 和 <script>
65 const newHTML = htmlStr
66 .replace(regStyle, '<link rel="stylesheet" href="./index.css" />')
67 .replace(regScript, '<script src="./index.js"></script>')
68 // 5.2 将替换完成之后的 html 代码,写入到 index.html 中
69 var fullPath = path.join(__dirname, DIR_NAME, 'index.html')
70 fs.writeFile(fullPath, newHTML, (err) => {
71 if (err) {
72 return console.log(err.message);
73 }
74 console.log('Writing html successful!');
75 })
76}
评论