引言

github地址:aizuyan/pinyin

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

将汉字转化为拼音

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

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

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

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

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

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

设计

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

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

除此之外,我还考虑了性能问题,安前辈php版本的有个固有的缺陷,每次请求都要去加载一遍数据文
件,大概有600~700kb左右,转换为数组,元素个数为40000~50000个左右,每次请求都会分配、释放这部分内
存。而且这个过程会有大量的计算过程(查找、替换),这也是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 = ;
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 = ;
int flag = ;
while(*line)
{
if(*line == '\n')
{
break;
}
if(*line == ',')
{
flag = ;
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 = ,
fromLen = strlen(from),
flag = ;
char *tmp = NULL,
*strTmp = str;
while(tmp = strstr(str, from))
{
pos = tmp - str;
strncat(ret, str, pos);
strcat(ret, to);
str = tmp + fromLen;
flag = ;
if(is_name)
break;
} strcat(ret, str);
if( == flag)
{
memcpy(strTmp, ret, strlen(ret));
strTmp[strlen(ret)] = '\0';
}
}

使用

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

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

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

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

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

 Array
(
[] => biao
[] => han
[] => de
[] => ren
[] => sheng
[] => bu
[] => xu
[] => yao
[] => jie
[] => shi
[] => !
)
 print_r(chinese_to_pinyin("燕睿涛"));
print_r(chinese_to_pinyin("燕睿涛", PINYIN_ISNAME));
print_r(chinese_to_pinyin("罗永浩", PINYIN_ISNAME));

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

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

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

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

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

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

总结

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

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

汉字转【pinyin】的更多相关文章

  1. 使用pinyin4j汉字转pinyin

    引入maven依赖<dependencies> <dependency> <groupId>com.belerweb</groupId> <art ...

  2. 基于SolrCloud的内容搜索和热点推送

    ➠更多技术干货请戳:听云博客 什么是热点 我认为热点有时效性和受众面 用户关注从低到高再到低的内容 .有公共热点和分类热点.例如医辽养老全民关注,科技汽车等只有特定的人群关注. 推送的条件 搜索频次达 ...

  3. solr入门之搜索建议的几种实现方式和最终选取实现思路

    上篇博客中我简单的讲了下solr自身的suggest模块来实现搜索建议.但是今天研究了下在solr自身的suggest中添加进去拼音来智能推荐时不时很方便.在次从网上搜集和整理思考了下该问题的解决. ...

  4. select2 全拼以及首字母

    转自:https://blog.csdn.net/kanhuadeng/article/details/78475317 具体实现方法为: 首先需要在网上下载select2的源码,并引入到项目中,具体 ...

  5. solr入门之pinyin4j源代码改写动态加入扩展词及整合进war项目中

    1.初始化时载入用户定义的字典 package net.sourceforge.pinyin4j; import net.sourceforge.pinyin4j.multipinyin.Trie; ...

  6. 搜索引擎keyword智能提示的一种实现

    问题背景 搜索关键字智能提示是一个搜索应用的标配.主要作用是避免用户输入错误的搜索词,并将用户引导到相应的关键词上,以提升用户搜索体验. 美团CRM系统中存在数以百万计的商家,为了让用户高速查找到目标 ...

  7. [功能帮助类] C#取汉字拼音的首字母PinYin帮助类 (转载)

    点击下载 PinYin.rar 主要功能就是取汉字拼音的首字母,只要你输入一个汉字,或者是多个汉字就会取出相应的道字母,主要是方便查询使用的 /// <summary> /// 编 码 人 ...

  8. 【Python】 汉字转化汉语拼音pinyin

    pinyin pinyin模块是github上一个小项目,在github.com/cleverdeng/pinyin.py上面可以下到源码.衷心感谢那些为这个模块做出贡献的人来方便我们[鞠躬] 安装: ...

  9. 获取汉字的拼音首字母--pinyin

    var pinyin = (function (){ var Pinyin = function (ops){ this.initialize(ops); }, options = { checkPo ...

随机推荐

  1. Turing Test

    Computer Science An Overview _J. Glenn Brookshear _11th Edition In the past the Turing test (propose ...

  2. P1965 转圈游戏

    很容易可以得到,答案应该是(x+m*10^k)%n 很显然,用O(n)一定会卡爆,所以用快速幂来算,或者找一下循环节也是可以的. #include <bits/stdc++.h> usin ...

  3. App之百度云推送

    集成SDK 下载最新的Android SDK压缩包并解压,在新建工程或已有工程中增加百度云推送功能. 我下载的是 ,里面有一个同名的文件夹,文件夹中有 导入云推送jar包和so文件: 将解压后的lib ...

  4. SQuirreL 连接 hive

    软件安装版本: hadoop-2.5.1 hbase-0.98.12.1-hadoop2 apache-hive-1.2.1-bin SQuirreL SQL Client3.7 集成步骤: 1. S ...

  5. 费用性支出预分摊form方式和web方式区别

    预分摊 1 form方式 费用性支出先通过生产资产行请求 生成一个资产行,如果该资产行分摊到两个资产上则分割成两个资产行,既所谓的针对资产行进行分摊. 2 web方式 费用性支出直接分摊到资产上形成资 ...

  6. GATT 服务器与客户端角色

    两个设备应用数据的通信是通过协议栈的GATT层实现的.从GATT角度来看,当两个设备建立连接后,他们处于以下两种角色之一: GATT服务器: 它是为GATT客户端提供数据服务的设备 GATT客户端:  ...

  7. Qt自定义model

    前面我们说了Qt提供的几个预定义model.但是,面对变化万千的需求,那几个model是远远不能满足我们的需要的.另外,对于Qt这种框架来说,model的选择首先要能满足绝大多数功能的需要,这就是说, ...

  8. Eclipse 常用整理

    1.编译 eclipse默认是修改程序后自动编译的,如果不能自动编译,你可以查看project->build automatically选项是否被选中. 手动编译整个工程,可以使用Project ...

  9. Almost Sorted Array---hdu5532(简单dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5532 题意:问一个含有n个数的序列,删除一个数后是否有序(递增或递减都可以) 我们只要求一下最长上升子 ...

  10. asp.net中控件的Attributes用法

    在点击保存时通常会验证输入框是否为空,一般我们会在按钮控件中添加OnClientClick=“return Check();”事件,并通过javascript来处理. 下面是另一种方法,在后台.cs代 ...