引言

github地址:aizuyan/pinyin

无意中看到了overtrue/pinyin这个项目,感觉很有意思,

这个项目做了这么一件事情:

将汉字转化为拼音

刚看到这里是不是觉得没什么难度,没什么意思?您不妨接着往下看。要是只是将汉字转为拼音好像

很容易就实现了,但是要是给转换之后的汉字带上音调呢,这样难度就很大了,因为汉字博大精深,

其中一方面就表现在多音字,同样一个字在不同的语句场景下,音调是不一样的。看到这里你在考

虑下如何处理?

这里我还是很佩服安正超(要是不了解他,不妨点进去看看,他

有好几个开源项目都非常棒)这位大神的,他的思路让我眼前一亮:

替换的时候首先将常用的词组替换了,比如短语、成语、常用的词语,这些词语替换的时候按照常

用程度由高到低排序。

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

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

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

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

设计

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

so,最后使用了下面的数据结构:

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

除此之外,我还考虑了性能问题,安前辈php版本的有个固有的缺陷,每次请求都要去加载一遍数据文

件,大概有600700kb左右,转换为数组,元素个数为4000050000个左右,每次请求都会分配、释放这部分内

存。而且这个过程会有大量的计算过程(查找、替换),这也是php很不擅长的,如果用c语言会好很多。

因此,我就使用了PHP扩展,在模块初始化的时候,将所有配置数据载入内存,如果是fast-cgi模式,

不用每次请求都加载一遍配置数据,只在进程启动的时候加载一遍。计算的话没有找到php里面比较合适的

函数,字节写了查找替换的函数。

还有就是如何读取配置文件数据了,我采用了下面的数据格式存储每一个汉-拼对“,csv个格式,每一行

第一列是短语、词语或者汉字,第二列是拼音,每个拼音之间使用制表符\t分割,这样读取、进一步处理

就很方便了

汉字,   han zi
......
{汉字|词语|短语}, pin yin

实现

实现部分,挑几个主要的函数出来:

首先是给链表中添加汉字拼音结构体的函数,这里有个地方要注意,这里使用了c语言原声的malloc

strdup,这是因为这个变量是全局的,不会随着请求的结束而销毁,而且也不会区分线程,因为所有的

线程都只会读取变量中的内容,所有的线程共享一套变量就可以了。

MyList *pinyin_list_append(MyList *last, const char *key, const char *value)
{
MyList *element = (MyList *)malloc(sizeof(MyList));
char *newKey = strdup(key);
char *newVal = strdup(value);
element->key = newKey;
element->val = newVal;
element->next = NULL;
last->next = element; return element;
}

下面这个函数是从一行通过逗号分隔的字符串中取出逗号前面的部分作为汉字部分。

const char *get_key_from_line(const char *line, char *ret)
{
int i = 0;
while(*line)
{
if(*line != ',')
{
ret[i] = *line;
}else {
break;
}
i++;
line++;
}
ret[i] = '\0';
return ret;
}

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

const char *get_val_from_line(const char *line, char *ret)
{
int i = 0;
int flag = 0;
while(*line)
{
if(*line == '\n')
{
break;
}
if(*line == ',')
{
flag = 1;
line++;
continue;
}else if(!flag) {
line++;
continue;
}
ret[i] = *line;
i++;
line++;
}
ret[i] = '\0';
return ret;
}

下面是最重要的一个,替换字符换函数,from是要替换的字符串,to是要替换为的字符串,

str是原始字符串,ret是临时字符串,会保存临时的结果,is_name表示是否是姓名,

如果是姓名,只替换一次。

void str_replace(const char *from, const char *to, char *str, char *ret, zend_bool is_name)
{
int pos = 0,
fromLen = strlen(from),
flag = 0;
char *tmp = NULL,
*strTmp = str;
while(tmp = strstr(str, from))
{
pos = tmp - str;
strncat(ret, str, pos);
strcat(ret, to);
str = tmp + fromLen;
flag = 1;
if(is_name)
break;
} strcat(ret, str);
if(1 == flag)
{
memcpy(strTmp, ret, strlen(ret));
strTmp[strlen(ret)] = '\0';
}
}

使用

只通过一个函数和标志位来实现,使用起来也是很方便的:

使用的时候可以参考github中的README.md,里面有详细的编译配置细节。

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

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

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

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

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

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

rray
(
[0] => yàn
[1] => ruì
[2] => tāo
)
Array
(
[0] => yān
[1] => ruì
[2] => tāo
)
Array
(
[0] => luō
[1] => yǒng
[2] => hào
)

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

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

PINYIN_TRIM、PINYIN_FORMAT_EN、PINYIN_FORMAT_CH是对立的,第一个清除所有标点、第二个

使用英文标点,第三个使用中文标点

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

总结

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

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

