码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2015-07-19 21:29:06      阅读:307      评论:0      收藏:0      [点我收藏+]

标签:

      接上一篇,接下来将来看看怎么实现lyric.c文件。

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

 

//initialize some global variables.
char* lrc_title = NULL;
char* lrc_artist = NULL;
char* lrc_album = NULL;
char* lrc_singer = NULL;
char* lrc_fileName = NULL;
double  lrc_offset = 0.0;
TextLine *(lrc_textLine[MAXTEXTLINECNT]);

static int lineIndex = 0;


      OK,我们再来回忆下LRC歌词的格式吧。容易发现,LRC文本中的都是以行分割的,准确的来说就是可以根据‘\n‘来分割。自然地,我们首先要做的事情就是想办法把每一行的歌词分割出来,然后再单独处理特定行即可。文本的分割在程序中是很常见的操作。在C#, C++等一些面向对象语言中都有方便的方法,比如C# String类中就有一个split()函数可以将文本按照特定的字符分割出来。那么,我们在C语言中有没有类似的分割函数呢?其实是有的,在<string.h>头文件中有这么个函数声明:char* strtok(char* str, const char* delim)。这个函数用起来其实很简单,但我们必须要小心地使用。比如,它是以更改原始字符串为代价的,因此如果你想在以后还要使用源字符串时,那在分割前记得要先拷贝一份。此外,我们不能用strtok()同时分割两个不同的字符串,你可以尝试在同一个函数中使用strtok()函数分割string1, string2,看看结果怎么样,此处不再赘述。

 

     

     好了,在实现头文件声明的那三个全局函数前,我们先看下在lyric.c中声明的若干个将会用到的"私有"函数吧。因为,我们希望使用不同的函数来做不同的工作,而不是一股脑地全塞给一个函数。那样的设计必然是不合理的。UNIX函数设计的一个原则便是,每个函数只做一件事情,分工应当明确。

 

//declaration of some "private" functions.
static STATUS   Initialize(void);
static void     GetLrcTags(const char *string, char* tag);
static void     GetOffset(char* string, double* offset);
static STATUS   GetTextLine(char* string, TextLine* textLine);
static long     GetLrcFileBytes(FILE *stream);
static LRCTYPE  GetLrcLineType(const char* lrcline);
static void     AnalyzeLrcLine(char* lrcline);
static void     Substring(const char *string, char *result, int from, int to);

      可以看到,上面的函数声明中都使用到了static关键字。当然,我们平时见到的static通常都是用来修饰一个变量的。比如,在函数中定义static int i = 0; 表示变量i的存储方式是静态存储的,它拥有和程序一样长的生命周期。然而,在这里用来修饰函数时,则是另外一种意义,即表明该函数是不同于全局函数的,它们在其它文件中是不可见的,而只能在当前的lyric.c中调用。这样做的好处有两点,第一,上述函数本来就不该对使用者开放,所以理应设计为“私有”的函数;第二,这样做的另一个好处是可以最大程度地避免命名空间的污染问题。

 

      对了,上面有个函数返回值是个枚举类型LRCTYPE,它的具体定义如下。定义这个枚举的用处主要是为了给每行作标记,比如哪一行是OFFSET,哪一行是歌词行等等。然后我们根据不同的返回值调用不同的函数来解析每行的文本。

 

enum LRCTYPE
{
    TITLE,
    ARTIST,
    ALBUM,
    SINGER,
    OFFSET,
    LRCTEXT,
    NONETYPE
};
typedef enum LRCTYPE LRCTYPE;

 

      下面就是每个函数的具体实现了。在此就不再对每个函数具体解释了,相信很容易看懂的。我不能保证每个函数都是完美正确的,我只是尽量让它们更加完美。如果你不幸在其中发现了一些奇怪的错误或者是一些小Bug之类的,也欢迎指正!

