汉字转【pinyin】

引言

github地址:aizuyan/pinyin

无意中看到了overtrue/pinyin这个项目,感觉很有意思,
这个项目做了这么一件事情:

将汉字转化为拼音

刚看到这里是不是觉得没什么难度,没什么意思?您不妨接着往下看。要是只是将汉字转为拼音好像
很容易就实现了,但是要是给转换之后的汉字带上音调呢,这样难度就很大了,因为汉字博大精深,
其中一方面就表现在多音字,同样一个字在不同的语句场景下,音调是不一样的。看到这里你在考
虑下如何处理?

替换的时候首先将常用的词组替换了,比如短语、成语、常用的词语,这些词语替换的时候按照常
用程度由高到低排序。

接着对剩余的未被替换的汉字进行替换,这里直接按照所有汉字和拼音的映射,没有特定顺序。

如果是姓名,首先对姓氏进行一遍特定替换,姓氏的声调可能和常用的不一致。

除了项目的想法很棒之外,还有一个大难题要解决,就是收集词语,姓氏等,这里我直接使用了安正 超的项目中的数据,再次也该写下安前辈

我的这个项目可以说是很大程度上是安前辈overtrue/pinyin项目的另一个版本。

设计

这个项目中会用到一个数据结构,单链表,用来存储拼音、汉字对应关系的数据,一开始想着使用Bu cket数据结构,后来觉得,这个数据结构体里面冗余的信息太多了,也不是很符合我预想的数据结构,
so,最后使用了下面的数据结构:

1 typedef struct mylist {
2     char *key;  //词语、成语或者单个汉字
3     char *val;  //对应的拼音,拼音前面都有一个制表符`\t`
4     struct mylist *next;    //指向下一个汉字拼音结构体
5 } MyList;

除此之外,我还考虑了性能问题,安前辈php版本的有个固有的缺陷,每次请求都要去加载一遍数据文
件,大概有600~700kb左右,转换为数组,元素个数为40000~50000个左右,每次请求都会分配、释放这部分内
存。而且这个过程会有大量的计算过程(查找、替换),这也是php很不擅长的,如果用c语言会好很多。

因此,我就使用了PHP扩展,在模块初始化的时候,将所有配置数据载入内存,如果是fast-cgi模式,
不用每次请求都加载一遍配置数据,只在进程启动的时候加载一遍。计算的话没有找到php里面比较合适的
函数,字节写了查找替换的函数。

还有就是如何读取配置文件数据了,我采用了下面的数据格式存储每一个汉-拼对“,csv个格式,每一行
第一列是短语、词语或者汉字,第二列是拼音,每个拼音之间使用制表符\t分割,这样读取、进一步处理
就很方便了

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] = ‘\0‘;
16     return ret;
17 }

下面是同一行中分离出拼音部分:

 1 const char *get_val_from_line(const char *line, char *ret)
 2 {
 3     int i = 0;
 4     int flag = 0;
 5     while(*line)
 6     {
 7         if(*line == ‘\n‘)
 8         {
 9             break;
10         }
11         if(*line == ‘,‘)
12         {
13             flag = 1;
14             line++;
15             continue;
16         }else if(!flag) {
17             line++;
18             continue;
19         }
20         ret[i] = *line;
21         i++;
22         line++;
23     }
24     ret[i] = ‘\0‘;
25     return ret;
26 }

下面是最重要的一个,替换字符换函数,from是要替换的字符串,to是要替换为的字符串,
str是原始字符串,ret是临时字符串,会保存临时的结果,is_name表示是否是姓名,
如果是姓名,只替换一次。

 1 void str_replace(const char *from, const char *to, char *str, char *ret, zend_bool is_name)
 2 {
 3     int pos = 0,
 4         fromLen = strlen(from),
 5         flag = 0;
 6     char *tmp = NULL,
 7          *strTmp = str;
 8     while(tmp = strstr(str, from))
 9     {
10         pos = tmp - str;
11         strncat(ret, str, pos);
12         strcat(ret, to);
13         str = tmp + fromLen;
14         flag = 1;
15         if(is_name)
16             break;
17     }
18
19     strcat(ret, str);
20     if(1 == flag)
21     {
22         memcpy(strTmp, ret, strlen(ret));
23         strTmp[strlen(ret)] = ‘\0‘;
24     }
25 }

使用

只通过一个函数和标志位来实现,使用起来也是很方便的:
使用的时候可以参考github中的README.md,里面有详细的编译配置细节。

例子
print_r(chinese_to_pinyin("彪悍的人生不需要解释!"));

输出内容,带音标、带标点(标点和拼音挤在一起)

 1 Array
 2 (
 3     [0] => biāo
 4     [1] => hàn
 5     [2] => de
 6     [3] => rén
 7     [4] => shēng
 8     [5] => bù
 9     [6] => xū
10     [7] => yào
11     [8] => jiě
12     [9] => shì!
13 )
print_r(chinese_to_pinyin("彪悍的人生不需要解释!", PINYIN_NONE|PINYIN_FORMAT_EN));

输出结果,不带音标,标点符号单独开了:

 1 Array
 2 (
 3     [0] => biao
 4     [1] => han
 5     [2] => de
 6     [3] => ren
 7     [4] => sheng
 8     [5] => bu
 9     [6] => xu
10     [7] => yao
11     [8] => jie
12     [9] => shi
13     [10] => !
14 )
1 print_r(chinese_to_pinyin("燕睿涛"));
2 print_r(chinese_to_pinyin("燕睿涛", PINYIN_ISNAME));
3 print_r(chinese_to_pinyin("罗永浩", PINYIN_ISNAME));

