綁定帳號登入

Android 台灣中文網

打印 上一主題 下一主題

[轉載] android 之 正则表达式解析LRC文件(歌詞文件)

[複製連結] 查看: 1965|回覆: 0|好評: 0
跳轉到指定樓層
樓主
lucky_jayce | 收聽TA | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
發表於 2013-5-27 15:49

馬上加入Android 台灣中文網,立即免費下載應用遊戲。

您需要 登錄 才可以下載或查看,沒有帳號?註冊

x
如果你要開發android的音乐,那势必涉及到解析lrc文件。
百度了很多文章,但最终没得到预期的效果。最终还是被我找到一篇合意的

原文链接[url]http://java-mzd.iteye.com/blog/811374[/url]


首先

我们应该明白LRC文件的组成

LRC文件本质就是个符合一定格式规范的文本文件

这一点对照XML文件就很好理解了

一个LRC文件的组成

通常由以下几个部分组成

[ti:约定]-------标题
[ar:周惠]------演唱者
[al:周蕙-精选]-------专辑
[00:26.00]远处的钟声回荡在雨里--------每句内容由一个时间点和内容组成

同时应该注意到
[02:23.00][00:49.00]一路从泥泞走到了美景---------在每个内容可能出现多个时间点  


然后

我们 用一个实体类

LrcInfo

来封装每个Lrc文件的具体内容


Java代码  [url=]

                               
登錄/註冊後可看大圖
[/url]


  • package javamzd.mp3player.Info;  
      
    import java.util.Map;  
      
    /**
    * 用来封装歌词信息的类
    * @author Administrator
    *
    */  
    public class LrcInfo {  
        private String title;//歌曲名  
        private String singer;//演唱者  
        private String album;//专辑     
        private Map<Long,String> infos;//保存歌词信息和时间点一一对应的Map  
       //以下为getter()  setter()  
          
    }


3.读入Lrc文件,开始逐行解析

   解析步骤:

     1.读入文件

     2.封装为BufferedReader对象

     3.调用readline()方法逐行读取数据,得到String str

     4.用parser()方法解析每一条具体的String语句

     5.每句解析完后,将得到的内容在LrcInfo对象中进行设置



