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