输出内容,可以看出PINYIN_ISNAME这个标志位还是很有用的,

 1 rray
 2 (
 3     [0] => yàn
 4     [1] => ruì
 5     [2] => tāo
 6 )
 7 Array
 8 (
 9     [0] => yān
10     [1] => ruì
11     [2] => tāo
12 )
13 Array
14 (
15     [0] => luō
16     [1] => yǒng
17     [2] => hào
18 )

初次之外,还有些关于标志位的使用规律:

PINYIN_NONE、PINYIN_UNICODE两个是对立的,使用前者没有音调,使用后者有音调,默认是前者。

PINYIN_TRIM、PINYIN_FORMAT_EN、PINYIN_FORMAT_CH是对立的,第一个清除所有标点、第二个
使用英文标点,第三个使用中文标点

PINYIN_ISNAME 如果设置了这个标志位,会使用姓氏的规则去解析读音。

总结

这是第二个PHP扩展了,这次写起来跟1年前相比容易了许多,错误也比较少了,继续努力吧~

不要停止学习的脚步,提高自身核心竞争力。

时间: 2024-11-10 00:23:04

汉字转【pinyin】的相关文章

搜索引擎关键字智能提示的一种实现

问题背景 搜索关键字智能提示是一个搜索应用的标配,主要作用是避免用户输入错误的搜索词,并将用户引导到相应的关键词上,以提升用户搜索体验. 美团CRM系统中存在数以百万计的商家,为了让用户快速查找到目标商家,我们基于solrcloud实现了商家搜索模块.用户在查找商家时主要输入商户名.商户地址进行搜索,为了提升用户的搜索体验和输入效率,本文实现了一种基于solr前缀匹配查询关键字智能提示(Suggestion)实现. 需求分析 支持前缀匹配原则 在搜索框中输入"海底",搜索框下面会以海底

基于搜索推荐系统根据用户搜索频率(热搜)排序

之前写的三叉树,有点儿简单,并不能满足实际项目的需要.先简单分析一下solr中搜索推荐系统的核心算法. wiki中有关于solr的搜索推荐的详细描述,但是核心算法需要自己查看源代码.关于wiki上的解读,之前做了一次简单的翻译,根据此文档,详细研读了源代码,先把核心思想呈现出来. 基本流程如下:当用户输入搜索词语前缀时,通过前端调用solr的suggest,找到Suggeser对象,Suggester根据匹配的field从主索引库中读取field下面的terms,来构建dictionry,由于主

字母条索引定位

activity_main.xml <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/listview" an

手机联系人快速索引

这是一个手机联系人快速索引的效果,总体来说代码不算难,拼音转换的地方略有复杂.下面上源码:源码中有注释. MainActivity: import java.util.ArrayList; import java.util.Collections; import java.util.List; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.view.

美团搜索-搜索引擎关键字智能提示的一种实现[转]

http://tech.meituan.com/pinyin-suggest.html --------------------------------------------------------------------- 快照: 问题背景 搜索关键字智能提示是一个搜索应用的标配,主要作用是避免用户输入错误的搜索词,并将用户引导到相应的关键词上,以提升用户搜索体验. 美团CRM系统中存在数以百万计的商家,为了让用户快速查找到目标商家,我们基于solrcloud实现了商家搜索模块.用户在查找商

iOS获取汉字的拼音

在iOS开发中经常涉及到汉字的排序,最常见的就是需要根据首字母的字符顺序排列,比如常见的通讯录等.总结出来,大致可以分为两种方法,其中参考文献[1]中提供的方法十分复杂,并且代码量很大,不建议采用.另一种方法是直接采用Core Foundation中提供的方法,十分简单,封装好的代码如下: 1 - (NSString *)transform:(NSString *)chinese{ 2 //将NSString装换成NSMutableString 3 NSMutableString *pinyin

Microsoft Visual Studio International Pack 1.0 SR1--关于汉字转拼音

Microsoft Visual Studio International Pack 1.0 SR1————微软的一个类库 地址:http://www.microsoft.com/zh-cn/download/details.aspx?id=15251 Visual Studio International Pack 包含一组类库,该类库扩展了.NET Framework对全球化软件开发的支持.使用该类库提供的类,.NET 开发人员可以更方便的创建支持多文化多语言的软件应用.SR1 包含对 Ja

将汉字转化为拼音

/* Boolean CFStringTransform(CFMutableStringRef string, CFRange *range, CFStringRef transform, Boolean reverse); 其中string参数是要转换的string,比如要转换的中文,同时它是mutable的,因此也直接作为最终转换后的字符串.range是要转换的范围,同时输出转换后改变的范围,如果为NULL,视为全部转换.transform可以指定要进行什么样的转换,这里可以指定多种语言的拼

Qt编写可换肤的中文双拼汉字输入法

时间过得真快,不知不觉已到2015年,农历春节一眨眼就过去了,端正状态收拾心情整装待发出发. 曾经有段时间,我有一个很执着的梦想,我要导演出一部空前绝后的巨幕.不过现实无情地碾碎我的梦想,也同时将我推进了人生的低谷. 我开始思考,思想很多很多我以前总是回避的问题,在一次次思考中,我能感觉到我正在向一个陌生的地方走去,前途是光是暗也无法推测,我发现我在赌,赌注是一辈子的懊悔或是郁郁终生. 但当我看到阳光弥漫在我身边时,才发现以前的我是那么幼稚,我始终在找借口或不是借口的借口推卸掉与生俱来的责任,在