node 在控制台按时间滚动显示歌词
2019-10-12 本文已影响0人
LilyLaw
先捋一下实现的算法:
- 读入歌词
- 歌词分行
- 获取每一行歌词所出现的时间
- 通过
setTimeout
来延时显示
遇到的问题及解决方案:
-
问题:歌词
lrc
格式的文件一般都是gbk
编码,而node自身无法处理此种格式;
解决方案:引入第三方插件iconv-lite -
问题:按照算法逻辑实现后,发现与原声有细微差别,且歌词越长,差别越明显;
解决方案:除了往事件队列中放setTimeout,之前的代码执行也需要时间(比如循环遍历),而这些耗时应被歌词延时减去,从而去掉因之前代码执行所消耗的时间
实现
共有三种方法:1. 直接读整个文件;2. 通过流的方式读文件;3. 一行一行地来读文件。
我们先把公共代码提取出来:
// showlrc.js
let begin = new Date().getTime(),
reg = /\[(\d{2})\:(\d{2})\.(\d{2})\](.+)/; // 正则表达式 使用()分成四组
function showing(item){
let matches = reg.exec(item)
if(matches){
let min = parseFloat(matches[1]);
let sec = parseFloat(matches[2]);
let msec = parseFloat(matches[3]);
let allTime = min*60*1000+sec*1000+msec;
let offsetTime = new Date().getTime() - begin;
setTimeout(()=>{
console.log(matches[4]);
},allTime-offsetTime)
}else{
console.log(item);
}
}
module.exports = { showing }
方案1 直接读整个文件
const path = require('path');
const fs = require('fs');
const iconv = require('iconv-lite');
const showlrc = require('./showlrc.js');
let fileName = path.join(__dirname,'./files/lake.lrc')
fs.readFile(fileName,(err,data)=>{
if(err) throw err;
let lrcs = iconv.decode(data,'gbk'),
lines = lrcs.split('\r\n');
lines.forEach((item)=>{
showlrc.showing(item);
})
})
方案2 通过流的方式读文件
const path = require('path');
const fs = require('fs');
const iconv = require('iconv-lite');
const showlrc = require('./showlrc.js');
let fileName = path.join(__dirname,'./files/lake.lrc'),
streamReader = fs.createReadStream(fileName).pipe(iconv.decodeStream('gbk')),
fileData = '';
// 采用流的方式读取文档
streamReader.on('data',(chunk)=>{ // 开始连续读文档
fileData += chunk.toString();
});
streamReader.on('end',()=>{ // 读文档结束后触发
let lines = fileData.split('\r\n');
lines.forEach((item)=>{
showlrc.showing(item);
})
});
方案3 一行一行地来读文件
const path = require('path');
const fs = require('fs');
const iconv = require('iconv-lite');
const readline = require('readline');
const showlrc = require('./showlrc.js');
let fileName = path.join(__dirname,'./files/lake.lrc');
let streamReader = fs.createReadStream(fileName).pipe(iconv.decodeStream('gbk')),
rl = readline.createInterface({
input: streamReader
});
rl.on('line',(line)=>{
showlrc.showing(line);
})
总结
显然方案2和方案3因为节省内存而比较高效(一块一块地读或者一行一行地读,整个过程占内存较小),尤其是文件较大,行数较多的时候。