一、前言

上篇文章(设计模式学习(三):生成器(Builder)模式)记录了 Builder 模式的具体内容,这次使用C语言来实现一个实际的例子——基于Builder模式的歌词解析器。

二、示例介绍

歌词文件(.lrc)是一种文本文件,用来描述歌曲的歌词。在该文件的帮助下,音乐播放器可以根据相应时间同步显示歌词。歌词文件由时间标签、ID标签和歌词组成。

时间标签,例如:[00:23.25]

ID标签,例如:[ar:谭咏麟]

歌词,例如:凄雨冷风中 多少繁华如梦

下面是谭咏麟先生的歌曲 水中花 歌词的截取部分:

[ti:水中花]

[ar:谭咏麟]

[al:心手相连]

[by:孟德良]

[00:00.00]《水中花》

[00:02.00]演唱:谭咏麟

[00:04.00]作词:娃娃

[00:05.50]作曲:简宁

[00:07.00]

[00:09.03]凄雨冷风中 多少繁华如梦

[00:15.25]曾经万紫千红 随风吹落

[00:23.25]蓦然回首中 欢爱宛如烟云

[00:29.57]似水年华流走 不留影踪

[00:36.30]

[00:37.18]我看见 水中的花朵

[00:40.31]强要留住一抹红

[00:44.50]奈何辗转在风尘

[00:48.07]不再有往日颜色

[00:50.84]

......

......

[02:46.85]感怀飘零的花朵

[02:50.01]城市中无从寄托

[02:54.10]任那雨打风吹 也沉默

[02:57.90]仿佛是我

[03:00.12]

[03:01.72]啦…啦…啦…啦…

[03:16.09]啦…啦…啦…啦…

三、示例代码(C)

以下歌词解析器的代码参考了李先静老师基于AWTK实现的多媒体播放器项目(awtk-media-player),可前往 GitHub开源仓库 下载,其中歌词解析器位于awtk-media-player\src\media_player\lrc目录中。

3.1 Builder接口(lrc_builder.h)

#ifndef TK_LRC_BUILDER_H

#define TK_LRC_BUILDER_H

#include "tkc/types_def.h"

BEGIN_C_DECLS

struct _lrc_builder_t;

typedef struct _lrc_builder_t lrc_builder_t;

typedef ret_t (*lrc_builder_on_id_tag_t)(lrc_builder_t* builder, const char* key,

const char* value);

typedef ret_t (*lrc_builder_on_time_tag_t)(lrc_builder_t* builder, uint32_t start_time);

typedef ret_t (*lrc_builder_on_text_t)(lrc_builder_t* builder, const char* text);

typedef ret_t (*lrc_builder_on_error_t)(lrc_builder_t* builder, const char* error);

typedef ret_t (*lrc_builder_destroy_t)(lrc_builder_t* builder);

typedef struct _lrc_builder_vtable_t {

lrc_builder_on_text_t on_text;

lrc_builder_on_error_t on_error;

lrc_builder_on_id_tag_t on_id_tag;

lrc_builder_on_time_tag_t on_time_tag;

lrc_builder_destroy_t destroy;

} lrc_builder_vtable_t;

/**

* @class lrc_builder_t

* lrc builder

*/

struct _lrc_builder_t {

const lrc_builder_vtable_t* vt;

};

/**

* @method lrc_builder_on_id_tag

* 处理id标签。

*

* @param {lrc_builder_t*} builder lrc_builder对象。

* @param {const char*} id 名称。

* @param {const char*} value 值。

*

* @return {ret_t} 返回RET_OK表示成功,否则表示失败。

*/

ret_t lrc_builder_on_id_tag(lrc_builder_t* builder, const char* id, const char* value);

/**

* @method lrc_builder_on_time_tag

* 处理time标签。

*

* @param {lrc_builder_t*} builder lrc_builder对象。

* @param {uint32_t} timestamp 时间。

*

* @return {ret_t} 返回RET_OK表示成功,否则表示失败。

*/

ret_t lrc_builder_on_time_tag(lrc_builder_t* builder, uint32_t timestamp);

/**

* @method lrc_builder_on_text

* 处理歌词。

*

* @param {lrc_builder_t*} builder lrc_builder对象。

* @param {const char*} text 歌词。

*

* @return {ret_t} 返回RET_OK表示成功,否则表示失败。

*/

