您当前的位置: 首页 > 学无止境 > 心得笔记 网站首页心得笔记
68_知识点如何显示歌词
发布时间:2021-03-10 18:45:04编辑:雪饮阅读()
这次要在昨天音乐播放器项目中完善以歌词的加载。
这个在安卓上面还不是很好做的,主要麻烦的是歌词的解析。
假定bslr.lrc(白色恋人)歌词文件如:
[ti:白色恋人]
[ar:游鸿明]
[al:诗人的眼泪]
[00:00.00]
[00:03.00]游鸿明-白色恋人
[00:06.00]
[00:43.10]冷空气却清晰
[00:47.53]你在南极冰山雪地里
[00:52.13]极光中雪白的肌肤
[00:56.72]是哀愁是美丽
[01:01.02]
[01:03.94]为了要遇见你
[01:08.51]我连呼吸都反复练习
[01:12.82]兰伯特仁慈的冰川
[01:17.50]带领我走向你
[01:21.33]
[02:40.32][01:24.49]零下九十一度的酷寒
[02:44.47][01:28.73]滚滚红尘千年的呼喊
[02:49.21][01:33.39]藏在沃斯托克的湖岸
[02:53.81][01:38.09]沉寂轻叹
[03:34.82][02:58.41][01:42.72]撒哈拉漫天狂沙
[03:39.74][03:03.07][01:47.26]金字塔谁能解答
[03:44.41][03:07.62][01:51.83]兵马俑谁与争锋
[03:49.72][03:12.92][01:56.97]长城万里相逢
[03:53.51][03:16.69][02:00.85]人世间悲欢聚散
[03:58.47][03:21.71][02:05.67]一页页写在心上
[04:02.66][03:25.99][02:10.21]含着泪白色恋人
[04:07.76][03:31.37][02:15.22]却有灰色的年轮
[04:11.74][02:19.10]
分析歌词,可以看出其实主要是带有时间段的中括号,和不带时间段的中括号,那么主要就是对带有时间段的中括号的解析(同一样行可能有多个时间段)以及带有时间段的中括号最好一个中括号之后的歌词。
这样纯文本的东西,不能直接加载到音乐播放器代码中,需要将这些信息解析以结构化比如list集合或map类集等。然后按照时间排序后就好处理了。
那么这里主要实现歌词解析以及以list或map形式存储,至于排序这个就小菜一碟了。这里不唠叨。
List实现
首先是以List的方式进行实现:Test.java:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.HashMap;
import java.util.Map;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.io.IOException;
public class Test{
public static void main(String[] args) {
printlrc();
}
public static void printlrc(){
LrcHandler lrcHandler=new LrcHandler();
try{
List<Map<Long, String>> list=lrcHandler.create("bslr.lrc");
System.out.println("----------------完整歌词信息-----------------");
if (list == null || list.isEmpty()) {
System.out.println("没有任何歌词信息!");
}
else{
for (Map<Long, String> map : list) {
for (Entry<Long, String> entry : map.entrySet()) {
System.out.println("时间:"+ entry.getKey() + "\t歌词:"+ entry.getValue());
}
}
}
}
catch(IOException e){
e.printStackTrace();
}
}
}
class LrcHandler{
public List<Map<Long, String>> create(String filePath) throws IOException{
List<Map<Long, String>> list = new ArrayList<Map<Long, String>>();
FileInputStream inputStream = new FileInputStream(filePath);
// 字符编码,若与歌词文件编码不符将会出现乱码
String encoding = "utf-8";
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,encoding));
String line = null;
while((line = bufferedReader.readLine()) != null)
{
Map<Long, String> map=this.handlerRow(line);
if(map!=null){
list.add(map);
}
}
inputStream.close();
bufferedReader.close();
return list;
}
private Map<Long, String> handlerRow(String row){
//先判断是否是以歌词时间段开头的
Matcher lineMatcher = Pattern.compile("^(\\[(\\d{2}:\\d{2}.\\d{2})\\]).*").matcher(row);
if (lineMatcher.matches()) {
System.out.println("是以歌词时间段开头的");
//查找中括号最右边(歌词)
String rightWord="";
//完整匹配
Matcher rightM = Pattern.compile("^((\\[\\d{2}:\\d{2}.\\d{2}\\])+)(.*)$").matcher(row);
if(rightM.find()){
rightWord=rightM.group(3);
System.out.println("歌词最右边:"+rightWord);
}
System.out.println("当前row:"+row);
//同一句歌词可能有多个时间段
Matcher timeM = Pattern.compile("(\\[(\\d{2}:\\d{2}.\\d{2})\\])").matcher(row);
//find局部查找,每次查找后会从上次位置继续向下局部查找
Map<Long, String> map = new HashMap<Long, String>();
while(timeM.find()){
//带中括号的时间段
String zkh=new String(timeM.group(1));
System.out.print("匹配出来:"+zkh);
System.out.print(" ");
//不带中括号的时间段
String pureTimeText=timeM.group(2);
System.out.print("匹配出来时间:"+pureTimeText);
//获取分、秒、毫秒
Matcher timemmh = Pattern.compile("\\[(\\d{2}):(\\d{2}).(\\d{2})\\]").matcher(zkh);
if(timemmh.find()){
String fen=timemmh.group(1);
String miao=timemmh.group(2);
String haomaio=timemmh.group(3);
System.out.print(" 分:"+timemmh.group(1));
System.out.print(" 秒:"+timemmh.group(2));
System.out.print(" 毫秒:"+timemmh.group(3));
//转换为纯毫秒
long totalHaomaio=Integer.parseInt(fen)*60*1000+Integer.parseInt(miao)*1000+Integer.parseInt(haomaio);
System.out.println(" 纯毫秒:"+totalHaomaio);
//形成map键值对
map.put(totalHaomaio, rightWord);
//此处可不能return,今天踩坑这里好久,还怀疑人家Matcher.find()
}
System.out.println("");
}
return map;
}
return null;
}
}
Map的实现
从代码逻辑上可以看到List实现这里依赖了Map,而且Map本身就是key-val的形式的集合。
所以用List反而麻烦了。
这里Map的实现则如Test2.java:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.HashMap;
import java.util.Map;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.io.IOException;
public class Test2{
public static void main(String[] args) {
printlrc();
}
public static void printlrc(){
try{
LrcHandler lrcHandler=new LrcHandler();
Map<Long, String> map=lrcHandler.create("bslr.lrc");
for (Entry<Long, String> entry : map.entrySet()) {
System.out.println("时间:"+ entry.getKey() + "\t歌词:"+ entry.getValue());
}
}
catch(IOException e){
e.printStackTrace();
}
}
}
class LrcHandler{
public Map<Long, String> create(String filePath) throws IOException{
Map<Long, String> map = new HashMap<Long, String>();
FileInputStream inputStream = new FileInputStream(filePath);
// 字符编码,若与歌词文件编码不符将会出现乱码
String encoding = "utf-8";
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream,encoding));
String line = null;
while((line = bufferedReader.readLine()) != null)
{
this.handlerRow(line,map);
}
inputStream.close();
bufferedReader.close();
return map;
}
private void handlerRow(String row,Map<Long, String> map){
//先判断是否是以歌词时间段开头的
Matcher lineMatcher = Pattern.compile("^(\\[(\\d{2}:\\d{2}.\\d{2})\\]).*").matcher(row);
if (lineMatcher.matches()) {
System.out.println("是以歌词时间段开头的");
//查找中括号最右边(歌词)
String rightWord="";
//完整匹配
Matcher rightM = Pattern.compile("^((\\[\\d{2}:\\d{2}.\\d{2}\\])+)(.*)$").matcher(row);
if(rightM.find()){
rightWord=rightM.group(3);
System.out.println("歌词最右边:"+rightWord);
}
System.out.println("当前row:"+row);
//同一句歌词可能有多个时间段
Matcher timeM = Pattern.compile("(\\[(\\d{2}:\\d{2}.\\d{2})\\])").matcher(row);
//find局部查找,每次查找后会从上次位置继续向下局部查找
//Map<Long, String> map = new HashMap<Long, String>();
while(timeM.find()){
//带中括号的时间段
String zkh=new String(timeM.group(1));
System.out.print("匹配出来:"+zkh);
System.out.print(" ");
//不带中括号的时间段
String pureTimeText=timeM.group(2);
System.out.print("匹配出来时间:"+pureTimeText);
//获取分、秒、毫秒
Matcher timemmh = Pattern.compile("\\[(\\d{2}):(\\d{2}).(\\d{2})\\]").matcher(zkh);
if(timemmh.find()){
String fen=timemmh.group(1);
String miao=timemmh.group(2);
String haomaio=timemmh.group(3);
System.out.print(" 分:"+timemmh.group(1));
System.out.print(" 秒:"+timemmh.group(2));
System.out.print(" 毫秒:"+timemmh.group(3));
//转换为纯毫秒
long totalHaomaio=Integer.parseInt(fen)*60*1000+Integer.parseInt(miao)*1000+Integer.parseInt(haomaio);
System.out.println(" 纯毫秒:"+totalHaomaio);
//形成map键值对
map.put(totalHaomaio, rightWord);
}
System.out.println("");
}
}
}
}
从代码量来看Map只有92行,而List有106行,还是节约了不少代码量,逻辑也更清晰了。
效果预览
无论哪种实现,效果基本都差不多,只是排序上可能有所不同,关于排序,可以根据自己情况来实现比较器等算法,或者其它方式进行排序。接下来具体看看效果,这样就完工了。
关键字词:android,音乐播放器,歌词,歌词解析
上一篇:67_音乐播放器
下一篇:69_soundpool的使用