看看如何用C语言解析LRC格式的歌词(上)

嗯,本该昨晚写的,结果因为系统出了点问题,所以便拖到了今天早上。好了,不废话了。

经常听歌的,必然都见过歌词吧。当然,我们这里要说的歌词格式是以LRC为后缀的歌词。此外,还有QRC,KRC格式的歌词。但相对来说,LRC格式歌词更为常见。我们先来看看LRC格式的歌词究竟长什么样:

好了,我们该怎么解读这种格式的歌词呢?推荐看百度百科对它的介绍,在此就不废话了。

相信写过音乐播放的或者想要写音乐播放器的都会涉及到歌词文件的解析吧。自然,使用高级语言可能更为方便一些。比如,我们可以用正则匹配的方式获得我们需要的时间和歌词行等等。但今天我们只用C语言,依靠C语言的库函数和自己编写的一些函数来实现对上面的歌词进行解析。额,下面的内容可能有点烦琐,此外,算法也不是最佳的。但,个人觉得有一点最大的好处,就是能够熟悉对C语言库函数的使用。比如,我们会用到malloc()/free()函数来动态管理内存,还会用到<string.h>中的字符串处理的函数等等。

Ok, 既然要用最土的办法来解析的话,我们就来理理思路吧。

首先,想法大致是这样的,我们可以设置几个全局变量或者全局数组来存放诸如歌曲的名称,歌曲所属唱片,歌曲的演唱人,以及偏移OFFSET等。所以,我们可以在头文件lyric.h中声明这么几个全局变量:

extern  char* lrc_title;
extern  char* lrc_artist;
extern  char* lrc_album;
extern  char* lrc_singer;
extern  char* lrc_fileName;
extern  double  lrc_offset; //milliseconds.

可以看到,我并没有直接声明成extern char lrc_title[100]这种形式,因为,将要在真正解析的时候使用动态申请内存的方式来实现对这些信息的存储。然后在我们不需要时候释放掉这些存储空间。

Well,再来看看怎么处理歌词正文吧。我们发现每一行歌词的前面都有对应的时间。当然,播放器也正是根据这些时间来显示对应行的歌词的。那么我们该怎么才能将时间对应到相应的歌词行呢?学过Java, C#, C++之类的高级语言的必定都用过类似的一个“容器”Map<T1 Key, T2 Value>,当然,可能名字不是这样,但功能都是类似的。Map<T1 Key, T2 Value>就允许我们将键值对存储进去。就像字典一样,每个单词都有对应的释义。但我们怎么在C语言中实现类似的功能呢?显示,我们不可能做到像Map<T1 Key, T2 Value>这样先进好用的容器。不过,C语言的结构体却可以帮助我们实现一个类似的功能较弱的“容器”。先看下我在头文件lyric.h中的结构体定义:

struct  TextLine
{
    double time;    ////milliseconds.
    char text[MAXLRCLINELEN];
};

可见,我是想办法把歌词前面的时间全部转换成毫秒再存放到time中,然后将时间对应的一行歌词存放到text[MAXLRCLINELEN]数组中。这里,之所以没有用char* text形式,主要是为了方便后面的处理。因为上面的lrc_title之类的将会演示这种动态分配内存的用法,所以就不再重复了。此外,我还想提到的是,C99还添加了一个叫做“柔性”数组的特性。也就是说,在上面的结构体中可以换成下面的方式:

struct  TextLine
{
    double time;    ////milliseconds.
    char text[];
};

然后,我们可以在解析的时候适当地分配一定大小的空间来存放歌词文本。不过在下面的代码中不再演示这种用法了。不知道“柔性”数组用法的可以自行Google一下。

好了,进入下一个环节。既然我们想好了怎么进行存储了,自然就应该设计一些重要的跟歌词解析有关的函数了。首先看下我在lyric.h中声明的全局函数如下:

//declaration of some global functions, which can be called in other files.
FILE*   GetLrcFileStream(const char* fileName);
STATUS  AnalyzeLrcFile(FILE* stream);
void    DestroyLrc(void);

这三个全局函数分别负责不同的任务。

1、函数:FILE* GetLrcFileStream(1)

用来将根据用户输入的文件名尝试以只读方式打开文件并返回"文件流"。

2、函数:STATUS AnalyzeLrcFile(1)

解析歌词的函数,它用来接收一个“文件流”,并将解析后的歌词信息存储到我们在上面声明的全局变量中,或者是结构体数组中。最后,会返回一个标志解析成功或者失败的状态。

3、函数:
void DestroyLrc()

这个是销毁函数。之所以设计这么一个函数,主要是因为我们可以在结束的时候将之前申请的堆内存空间释放出来,这样才能体现动态内存管理的实质 。其实,关于这个DestroyLrc()函数的实现还是得要小心的,因为我们必须要记住哪些申请的存储空间是要在最后释放的,并且能够正确的释放掉。同时,我们还要避免“迷途指针”的出现。这些具体的细节,将会在下一篇中给出具体的实现。

最后,在看如何实现上面声明的函数之前,我们先来看看头文件lyric.h吧:

 1 /*
 2  *Filename  lyric.h
 3  *Author    leomon.
 4  *Date      2014.4.14 to 2014.4.15.
 5  *Version   0.1.0
 6  *changelog nothing.
 7  */
 8 #ifndef LYRIC_H
 9 #define LYRIC_H
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <assert.h>
15 #include <ctype.h>
16
17 #define MAXTITLELEN     50
18 #define MAXTAGLEN       50
19 #define MAXLRCLINELEN   128
20 #define MAXTEXTLINECNT  128
21
22 struct  TextLine
23 {
24     double time;    ////milliseconds.
25     char text[MAXLRCLINELEN];
26 };
27
28 enum    STATUS
29 {
30     SUCCESSFUL,
31     FAILED
32 };
33
34 typedef enum STATUS STATUS;
35 typedef struct TextLine TextLine;
36
37 //declaration of some global variables.
38 extern  char* lrc_title;
39 extern  char* lrc_artist;
40 extern  char* lrc_album;
41 extern  char* lrc_singer;
42 extern  char* lrc_fileName;
43 extern  double  lrc_offset; //milliseconds.
44 extern  TextLine *(lrc_textLine[MAXTEXTLINECNT]);
45
46 //declaration of some global functions, which can be called in other files.
47 FILE*   GetLrcFileStream(const char* fileName);
48 STATUS  AnalyzeLrcFile(FILE* stream);
49 void    DestroyLrc(void);
50 #endif // LYRIC_H