技术分享
  1 /*
  2  *Filename  lyric.c
  3  *Author    leomon.
  4  *Date      2014.4.14 to 2014.4.15.
  5  *Version   0.1.0
  6  *changelog nothing.
  7  */
  8 #include "lyric.h"
  9 
 10 enum LRCTYPE
 11 {
 12     TITLE,
 13     ARTIST,
 14     ALBUM,
 15     SINGER,
 16     OFFSET,
 17     LRCTEXT,
 18     NONETYPE
 19 };
 20 typedef enum LRCTYPE LRCTYPE;
 21 
 22 //initialize some global variables.
 23 char* lrc_title = NULL;
 24 char* lrc_artist = NULL;
 25 char* lrc_album = NULL;
 26 char* lrc_singer = NULL;
 27 char* lrc_fileName = NULL;
 28 double  lrc_offset = 0.0;
 29 TextLine *(lrc_textLine[MAXTEXTLINECNT]);
 30 
 31 static int lineIndex = 0;
 32 //declaration of some "private" functions.
 33 static STATUS   Initialize(void);
 34 static void     GetLrcTags(const char *string, char* tag);
 35 static void     GetOffset(char* string, double* offset);
 36 static STATUS   GetTextLine(char* string, TextLine* textLine);
 37 static long     GetLrcFileBytes(FILE *stream);
 38 static LRCTYPE  GetLrcLineType(const char* lrcline);
 39 static void     AnalyzeLrcLine(char* lrcline);
 40 static void     Substring(const char *string, char *result, int from, int to);
 41 
 42 //definition of all the functions.
 43 //hope you like them.
 44 
 45 //global functions:
 46 FILE* GetLrcFileStream(const char* fileName)
 47 {
 48     assert(fileName != NULL);
 49     //check first.
 50     FILE* stream = fopen(fileName, "r");
 51     if (stream == NULL)
 52     {
 53         printf("Sorry, I really cannot find file : ‘%s‘, please check it again! Thank you ^_^\n", fileName);
 54     }
 55     return stream;
 56 }
 57 
 58 //!Warning:We cannot use function names strok() to split two different strings at the
 59 //same time.
 60 //So I have to define another func. names Substring(), you can
 61 //use it at anytime you want. Most importantly, it dose not change the original
 62 //string.
 63 STATUS AnalyzeLrcFile(FILE* stream)
 64 {
 65     assert(stream != NULL);
 66     STATUS status = SUCCESSFUL;
 67     if (Initialize() == SUCCESSFUL)
 68     {
 69         long size = GetLrcFileBytes(stream);
 70         //When everything‘s done, remember to free the allocated area.
 71         char* lrcbuf = (char*)malloc((size * sizeof(char)));
 72         if (lrcbuf != NULL)
 73         {
 74             (void)fread(lrcbuf, size, 1, stream);
 75             //to save a single line text.
 76             char* lrcline = (char*)malloc(MAXLRCLINELEN * sizeof(char));
 77             if (lrcline != NULL)
 78             {
 79                 memset(lrcline, \0, MAXLRCLINELEN);
 80                 //
 81                 char* add = strtok(lrcbuf, "\n");
 82                 if (add == NULL)
 83                 {
 84                     status = FAILED;
 85                     return status;
 86                 }
 87                 strcpy(lrcline, add);
 88                 AnalyzeLrcLine(lrcline);
 89                 while(!feof(stream))
 90                 {
 91                     memset(lrcline, \0, MAXLRCLINELEN);
 92                     add = strtok(NULL, "\n");
 93                     if (add != NULL)
 94                     {
 95                         strcpy(lrcline, add);    //Get the next lrcline text.
 96 //                        printf("%s\n", lrcline);
 97                         AnalyzeLrcLine(lrcline);
 98                     }
 99                     else
100                     {
101                         break;
102                     }
103                 }
104 
105                 //free all the temp memory areas
106                 free(lrcbuf);
107                 free(lrcline);
108                 lrcbuf = NULL;
109                 lrcline = NULL;
110             }
111             else
112             {
113                 status = FAILED;
114             }
115         }
116         else
117         {
118             status = FAILED;
119         }
120     }
121     fclose(stream);
122     return status;
123 }
124 
125 void  DestroyLrc(void)
126 {
127     int i = 0;
128     //Free them all.
129     lineIndex = 0;
130     if (lrc_title != NULL)
131         free(lrc_title);
132     if (lrc_artist != NULL)
133         free(lrc_artist);
134     if (lrc_album != NULL)
135         free(lrc_album);
136     if (lrc_singer != NULL)
137         free(lrc_singer);
138     //free all the structs.
139     for (i=0; i<MAXTEXTLINECNT; i++)
140     {
141         if (lrc_textLine[i] != NULL)
142         {
143             free(lrc_textLine[i]);
144             lrc_textLine[i] = NULL;
145         }
146     }
147     lrc_title = NULL;
148     lrc_artist = NULL;
149     lrc_album = NULL;
150     lrc_singer = NULL;
151 }
152 
153 //private ones:
154 static STATUS Initialize(void)
155 {
156     STATUS status = SUCCESSFUL;
157     lineIndex = 0;
158     int i = 0;
159     lrc_offset = 0.0;
160     //Allocate enough memory for title, artist, etc. Then initilize them.
161     if (lrc_title == NULL)
162         lrc_title = (char*)malloc(MAXTAGLEN * (sizeof(char)));
163 
164     if (lrc_artist == NULL)
165         lrc_artist = (char*)malloc(MAXTAGLEN * (sizeof(char)));
166 
167     if (lrc_album == NULL)
168         lrc_album = (char*)malloc(MAXTAGLEN * (sizeof(char)));
169 
170     if (lrc_singer == NULL)
171         lrc_singer = (char*)malloc(MAXTAGLEN * (sizeof(char)));
172 
173     if (lrc_title != NULL && lrc_artist != NULL &&
174         lrc_album != NULL && lrc_singer != NULL)
175     {
176         status = SUCCESSFUL;
177         //initialize them.
178         memset(lrc_title, \0, MAXTAGLEN);
179         memset(lrc_artist, \0, MAXTAGLEN);
180         memset(lrc_album, \0, MAXTAGLEN);
181         memset(lrc_singer, \0, MAXTAGLEN);
182         //Initialize pointer-array.
183         for (i=0; i<MAXTEXTLINECNT; i++)
184             lrc_textLine[i] = NULL;
185     }
186     else
187     {
188         status = FAILED;
189     }
190     return status;
191 }
192 
193 static void GetLrcTags(const char *string, char *tag)
194 {
195     assert(string != NULL && tag != NULL);
196     //
197     int startPos = 0, endPos = 0;
198     char* ptr = NULL;
199     ptr = strchr(string, :);
200     if (ptr != NULL)
201     {
202         startPos = ptr - string;
203         ptr = strchr(string, ]);
204         if (ptr != NULL)
205         {
206             endPos = ptr - string;
207             Substring(string, tag, startPos + 1, endPos - 1);
208         }
209         else
210         {
211             return;
212         }
213     }
214     else
215     {
216         return;
217     }
218 }
219 
220 static void GetOffset(char *string, double *offset)
221 {
222     assert(string != NULL && offset != NULL);
223     char str[8];
224     memset(str, 0, 8);
225     int startPos = 0, endPos = 0;
226     char* ptr = NULL;
227     ptr = strchr(string, :);
228     if (ptr != NULL)
229     {
230         startPos = ptr - string;
231         ptr = strchr(string, ]);
232         if (ptr != NULL)
233         {
234             endPos = ptr - string;
235             Substring(string, str, startPos + 1, endPos - 1);
236             *offset = atof(str);
237         }
238         else
239         {
240             memset(str, 0, 8);
241             *offset = 0.0;
242             return;
243         }
244     }
245     else
246     {
247         memset(str, 0, 8);
248         *offset = 0.0;
249         return;
250     }
251 }
252 
253 static STATUS GetTextLine(char* string, TextLine* textLine)
254 {
255     assert(string != NULL && textLine != NULL);
256     STATUS status = SUCCESSFUL;
257     char buf[MAXTEXTLINECNT];
258     memset(buf, \0, MAXTEXTLINECNT);
259     int min = 0;
260     double sec = 0.0;
261     //[00:
262     int startPos = 0, endPos = 0;
263     char* p = strchr(string, [);
264     if (p != NULL)
265     {
266         startPos = p - string + 1;
267         p = strchr(string, :);
268         if (p != NULL)
269         {
270             endPos = p - string - 1;
271             Substring(string, buf, startPos, endPos);
272             //convert to int.
273             min = atoi(buf);
274             memset(buf, \0, MAXTEXTLINECNT);
275             startPos = endPos + 2;
276             p = strchr(string, ]);
277             if (p != NULL)
278             {
279                 endPos = p - string - 1;
280                 Substring(string, buf, startPos, endPos);
281                 sec = atof(buf);
282 
283                 memset(buf, \0, MAXTEXTLINECNT);
284                 startPos = endPos + 2;
285                 endPos = strlen(string);
286                 Substring(string, buf, startPos, endPos);
287 
288                 textLine->time = 1000 * (min * 60 + sec);
289                 strcpy(textLine->text, buf);
290             }
291             else
292             {
293                 status = FAILED;
294             }
295         }
296         else
297         {
298             status = FAILED;
299         }
300     }
301     else
302     {
303         status = FAILED;
304     }
305     return status;
306 }
307 
308 static long GetLrcFileBytes(FILE *stream)
309 {
310     assert(stream != NULL);
311     long size = 0L, curPos = 0L;
312     curPos = ftell(stream);
313 
314     fseek(stream, 0L, SEEK_END);
315     size = ftell(stream);
316 
317     fseek(stream, curPos, SEEK_SET);
318     return size;
319 }
320 
321 static void AnalyzeLrcLine(char* lrcline)
322 {
323     assert(lrcline != NULL);
324     int len = strlen(lrcline);
325     if (len >= 3)
326     {
327         LRCTYPE type = GetLrcLineType((lrcline));
328         if (type == LRCTEXT)
329         {
330             TextLine* p = (TextLine*)malloc(sizeof(TextLine));
331             if (GetTextLine(lrcline, p) == SUCCESSFUL)
332             {
333                 lrc_textLine[lineIndex++] = p;
334             }
335             else
336             {
337                 free(p);
338                 p = NULL;
339             }
340         }
341         else
342         {
343             switch (type)
344             {
345             case TITLE:
346                 GetLrcTags(lrcline, lrc_title);
347 //                printf("title: %s\n", title);
348                 break;
349             case ARTIST:
350                 GetLrcTags(lrcline, lrc_artist);
351 //                printf("artist: %s\n", artist);
352                 break;
353             case ALBUM:
354                 GetLrcTags(lrcline, lrc_album);
355 //                printf("album: %s\n", album);
356                 break;
357             case SINGER:
358                 GetLrcTags(lrcline, lrc_singer);
359                 break;
360             case OFFSET:
361                 GetOffset(lrcline, &lrc_offset);
362 //                printf("OFFSET: %lf\n", offset);
363                 break;
364             case NONETYPE:
365               printf("NONETYPE: %s\n", lrcline);
366                 break;
367             default:
368                 break;
369             }
370         }
371     }
372     else
373     {
374         return;
375     }
376 }
377 
378 static void Substring(const char* string, char* result, int from, int to)
379 {
380     assert(string != NULL && result != NULL);
381     if (from >= 0 && to >= from)
382     {
383         int i = 0, j = from;
384         while(string[j] != \0)
385         {
386             if (j > to)
387                 break;
388             result[i++] = string[j++];
389         }
390     }
391     else
392     {
393         return;
394     }
395 }
396 
397 static LRCTYPE GetLrcLineType(const char* lrcline)
398 {
399     assert(lrcline != NULL);
400     LRCTYPE type = NONETYPE;
401     char* ptr = NULL;
402     ptr = strchr(lrcline, [);
403     if (ptr != NULL)
404     {
405         if (((0 <= lrcline[1]) && (lrcline[1] <= 9)) &&
406                 ((0 <= lrcline[2]) && (lrcline[2]) <= 9))
407         {
408             return LRCTEXT;
409         }
410         else if (NULL != (strstr(lrcline, "ti")))
411         {
412             return TITLE;
413         }
414         else if (NULL != (strstr(lrcline, "by")))
415         {
416             return SINGER;
417         }
418         else if (NULL != (strstr(lrcline, "ar")))
419         {
420             return ARTIST;
421         }
422         else if (NULL != (strstr(lrcline, "al")))
423         {
424             return ALBUM;
425         }
426         else if (NULL != (strstr(lrcline, "off")))
427         {
428             return OFFSET;
429         }
430         else
431         {
432             return NONETYPE;
433         }
434     }
435     else
436     {
437         return NONETYPE;
438     }
439     return type;
440 }
View Code

      好了,最后来看看我们该怎么使用这么个歌词解析库吧。具体参见下面的main.c文件的实现:

技术分享
 1 /*
 2  *Filename  main.c
 3  *Author    leomon.
 4  *Date      2014.4.14 to 2014.4.15 2014.4.30
 5  *Version   0.1.2
 6  *changelog nothing.
 7  */
 8 #include "lyric.h"
 9 #include <time.h>
10 #define MAXFILENAMELEN 50
11 
12 int main(void)
13 {
14     char fileName[MAXFILENAMELEN];
15     int test_count = 3;
16     time_t t = time(NULL);
17     printf("/*\n**By leomon. All rights reserved.");
18     printf("\n**All codes are free to you. Do anything you want!");
19     printf("\n**Now Date&Time is : %s*/\n", ctime(&t));
20     printf("Press Enter key to continue(Note: On an Unix-like OS, press \"Ctrl+\\\" to quit program)...");
21     getchar();
22     while (test_count--)
23     {
24         printf("Please input your lrc filename(Note: On an Unix-like OS, the example is \"//home//leomon//filename.lrc\"):\n");
25         scanf("%s", fileName);
26         FILE* stream = GetLrcFileStream(fileName);
27         if (stream != NULL)
28         {
29             if (AnalyzeLrcFile(stream) == SUCCESSFUL)
30             {
31                 printf("/******************************************************************/\n");
32                 printf("Well, let‘s see, wow, it works perfectly!\n");
33                 printf("Note: If it is garbled text, it\‘s normal. Because only UTF-8 encoding is supported.\n\n");
34                 printf("#Tile of Song: %s\n", lrc_title);
35                 printf("#Album of Song: %s\n", lrc_album);
36                 printf("#Artist of Song: %s\n", lrc_artist);
37                 printf("#Singer of Song: %s\n", lrc_singer);
38                 printf("#Lrc Offset: %ld ms.\n\n", (long)lrc_offset);
39                 int i = 0;
40                 while(lrc_textLine[i] != NULL)
41                 {
42                     printf("#Time: %ld ms, Text: %s\n", (long)lrc_textLine[i]->time,
43                            lrc_textLine[i]->text);
44                     i++;
45                 }
46                 printf("It‘s time to destroy them all!\n");
47                 printf("/******************************************************************/\n");
48                 DestroyLrc();
49             }
50             else
51             {
52                 printf("Sorry, I tried. But I failed! Please check your file \‘%s\‘.\n", fileName);
53             }          
54         }
55     }
56     return 0;
57 }
View Code

        在Ubuntu下使用GCC 编译器对所以代码编译后运行截图如下。在此需要说明的一点是,在Microsof Visual Studio中如果直接编译上述代码可能会出现错误。具体可根据提示稍许修改即可。

技术分享


技术分享


-------------------------------------------------------------------END 全文完-------------------------------------------------------------------------

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

 

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

标签:

原文地址:http://www.cnblogs.com/chriscabin/p/4659340.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!