代码解析_100行代码 | 歌词解析器

将歌词文件拖放到窗口自动解析出歌词的小工具。去掉代码注释大概100多行左右。

1aa9ab721546f9035d4265414c65e4d1.gif

  这里摘取周杰伦 - 告白气球.lrc歌词文件的一小段内容。

[ti:告白气球]
[ar:周杰伦]
[al:周杰伦的床边故事]
[by:]
[offset:0]
[00:00.00]告白气球 - 周杰伦 (Jay Chou)
[00:07.86]词:方文山
[00:15.72]曲:周杰伦
[00:23.59]塞纳河畔 左岸的咖啡
[00:26.16]我手一杯 品尝你的美

0x01 歌词格式

  文件后缀一般为.lrc。而歌词内容分为以下两种格式:(1) 「标识标签」,其格式为[标识名:值],如:

[ti:告白气球] 
[ar:周杰伦]
[al:周杰伦的床边故事]
[by:]
[offset:0]

  对应英文缩写为:

缩写全称解释
tititle歌名
arartist歌手
alalbum专辑
byby该歌词文件制作者
offsetoffset歌词提前或延后的进度(单位为毫秒)

(2) 「时间标签」,其标准格式为[分钟:秒钟:毫秒],如:

[00:23.59]塞纳河畔 左岸的咖啡
[00:26.16]我手一杯 品尝你的美

0x02 歌词解析实现

  LyricParser为歌词结构体,内容包含歌名歌手歌词正文等信息。还包含一个共有函数parser,用于传入原始歌词内容并解析出对应的歌词信息。

/* 歌词结构体 */
struct LyricParser
{
    QString title;  /* 歌名 */
    QString artist; /* 歌手 */
    QString album;  /* 专辑 */
    QString lyricsEditor; /* LRC歌词制作者 */
    int     offset;       /* 进度补偿时值,500=0.5秒 */

    /**
     * 歌词正文内容(列表)
     * QTime类型为歌词时间
     * QString类型为对应的歌词
     */
    QList > contents;/* 传入歌词字符串解析出歌词信息 */void parse(const QString &string);private:/* 主要为解析歌词信息如:歌名,歌手,专辑等信息 */
    QString _parse(const QString &regexp, const QString &string);/* 解析歌词正文内容 */
    QList > parseContents(const QString &string);
};

  LyricParser结构体还包含_parseparseContents私有成员函数,都是为解析歌词而服务。下列为LyricParser的成员函数实现。

/* 传入歌词字符串解析出歌词信息 */
void parse(const QString &string){
    title        = _parse("^\\[ti:(.*)\\]$", string);
    artist       = _parse("^\\[ar:(.*)\\]$", string);
    album        = _parse("^\\[al:(.*)\\]$", string);
    lyricsEditor = _parse("^\\[by:(.*)\\]$", string);
    offset       = _parse("^\\[offset:(.*)\\]$", string).toInt();
    contents     = parseContents(string);
}

/* 主要为解析歌词信息如:歌名,歌手,专辑等信息 */
QString _parse(const QString &regexp, const QString &string)
{
    /* 创建一个正则表达式对象 */
    QRegularExpression re(regexp);
    /* 设置为多行匹配模式 */
    re.setPatternOptions(QRegularExpression::MultilineOption);

    /* 匹配操作,返回值为匹配结果 */
    QRegularExpressionMatch match = re.match(string);
    if (match.hasMatch() && re.captureCount() == 1) {
        /* 返回组捕获内容 */
        return match.captured(1);
    }

    return QString("");
}

/* 解析歌词正文内容 */
QList > parseContents(const QString &string)
{/* 歌词正文列表容器 */
    QList > out;/* 正则匹配歌词正文内容,匹配格式:"[00:00.00]内容" */QRegularExpression re("^\\[(?\\d+:\\d+(?:\\.\\d+)?)\\](?: *(?.*))?$");
    re.setPatternOptions(QRegularExpression::MultilineOption);/* 使用全局匹配 */
    QRegularExpressionMatchIterator iter = re.globalMatch(string);/* 遍历全局匹配到的内容 */while (iter.hasNext()) {
        QRegularExpressionMatch match = iter.next();/* 获取组捕获分组名为"time"的内容 */
        QTime t = QTime::fromString(match.captured("time"), "mm:ss.z");/* 获取组捕获分组名为"content"的内容 */
        out <"content"));
    }return out;
}

0x03 显示歌词

  使用QTableWidget显示歌词内容。拖拽歌词文件后解析歌词的结果放到m_lyricParser成员变量中,以用于refresh函数更新歌词内容:

/* 拖动事件的释放操作事件 */
void dropEvent(QDropEvent *event){
    /* 获取拖放的第一个目标文件 */
    QString targetFile = event->mimeData()->urls().first().toLocalFile();

    QFile file(targetFile);
    file.open(QFile::ReadOnly);
    /* 解析歌词内容 */
    m_lyricParser.parse(file.readAll());
    /* 刷新歌词内容到表格中 */
    refresh();
}

/* 拖动事件的进入操作事件 */
void dragEnterEvent(QDragEnterEvent* event){
    if (event->mimeData()->hasUrls()) {
        /* 表示可以在这个部件上拖放对象 */
        event->acceptProposedAction();
    }
    else {
        event->ignore();
    }
}

  更新歌词表格内容:

void refresh(){
    /* 将解析到的歌词显示在QTableWidget上 */
    QTableWidget *tableWidget = new QTableWidget(this);
    ...
    tableWidget->setItem(0, 0, new QTableWidgetItem(QStringLiteral("歌名")));
    tableWidget->setItem(0, 1, new QTableWidgetItem(m_lyricParser.title));

    tableWidget->setItem(1, 0, new QTableWidgetItem(QStringLiteral("歌手")));
    tableWidget->setItem(1, 1, new QTableWidgetItem(m_lyricParser.artist.isEmpty() ? QString("--") : m_lyricParser.artist));

    tableWidget->setItem(2, 0, new QTableWidgetItem(QStringLiteral("专辑")));
    tableWidget->setItem(2, 1, new QTableWidgetItem(m_lyricParser.album.isEmpty() ? QString("--") : m_lyricParser.album));

    tableWidget->setItem(3, 0, new QTableWidgetItem(QStringLiteral("歌词制作者")));
    tableWidget->setItem(3, 1, new QTableWidgetItem(m_lyricParser.lyricsEditor.isEmpty() ? QString("--") : m_lyricParser.lyricsEditor));

    tableWidget->setItem(4, 0, new QTableWidgetItem(QStringLiteral("进度补偿值")));
    tableWidget->setItem(4, 1, new QTableWidgetItem(QString::number(m_lyricParser.offset)));

    /* 遍历歌词正文内容,并逐一写入到表格中 */
    for (int i = 0; i         const QPair &each = m_lyricParser.contents.at(i);
        QTableWidgetItem *item1 = new QTableWidgetItem(each.first.toString("mm:ss.zzz"));
        QTableWidgetItem *item2 = new QTableWidgetItem(each.second);
        tableWidget->setItem(i + 5, 0, item1);
        tableWidget->setItem(i + 5, 1, item2);
    }
    tableWidget->show();
}

关于更多

  • 完整源码请在Qt君公众号聊天界面回复「歌词解析器源码」获取源码地址。
  • 也可加入Qt技术交流群:732271126后在群文件获取。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值