下一篇将会给出所有函数的实现细节。本打算放在一篇中搞定的,但发现实在太长了,看起来也不舒服。好了,又要上课去了。^_^

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-02 23:40:31

看看如何用C语言解析LRC格式的歌词(上)的相关文章

看看如何用C语言解析LRC格式的歌词(下)

接上一篇,接下来将来看看怎么实现lyric.c文件. 嗯,在详细说明每个实现的函数之前,首先要在lyric.c中实现在头文件中声明的一些全局变量的定义.当然,要记得定义指针时应拴住指针到一个合法的位置处.因为一个未初始化的指针可能指向内存中任何一个位置,或许是个合法可访问的位置,又或许是个非法不可访问的位置.所以,为了安全起见,我们应当养成好习惯,即在指针初始化时指向一个确定的位置.具体定义如下: //initialize some global variables. char* lrc_tit

歌词文件解析(二):LRC格式文件的绘制

通过对LRC文件的解析,可以轻松实现歌词可视化. 函数名: paintLyrics(ByVal pBox As PictureBox, ByVal CurrentPosition As Integer, ByVal type As Boolean, Optional ByVal pLyric As LyricClass = Nothing) 参数: pBox:用于绘制歌词的Picturebox CurrentPosition:当前歌词的时间进度,单位为秒(Second) type:值为True时

Java使用正则表达式解析LRC歌词文件

LRC歌词是一种应用广泛的歌词文件,对其进行解析时 标准格式: [分钟:秒.毫秒] 歌词 1 import java.io.BufferedReader; 2 import java.io.File; 3 import java.io.FileInputStream; 4 import java.io.InputStreamReader; 5 import java.util.ArrayList; 6 import java.util.HashMap; 7 import java.util.Li

C# 解析JSON格式数据

JSON简介 JSON(全称为JavaScript ObjectNotation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集.JSON采用完全独立于语言的文本格式,可以很容易在各种网络.平台和程序之间传输.JSON的语法很简单,易于人阅读和编写,同时也易于机器解析和生成. JSON与XML的比较 ◆可读性 JSON和XML的可读性相比较而言,由于XML提供辅助的标签,更加适合人阅读和理解. ◆文件大小与传输 XML允许使用方便的标签,所以文件尺寸是要比JSON大

【转】C# 解析JSON格式数据

http://blog.csdn.net/coolszy/article/details/8606803 JSON简介 JSON(全称为JavaScript ObjectNotation) 是一种轻量级的数据交换格式.它是基于JavaScript语法标准的一个子集.JSON采用完全独立于语言的文本格式,可以很容易在各种网络.平台和程序之间传输.JSON的语法很简单,易于人阅读和编写,同时也易于机器解析和生成. JSON与XML的比较 ◆可读性 JSON和XML的可读性相比较而言,由于XML提供辅

iOS开发之解析XML格式数据

XML格式的数据是一种数据的传输格式.因为它方便编写.结构清晰,所以深受程序猿的喜爱,非常多人都喜欢使用XML格式数据传输或者作为程序的配置信息. 如今我将来实如今iOS中解析XML格式数据,语言使用Swift. 首先自己写一个简单的XML: <data> <person age="15">zhangsan</person> <person age="2">lisi</person> <person&

我的Android进阶之旅------&gt;Android自定义View来实现解析lrc歌词并同步滚动、上下拖动、缩放歌词的功能

前言 一LRC歌词文件简介 1什么是LRC歌词文件 2LRC歌词文件的格式 LRC歌词文件的标签类型 1标识标签 2时间标签 二解析LRC歌词 1读取出歌词文件 2解析得到的歌词内容 1表示每行歌词内容的实体类LrcRow 2解析歌词的构造器 ILrcBuilder接口 DefaultLrcBuilder歌词解析构造器 lrc歌词原始内容 lrc歌词解析后的内容 三显示LRC歌词内容 1定义一个ILrcViewListener接口 2定义一个ILrcView接口 3自定义一个LrcView 同步

如何用C#语言构造蜘蛛程序

"蜘蛛"(Spider)是Internet上一种很有用的程序,搜索引擎利用蜘蛛程序将Web页面收集到数据库,企业利用蜘蛛程序监视竞争对手的网站并跟踪变动,个人用户用蜘蛛程序下载Web页面以便脱机使用,开发者利用蜘蛛程序扫描自己的Web检查无效的链接……对于不同的用户,蜘蛛程序有不同的用途.那么,蜘蛛程序到底是怎样工作的呢? 蜘蛛是一种半自动的程序,就象现实当中的蜘蛛在它的Web(蜘蛛网)上旅行一样,蜘蛛程序也按照类似的方式在Web链接织成的网上旅行.蜘蛛程序之所以是半自动的,是因为它总

Jquery解析Json格式数据

今天稍微学习了一下Json,JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式. 易于人阅读和编写.同时也易于机器解析和生成. JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等). 这些特性使JSON成为理想的数据交换语言. JSON建构于两种结构: “名称/值”对的集合(A collection of name/value pairs)