ret_t lrc_builder_on_text(lrc_builder_t* builder, const char* text);

/**

* @method lrc_builder_on_error

* 处理错误。

*

* @param {lrc_builder_t*} builder lrc_builder对象。

* @param {const char*} error 错误。

*

* @return {ret_t} 返回RET_OK表示成功,否则表示失败。

*/

ret_t lrc_builder_on_error(lrc_builder_t* builder, const char* error);

/**

* @method lrc_builder_destroy

* 销毁lrc builder对象。

*

* @param {lrc_builder_t*} builder lrc_builder对象。

*

* @return {ret_t} 返回RET_OK表示成功,否则表示失败。

*/

ret_t lrc_builder_destroy(lrc_builder_t* builder);

END_C_DECLS

#endif /*TK_LRC_BUILDER_H*/

3.2 Builder的实现一(lrc_builder_dump.c)

lrc_builder_dump:直接把解析的内容保存为文本,方便打印调试 以及 美化格式较乱的 lrc 文件,其主要代码如下:

#include "tkc/mem.h"

#include "tkc/utils.h"

#include "media_player/lrc/lrc_builder_dump.h"

static ret_t lrc_builder_dump_on_id_tag(lrc_builder_t* builder, const char* id, const char* value) {

lrc_builder_dump_t* dump = (lrc_builder_dump_t*)builder;

str_append(&(dump->result), "[");

str_append(&(dump->result), id);

str_append(&(dump->result), ":");

str_append(&(dump->result), value);

str_append(&(dump->result), "]");

return RET_OK;

}

static ret_t lrc_builder_dump_on_time_tag(lrc_builder_t* builder, uint32_t timestamp) {

char buff[64];

uint32_t m = timestamp / (1000 * 60);

double s = (timestamp % (1000 * 60)) / 1000.0f;

lrc_builder_dump_t* dump = (lrc_builder_dump_t*)builder;

tk_snprintf(buff, sizeof(buff), "[%02d:%2.2f]", m, s);

str_append(&(dump->result), buff);

return RET_OK;

}

static ret_t lrc_builder_dump_on_text(lrc_builder_t* builder, const char* text) {

lrc_builder_dump_t* dump = (lrc_builder_dump_t*)builder;

str_append(&(dump->result), text);

return RET_OK;

}

static ret_t lrc_builder_dump_on_error(lrc_builder_t* builder, const char* error) {

lrc_builder_dump_t* dump = (lrc_builder_dump_t*)builder;

str_append(&(dump->result), error);

return RET_OK;

}

static ret_t lrc_builder_dump_destroy(lrc_builder_t* builder) {

lrc_builder_dump_t* dump = (lrc_builder_dump_t*)builder;

str_reset(&(dump->result));

TKMEM_FREE(builder);

return RET_OK;

}

static const lrc_builder_vtable_t s_lrc_builder_dump_vtable = {

.on_text = lrc_builder_dump_on_text,

.on_id_tag = lrc_builder_dump_on_id_tag,

.on_time_tag = lrc_builder_dump_on_time_tag,

.on_error = lrc_builder_dump_on_error,

.destroy = lrc_builder_dump_destroy,

};

lrc_builder_t* lrc_builder_dump_create(void) {

lrc_builder_dump_t* dump = TKMEM_ZALLOC(lrc_builder_dump_t);

return_value_if_fail(dump != NULL, NULL);

str_init(&(dump->result), 0);

dump->lrc_builder.vt = &s_lrc_builder_dump_vtable;

return (lrc_builder_t*)dump;

}

3.3 Builder的实现二(lrc.c)

lrc:这是默认的builder,它负责把lrc文件构建成内存中的结构,以便查询,其主要代码如下:

#include "tkc/mem.h"

#include "media_player/lrc/lrc.h"

#include "media_player/lrc/lrc_parser.h"

#include "media_player/lrc/lrc_builder.h"

typedef struct _lrc_builder_default_t {

lrc_builder_t lrc_builder;

lrc_t* lrc;

char* p;

char* strs;

uint32_t size;

} lrc_builder_default_t;

#define lrc_isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r')