这是github地址:pinyin,欢迎大家点赞、fork、

pull-request或者提建议。

【汉字】转【pīnyīn】的更多相关文章

  1. ADF_Data Binding系列1_使用Bean Data Control

    2015-02-16 Created By BaoXinjian

  2. 结巴分词3--基于汉字成词能力的HMM模型识别未登录词

    作者:zhbzz2007 出处:http://www.cnblogs.com/zhbzz2007 欢迎转载,也请保留这段声明.谢谢! 1 算法简介 在 结巴分词2--基于前缀词典及动态规划实现分词 博 ...

  3. .net汉字转字母

    目前手上有一个需要实现:将用户输入的姓名转换成汉语拼音. 使用枚举,既麻烦又易出错,发现有一个微软拼音转换工具类ChnCharInfo.dll,在此记录下: 首先需要引入此dll, 链接: http: ...

  4. STemwin汉字显示

    硬件环境: STM32F429,电容屏800X480 5点触控RGB屏幕 ,SPI flash: 软件环境: UCOSIII,STemwin: 汉字显示方法: 1.在SPIflash中装在字库XBF_ ...

  5. 【转载】C#怎么判断字符是不是汉字

    支持并尊重原创!原文地址:http://jingyan.baidu.com/article/2c8c281deb79ed0008252af1.html 判断一个字符是不是汉字通常有三种方法,第1种用 ...

  6. iOS 汉字的拼音

    获取汉字的拼音 #import <Foundation/Foundation.h> @interface NSString (Utils) /** * 汉字的拼音 * * @return ...

  7. Oracle汉字转拼音package

    --函数GetHzFullPY(string)用于获取汉字字符串的拼音 --select GetHzFullPY('中华人民共和国') from dual; --返回:ZhongHuaRenMinGo ...

  8. Linux C语言解析.bmp格式图片并显示汉字

    bmp.h 文件 #ifndef __BMP_H__ #define __BMP_H__ #include <unistd.h> #include <stdio.h> #inc ...

  9. [python]获取网页中内容为汉字的字符串的判断

    实际上是这样,将获取到网页中表单内容与汉字字符串作比较,即: a = request.POST['a'] if a == '博客园': print 'ok' else: print 'false' a ...

随机推荐

  1. 十种MYSQL显错注入原理讲解(二)

    上一篇讲过,三种MYSQL显错注入原理.下面我继续讲解. 1.geometrycollection() and geometrycollection((select * from(select * f ...

  2. MAC OS X El CAPITAN 搭建SPRING MVC (1)- 目录、包名、创建web.xml

    一. 下载STS(Spring Tool Suite) 官方地址:http://spring.io/tools/sts 下载spring tool suite for mac 最新版本.这个IDE是很 ...

  3. Android 多个listview的实现

    正好,今天项目中需要,先写了个demo,给大家参考参考. 先上图,需要的自己,看看具体的代码实现步骤 大概说一下实现步骤: 1.布局中先用 scrollview 包裹 LinearLayout < ...

  4. 【代码笔记】iOS-账号,密码记住

    一,效果图. 二,工程图. 三,代码. RegisViewController.h #import <UIKit/UIKit.h> @interface RegisViewControll ...

  5. 关于用sql语句实现一串数字位数不足在左侧补0的技巧

    在日常使用sql做查询插入操作时,我们通常会用到用sql查询一串编号,这串编号由数字组成.为了统一美观,我们记录编号时,统一指定位数,不足的位数我们在其左侧补0.如编号66,我们指定位数为5,则保存数 ...

  6. JSON金额解析BUG的解决过程

    [原创申明:文章为原创,欢迎非盈利性转载,但转载必须注明来源] 这是在我们开发的一个支付系统中暴露的一个BUG,问题本身比较简单,有意思的是解决问题的过程.将过程分享出来,希望能够对大家有所帮助. 一 ...

  7. 传统IT企业与互联网企业的一点思考

    [注意前提]应当说,比较常用的管理策略并没有界线分明的优劣之分,只有适不适合企业的经营战略,团队文化,发展状况等. 之所以有传统IT企业与互联网企业的区别,主要的原因是两者所处的市场环境与经营思路造成 ...

  8. The user specified as a definer ('root'@'%') does not exist 异常解决

    参考:http://www.cnblogs.com/Magicam/archive/2013/07/22/3207382.html 权限问题,授权 给 root  所有sql 权限 mysql> ...

  9. Long类型的数据转换时间格式方法

    function getDate(date) { //得到日期对象 var d=new Date(date); //得到年月日 var year =d.getFullYear(); ); var da ...

  10. linux输入子系统(input subsystem)之evdev.c事件处理过程

    1.代码 input_subsys.drv.c 在linux输入子系统(input subsystem)之按键输入和LED控制的基础上有小改动,input_subsys_test.c不变. input ...