引言
github地址:aizuyan/pinyin
无意中看到了overtrue/pinyin这个项目,感觉很有意思,
这个项目做了这么一件事情:
将汉字转化为拼音
刚看到这里是不是觉得没什么难度,没什么意思?您不妨接着往下看。要是只是将汉字转为拼音好像
很容易就实现了,但是要是给转换之后的汉字带上音调呢,这样难度就很大了,因为汉字博大精深,
其中一方面就表现在多音字
,同样一个字在不同的语句场景下,音调是不一样的。看到这里你在考
虑下如何处理?
替换的时候首先将常用的词组替换了,比如短语、成语、常用的词语,这些词语替换的时候按照常
用程度由高到低排序。
接着对剩余的未被替换的汉字进行替换,这里直接按照所有汉字和拼音的映射,没有特定顺序。
如果是姓名,首先对姓氏进行一遍特定替换,姓氏的声调可能和常用的不一致。
除了项目的想法很棒之外,还有一个大难题要解决,就是收集词语,姓氏等,这里我直接使用了安正 超
的项目中的数据,再次也该写下安前辈
。
我的这个项目可以说是很大程度上是安前辈
的overtrue/pinyin
项目的另一个版本。
设计
这个项目中会用到一个数据结构,单链表,用来存储拼音、汉字对应关系的数据,一开始想着使用Bu cket
数据结构,后来觉得,这个数据结构体里面冗余的信息太多了,也不是很符合我预想的数据结构,
so,最后使用了下面的数据结构:
1 typedef struct mylist { 2 char *key; //词语、成语或者单个汉字 3 char *val; //对应的拼音,拼音前面都有一个制表符` ` 4 struct mylist *next; //指向下一个汉字拼音结构体 5 } MyList;
除此之外,我还考虑了性能问题,安前辈php版本的有个固有的缺陷,每次请求都要去加载一遍数据文
件,大概有600~700kb左右,转换为数组,元素个数为40000~50000个左右,每次请求都会分配、释放这部分内
存。而且这个过程会有大量的计算过程(查找、替换),这也是php很不擅长的,如果用c语言会好很多。
因此,我就使用了PHP扩展,在模块初始化的时候,将所有配置数据载入内存,如果是fast-cgi
模式,
不用每次请求都加载一遍配置数据,只在进程启动的时候加载一遍。计算的话没有找到php里面比较合适的
函数,字节写了查找替换的函数。
还有就是如何读取配置文件数据了,我采用了下面的数据格式存储每一个汉-拼对“,csv个格式,每一行
第一列是短语、词语或者汉字,第二列是拼音,每个拼音之间使用制表符
分割,这样读取、进一步处理
就很方便了
1 汉字, han zi 2 ...... 3 {汉字|词语|短语}, pin yin
实现
实现部分,挑几个主要的函数出来:
首先是给链表中添加汉字拼音结构体的函数,这里有个地方要注意,这里使用了c语言原声的malloc
和strdup
,这是因为这个变量是全局的,不会随着请求的结束而销毁,而且也不会区分线程,因为所有的
线程都只会读取变量中的内容,所有的线程共享一套变量就可以了。
1 MyList *pinyin_list_append(MyList *last, const char *key, const char *value) 2 { 3 MyList *element = (MyList *)malloc(sizeof(MyList)); 4 char *newKey = strdup(key); 5 char *newVal = strdup(value); 6 element->key = newKey; 7 element->val = newVal; 8 element->next = NULL; 9 last->next = element; 10 11 return element; 12 }
下面这个函数是从一行通过逗号分隔的字符串中取出逗号前面的部分作为汉字
部分。
1 const char *get_key_from_line(const char *line, char *ret) 2 { 3 int i = 0; 4 while(*line) 5 { 6 if(*line != ',') 7 { 8 ret[i] = *line; 9 }else { 10 break; 11 } 12 i++; 13 line++; 14 } 15 ret[i] = '