Java代码  [url=]

                               
登錄/註冊後可看大圖
[/url]


  • package d;

    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.nio.charset.Charset;
    import java.util.Map;
    import java.util.TreeMap;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;

    /**
    * 此类用来解析LRC文件 将解析完整的LRC文件放入一个LrcInfo对象中 并且返回这个LrcInfo对象s author:java_mzd
    */
    public class LrcParser {
    private LrcInfo lrcinfo = new LrcInfo();

    private long currentTime = 0;// 存放临时时间
    private String currentContent = null;// 存放临时歌词
    private Map<Long, String> maps = new TreeMap<Long, String>();// 用户保存所有的歌词和时间点信息间的映射关系的Map

    /**
    * 根据文件路径,读取文件,返回一个输入流
    *
    * @param path
    *            路径
    * @return 输入流
    * @throws FileNotFoundException
    */
    private InputStream readLrcFile(String path) throws FileNotFoundException {
    File f = new File(path);
    InputStream ins = new FileInputStream(f);
    return ins;
    }

    public LrcInfo parser(String path) throws Exception {
    InputStream in = readLrcFile(path);
    lrcinfo = parser(in);
    return lrcinfo;

    }

    public LrcInfo parser(String path, Charset charset) throws Exception {
    InputStream in = readLrcFile(path);
    lrcinfo = parser(in, charset);
    return lrcinfo;
    }

    /**
    * 将输入流中的信息解析,返回一个LrcInfo对象
    *
    * @param inputStream
    *            输入流
    * @return 解析好的LrcInfo对象
    * @throws IOException
    */
    public LrcInfo parser(InputStream inputStream) throws IOException {
    return parser(inputStream, Charset.defaultCharset());
    }

    public LrcInfo parser(InputStream inputStream, Charset charset)
    throws IOException {
    // 三层包装
    InputStreamReader inr = new InputStreamReader(inputStream, charset);
    BufferedReader reader = new BufferedReader(inr);
    // 一行一行的读,每读一行,解析一行
    String line = null;
    while ((line = reader.readLine()) != null) {
    parserLine(line);
    }
    // 全部解析完后,设置info
    lrcinfo.setInfos(maps);
    return lrcinfo;
    }

    /**
    * 利用正则表达式解析每行具体语句 并在解析完该语句后,将解析出来的信息设置在LrcInfo对象中
    *
    * @param str
    */
    private void parserLine(String str) {
    // 取得歌曲名信息
    if (str.startsWith("[ti:")) {
    String title = str.substring(4, str.length() - 1);
    System.out.println("title--->" + title);
    lrcinfo.setTitle(title);

    }// 取得歌手信息
    else if (str.startsWith("[ar:")) {
    String singer = str.substring(4, str.length() - 1);
    System.out.println("singer--->" + singer);
    lrcinfo.setSinger(singer);

    }// 取得专辑信息
    else if (str.startsWith("[al:")) {
    String album = str.substring(4, str.length() - 1);
    System.out.println("album--->" + album);
    lrcinfo.setAlbum(album);

    }// 通过正则取得每句歌词信息
    else {
    // 设置正则规则
    String reg = "\\[(\\d{2}:\\d{2}\\.\\d{2})\\]";
    // 编译
    Pattern pattern = Pattern.compile(reg);
    Matcher matcher = pattern.matcher(str);

    // 如果存在匹配项,则执行以下操作
    while (matcher.find()) {
    // 得到匹配的所有内容
    String msg = matcher.group();
    // 得到这个匹配项开始的索引
    int start = matcher.start();
    // 得到这个匹配项结束的索引
    int end = matcher.end();

    // 得到这个匹配项中的组数
    int groupCount = matcher.groupCount();
    // 得到每个组中内容
    for (int i = 0; i <= groupCount; i++) {
    String timeStr = matcher.group(i);
    if (i == 1) {
    // 将第二组中的内容设置为当前的一个时间点
    currentTime = strToLong(timeStr);
    }
    }

    // 得到时间点后的内容
    String[] content = pattern.split(str);
    // 输出数组内容
    for (int i = 0; i < content.length; i++) {
    if (i == content.length - 1) {
    // 将内容设置为当前内容
    currentContent = content;
    }
    }
    // 设置时间点和内容的映射
    maps.put(currentTime, currentContent);
    }
    }
    }

    /**
    * 将解析得到的表示时间的字符转化为Long型
    *
    * @param group
    *            字符形式的时间点
    * @return Long形式的时间
    */
    private long strToLong(String timeStr) {
    // 因为给如的字符串的时间格式为XX:XX.XX,返回的long要求是以毫秒为单位
    // 1:使用:分割 2:使用.分割
    String[] s = timeStr.split(":");
    int min = Integer.parseInt(s[0]);
    String[] ss = s[1].split("\\.");
    int sec = Integer.parseInt(ss[0]);
    int mill = Integer.parseInt(ss[1]);
    return min * 60 * 1000 + sec * 1000 + mill * 10;
    }

    public static void main(String[] args) {
    LrcParser lp = new LrcParser();
    try {
    LrcInfo info = lp.parser("D:\\1.lrc", Charset.forName("gbk"));
    System.out.println(info);
    } catch (Exception e) {
    System.out.println("parser erro");
    e.printStackTrace();
    }

    }
    }


以上代码难度都不大

个人觉得

正则表达式其实并不难

只是因为有很多不规则符号堆叠在一起

让我们直观的很难理解

掌握符号规则后

还是挺好用的



正则表达在JAVA中都被封装在

regex包下面

主要是Pattern类与Matcher类



其实我个人在掌握了正则的基本概念后

用JAVA写这个代码却花了不少时间


主要是对这两个对象中的一些方法理解错误


以下简单总结下

两个类中易理解错的方法


Matcher对象中

matcher()方法是匹配整个字符串
lookingat()是匹配字符串的开头
find()是查找字符串中能否匹配


使用find()方法

得到一个字符串中的匹配后

matcher.start()得到这个匹配的startIndex
matcher.end()得到这个匹配的endIndex


matcher.group()能得到满足匹配的全部内容(最大的一个组)


matcher.groupCount()能得到当前匹配中的组数------(在正则中用()包围起来的一个部分算一个单独的组)
marcher.group(i) 得到指定的某个组的内容

又通过matcher.find()

我们可能在某一行可以得到多个匹配结果

每当调用一次matcher.find()

当前匹配对象就自动换为下个匹配成功对象


要遍历所有匹配结果




最后

我们解析完LRC文件后

在播放Mp3时

只需要根据播放时间

取出HashMap中的内容进行显示即可

「用Android 就來APK.TW」,快來加入粉絲吧!
Android 台灣中文網(APK.TW)

評分

參與人數 1碎鑽 +3 幫助 +1 收起 理由
ploglin + 3 + 1 轉載的話可以加入一些自己的心得會更好.

查看全部評分

收藏收藏2 分享分享 分享專題
用Android 就來Android 台灣中文網(https://apk.tw)
回覆

使用道具 舉報

您需要登錄後才可以回帖 登錄 | 註冊

本版積分規則