标签:
接上一篇,接下来将来看看怎么实现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 }
好了,最后来看看我们该怎么使用这么个歌词解析库吧。具体参见下面的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 }
在Ubuntu下使用GCC 编译器对所以代码编译后运行截图如下。在此需要说明的一点是,在Microsof Visual Studio中如果直接编译上述代码可能会出现错误。具体可根据提示稍许修改即可。
-------------------------------------------------------------------END 全文完-------------------------------------------------------------------------
版权声明:本文为博主原创文章,未经博主允许不得转载。
标签:
原文地址:http://www.cnblogs.com/chriscabin/p/4659340.html