static const char* lrc_builder_default_dup(lrc_builder_default_t* b, const char* text) {

char* p = b->p;

uint32_t size = strlen(text);

const char* start = text;

const char* end = start + size - 1;

while (*start && lrc_isspace(*start)) start++;

while (end > start && lrc_isspace(*end)) end--;

size = end - start + 1;

memcpy(p, start, size);

p[size] = '\0';

b->p += size + 1;

return p;

}

#define DUP(text) lrc_builder_default_dup(b, text)

static ret_t lrc_builder_default_on_id_tag(lrc_builder_t* builder, const char* id,

const char* value) {

lrc_builder_default_t* b = (lrc_builder_default_t*)builder;

lrc_id_tag_list_append(b->lrc->id_tags, DUP(id), DUP(value));

return RET_OK;

}

static ret_t lrc_builder_default_on_time_tag(lrc_builder_t* builder, uint32_t timestamp) {

lrc_builder_default_t* b = (lrc_builder_default_t*)builder;

lrc_time_tag_list_append(b->lrc->time_tags, timestamp);

return RET_OK;

}

static ret_t lrc_builder_default_on_text(lrc_builder_t* builder, const char* text) {

lrc_builder_default_t* b = (lrc_builder_default_t*)builder;

lrc_time_tag_list_set_text(b->lrc->time_tags, DUP(text));

return RET_OK;

}

static ret_t lrc_builder_default_on_error(lrc_builder_t* builder, const char* error) {

log_debug("error:%s\n", error);

return RET_OK;

}

static ret_t lrc_builder_default_destroy(lrc_builder_t* builder) {

lrc_builder_default_t* b = (lrc_builder_default_t*)builder;

b->lrc->strs = b->strs;

return RET_OK;

}

static const lrc_builder_vtable_t s_lrc_builder_default_vtable = {

.on_text = lrc_builder_default_on_text,

.on_id_tag = lrc_builder_default_on_id_tag,

.on_time_tag = lrc_builder_default_on_time_tag,

.on_error = lrc_builder_default_on_error,

.destroy = lrc_builder_default_destroy,

};

lrc_builder_t* lrc_builder_default_init(lrc_builder_default_t* b, lrc_t* lrc, char* strs,

uint32_t size) {

return_value_if_fail(strs != NULL, NULL);

b->lrc = lrc;

b->p = strs;

b->strs = strs;

b->size = size;

memset(strs, 0x00, size);

b->lrc_builder.vt = &s_lrc_builder_default_vtable;

return (lrc_builder_t*)b;

}

static lrc_t* lrc_parse(lrc_t* lrc, const char* text) {

ret_t ret = RET_OK;

lrc_builder_default_t builder;

uint32_t size = strlen(text) + 1;

char* strs = TKMEM_ALLOC(size);

lrc_builder_t* b = lrc_builder_default_init(&builder, lrc, strs, size);

ret = lrc_parser_parse(b, text);

lrc_time_tag_list_sort(lrc->time_tags);

lrc_builder_destroy(&builder);

return ret == RET_OK ? lrc : NULL;

}

lrc_t* lrc_create(const char* text) {

lrc_t* lrc = NULL;

return_value_if_fail(text != NULL, NULL);

lrc = TKMEM_ZALLOC(lrc_t);

return_value_if_fail(lrc != NULL, NULL);

lrc->id_tags = lrc_id_tag_list_create();

lrc->time_tags = lrc_time_tag_list_create();

if (lrc->id_tags == NULL || lrc->time_tags == NULL) {

lrc_destroy(lrc);

lrc = NULL;

}

return_value_if_fail(lrc != NULL, NULL);

if (lrc_parse(lrc, text) == NULL) {

lrc_destroy(lrc);

lrc = NULL;

}

return lrc;

}

ret_t lrc_destroy(lrc_t* lrc) {

return_value_if_fail(lrc != NULL, RET_BAD_PARAMS);

lrc_id_tag_list_destroy(lrc->id_tags);

lrc_time_tag_list_destroy(lrc->time_tags);

TKMEM_FREE(lrc->strs);

TKMEM_FREE(lrc);

return RET_OK;

}

3.4 产品(Product)

根据上篇 文章 中的描述,Product 就是 ConcreteBuilder 产生的结果,不同的 ConcreteBuilder 所产生的 Product 是不同的。

在上面的例子中,lrc_builder_dump 产生的 Product 是一段文本。而Builder默认的实现(lrc)产生的 Product 是一个数据结构,如下:

/**

* @class lrc_t

* lrc

*/

typedef struct _lrc_t {

/**

* @property {lrc_id_tag_list_t*} id_tags

* @annotation ["readable"]

* id tags。

*/

lrc_id_tag_list_t* id_tags;

/**

* @property {lrc_time_tag_list_t*} time_tags

* @annotation ["readable"]

* time tags。

*/

lrc_time_tag_list_t* time_tags;

/*private*/

char* strs;

} lrc_t;

3.5 解析器(Director)的工作过程

解析器的任务就是解析出 lrc 文件最基本的元素:时间标签、ID标签和歌词,然后调用builder相应的函数表示出来,此处仅做例子,完整代码请参考 lrc_parser.c 。

static ret_t lrc_parser_parse_tag(lrc_parser_t* parser) {

lrc_parser_skip_chars(parser, "\t \r\n");

if (parser->p[0] == '\0') {

return RET_OK;

}

if (isdigit(parser->p[0])) {

return lrc_parser_parse_time_tag(parser);

} else {

return lrc_parser_parse_id_tag(parser);

}

}

static ret_t lrc_parser_parse_impl(lrc_parser_t* parser) {

lrc_parser_skip_text(parser);

while (TRUE) {

char c = parser->p[0];

if (c == '\0') {

break;

}

if (c == '[') {

parser->p++;

lrc_parser_parse_tag(parser);

if (parser->p[0] == ']') {

parser->p++;

}

} else {

lrc_parser_parse_text(parser);

}

}

return RET_OK;

}

ret_t lrc_parser_parse(lrc_builder_t* builder, const char* str) {

lrc_parser_t p;

ret_t ret = RET_OK;

return_value_if_fail(lrc_parser_init(&p, builder, str) == RET_OK, RET_BAD_PARAMS);

ret = lrc_parser_parse_impl(&p);

lrc_parser_deinit(&p);

return ret;

}

3.6 调用者(Client)

有了 解析器(Parser)和 相应的Builder 后,调用者需要把它们组合起来。解析完成时,调用者还希望从 Builder 取出 Product,以便后面使用。例如此处调用 lrc_create() 函数解析歌词文本(text参数)即可,代码如下:

static lrc_t* lrc_parse(lrc_t* lrc, const char* text) {

ret_t ret = RET_OK;

lrc_builder_default_t builder;

uint32_t size = strlen(text) + 1;

char* strs = TKMEM_ALLOC(size);

lrc_builder_t* b = lrc_builder_default_init(&builder, lrc, strs, size);

ret = lrc_parser_parse(b, text);

lrc_time_tag_list_sort(lrc->time_tags);

lrc_builder_destroy(&builder);

return ret == RET_OK ? lrc : NULL;

}

lrc_t* lrc_create(const char* text) {

lrc_t* lrc = NULL;

return_value_if_fail(text != NULL, NULL);

lrc = TKMEM_ZALLOC(lrc_t);

return_value_if_fail(lrc != NULL, NULL);

lrc->id_tags = lrc_id_tag_list_create();

lrc->time_tags = lrc_time_tag_list_create();

if (lrc->id_tags == NULL || lrc->time_tags == NULL) {

lrc_destroy(lrc);

lrc = NULL;

}

return_value_if_fail(lrc != NULL, NULL);

if (lrc_parse(lrc, text) == NULL) {

lrc_destroy(lrc);

lrc = NULL;

}

return lrc;

}

