汉字转【pinyin】
引言
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】的更多相关文章
- 使用pinyin4j汉字转pinyin
引入maven依赖<dependencies> <dependency> <groupId>com.belerweb</groupId> <art ...
- 基于SolrCloud的内容搜索和热点推送
➠更多技术干货请戳:听云博客 什么是热点 我认为热点有时效性和受众面 用户关注从低到高再到低的内容 .有公共热点和分类热点.例如医辽养老全民关注,科技汽车等只有特定的人群关注. 推送的条件 搜索频次达 ...
- solr入门之搜索建议的几种实现方式和最终选取实现思路
上篇博客中我简单的讲了下solr自身的suggest模块来实现搜索建议.但是今天研究了下在solr自身的suggest中添加进去拼音来智能推荐时不时很方便.在次从网上搜集和整理思考了下该问题的解决. ...
- select2 全拼以及首字母
转自:https://blog.csdn.net/kanhuadeng/article/details/78475317 具体实现方法为: 首先需要在网上下载select2的源码,并引入到项目中,具体 ...
- solr入门之pinyin4j源代码改写动态加入扩展词及整合进war项目中
1.初始化时载入用户定义的字典 package net.sourceforge.pinyin4j; import net.sourceforge.pinyin4j.multipinyin.Trie; ...
- 搜索引擎keyword智能提示的一种实现
问题背景 搜索关键字智能提示是一个搜索应用的标配.主要作用是避免用户输入错误的搜索词,并将用户引导到相应的关键词上,以提升用户搜索体验. 美团CRM系统中存在数以百万计的商家,为了让用户高速查找到目标 ...
- [功能帮助类] C#取汉字拼音的首字母PinYin帮助类 (转载)
点击下载 PinYin.rar 主要功能就是取汉字拼音的首字母,只要你输入一个汉字,或者是多个汉字就会取出相应的道字母,主要是方便查询使用的 /// <summary> /// 编 码 人 ...
- 【Python】 汉字转化汉语拼音pinyin
pinyin pinyin模块是github上一个小项目,在github.com/cleverdeng/pinyin.py上面可以下到源码.衷心感谢那些为这个模块做出贡献的人来方便我们[鞠躬] 安装: ...
- 获取汉字的拼音首字母--pinyin
var pinyin = (function (){ var Pinyin = function (ops){ this.initialize(ops); }, options = { checkPo ...
随机推荐
- Java中的可变参数以及foreach语句
Java中的可变参数的定义格式如下: 返回值类型 方法名称(类型 ... 参数名称){} foreach语句的格式如下: for ( 数据类型 变量名称 :数据名称){ ... } public ...
- DELPHI2007 安装ACTIVEX插件的方法
先新建一个Package file----NEW-----Package Delphi for win32, 再在Component->Import Component里面添加好Activ ...
- Delphi 中的哈希表(二)—— TStringHash
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms ...
- 面向对象之abstract
1.abstract class,抽象类不能被实例化,只能被继承:抽象类中可以包含非抽象方法 2.abstract method();抽象方法只能在抽象类中进行声明,并且没有方法体,非抽象继承子类中必 ...
- 蓝牙的SDP协议总结
1.概念 SDP协议让客户机的应用程序发现存在的服务器应用程序提供的服务以及这些服务的属性.SDP只提供发现服务的机制,不提供使用这些服务的方法.每个蓝牙设备都需要一个SDP Service, ...
- Python的深拷贝与浅拷贝
一.前奏:熟悉Python内存管理 在Python中,变量在第一次赋值时自动声明,在创建—也就是赋值的时候,解释器会根据语法和右侧的操作数来决定新对象的类型. 引用计数器:一个内部跟踪变量 引用计数: ...
- QT多线程及通过事件进行通信(通过自定义事件,然后QApplication::postEvent给主界面,我之前用的是信号槽)
可以通过QThread实现跨平台的多线程开发,Qt库负责在特定平台上的特定多线程实现.要采用QThread进行多线程开发,首先需要包含头文件: #include <QThread> 然后需 ...
- ArcGIS中如何导出单个矢量要素图形
原文:ArcGIS中如何导出单个矢量要素图形 在ARCGIS中载入了一张含有省界的中国地图,是SHP文件.现在我只想要其中一块地区的,实现方法如下: 加入到ArcGIS后,右击图层,打开属性表(att ...
- Linux: 20 Iptables Examples For New SysAdmins
Linux comes with a host based firewall called Netfilter. According to the official project site: net ...
- Stakeholder Risk Management
In this article we'll address the people swirling around your project: stakeholders. You'll find som ...