java歌词解析器_设计模式学习(四):基于Builder模式的歌词解析器相关推荐

  1. 设计模式学习(四):基于Builder模式的歌词解析器

    一.前言 上篇文章(设计模式学习(三):生成器(Builder)模式)记录了 Builder 模式的具体内容,这次使用C语言来实现一个实际的例子--基于Builder模式的歌词解析器. 本文的示例来自 ...

  2. 设计模式学习笔记——建造者(Builder)模式

    设计模式学习笔记--建造者(Builder)模式 @(设计模式)[设计模式, 建造者模式, builder] 设计模式学习笔记建造者Builder模式 基本介绍 建造者案例 类图 实现代码 Build ...

  3. 设计模式(四)--代理模式

    设计模式(四)–代理模式 文章目录 设计模式(四)--代理模式 其他链接 代理模式 1.简介 2.静态代理 角色 代码实现 好处 3.动态代理 代码实现 解析 其他链接 JVM学习笔记(一) JVM学 ...

  4. 设计模式学习笔记——解释器(Interpreter)模式

    设计模式学习笔记--解释器(Interpreter)模式 @(设计模式)[设计模式, 解释器模式, Interpreter] 设计模式学习笔记解释器Interpreter模式 基本介绍 解释器案例 类 ...

  5. 设计模式学习笔记——外观(Facade)模式

    设计模式学习笔记--外观(Facade)模式 @(设计模式)[设计模式, 外观模式, facade] 设计模式学习笔记外观Facade模式 基本介绍 外观案例 类图 实现代码 Database类 ma ...

  6. 设计模式学习笔记——命令(Command)模式

    设计模式学习笔记--命令(Command)模式 @(设计模式)[设计模式, 命令模式, command] 设计模式学习笔记命令Command模式 基本介绍 命令案例 类图 实现代码 Command接口 ...

  7. 设计模式学习笔记——状态(State)模式框架

    设计模式学习笔记--状态(State)模式框架 @(设计模式)[设计模式, 状态模式, State] 设计模式学习笔记状态State模式框架 基本介绍 状态案例 类图 实现代码 State接口 Day ...

  8. 设计模式学习笔记——备忘录(Memento)模式

    设计模式学习笔记--备忘录(Memento)模式 @(设计模式)[设计模式, 备忘录模式, memento] 设计模式学习笔记备忘录Memento模式 基本介绍 备忘录案例 类图 实现代码 Memen ...

  9. 设计模式学习笔记——观察者(Observer)模式

    设计模式学习笔记--观察者(Observer)模式 @(设计模式)[设计模式, 观察者模式, Observer] 设计模式学习笔记观察者Observer模式 基本介绍 观察者案例 类图 实现代码 Ob ...

  10. 设计模式学习笔记——访问者(Visitor)模式

    设计模式学习笔记--访问者(Visitor)模式 @(设计模式)[设计模式, 访问者模式, visitor] 设计模式学习笔记访问者Visitor模式 基本介绍 访问者案例 类图 实现代码 Visit ...

最新文章

  1. hive外部表改为内部表_3000字揭秘Greenplum的外部数据加载——外部表
  2. 如何判断ios设备中是否安装了某款应用
  3. 7-20上午上机题实现
  4. 世界对一名颓废者的惩罚——SDOI2019游记
  5. cocos2d-x初探学习笔记(3)--动作(CCAction)
  6. oracle怎么以时间排序,查询表怎样按修改时间排序
  7. 谷歌大脑提出DropBlock卷积正则化方法,显著改进CNN精度
  8. NG2入门 - 架构
  9. 人脸方向学习(十二):Face Detection-Tiny-DSOD解读
  10. AS函数的一些特殊应用
  11. 装箱问题 BPP first fit、best fit、first fit decreasing、best fit decreasing
  12. 网络工程师linux题,历年软考网络工程师Linux真题详解
  13. mysql数据库面试题大全(持续更新)
  14. c语言双人贪吃蛇小游戏代码,关于C 双人贪吃蛇显示的问题
  15. 微软官方Power Query M语言规范参考手册(Power Query M function reference)
  16. 错误排查:Cloudera Manager Agent 的 Parcel 目录位于可用空间小于 10.0 吉字节 的文件系统上。 /opt/cloudera/parcels...
  17. 深度解读|盘扣销售价格上涨背后的原因是什么?
  18. QPS、TPS、PV、UV、GMV、IP、RPS的概念解释
  19. root权限下手动安装open gapps.2022-01-31
  20. ADC和DAC的DNL和INL

热门文章

  1. cad打开a3样板图形_cad开始怎样设置A3纸?
  2. 雷赛控制卡可以用java写吗_运动控制卡应用编程技巧几招(2)
  3. 无法删除文件:无法读源文件或磁盘
  4. at91rm9200移植u-boot
  5. 腾讯产品策划类笔试题
  6. 百度网盘 自定义分享密码 提取码
  7. 预处理中的头文件stdio.h在哪?里面有什么?
  8. 对计算机数字不敏感怎么解决,英语听力对数字不敏感的解决办法
  9. 基于jsp、ssm企业工资管理系统
  10. BeX5安装遇到问题