ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 2549|回复: 4

[原创] 利用微软拼音来实现中文分词

[复制链接]

TA的精华主题

TA的得分主题

发表于 2019-5-21 17:23 | 显示全部楼层 |阅读模式
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
其实就是IFELanguage接口的应用
因为VB6里不直接支持这个接口,所以需要调用它,只能自己写tlb、动态调用、自己实现接口
写tlb当然可以,需要一点c基础,但是毕竟相对困难
动态调用,就是使用DispCallFunc或者手撸com的方法来调用
这里借用t.tui写好的应用汉字转拼音类来讲
从相关文档我们可以看到IFELanguage接口的vTable如下:
Enum pIFELanguageVTable
pQueryInterface = 0
pAddRef = 4
pRelease = 8
pOpen = 12
pClose = 16
pGetJorphResult = 20
pGetConversationModeCaps = 24
pGetPhonetic = 28
pGetConversion = 32
End Enum
动态调用接口最大的问题是内存对齐
在VB6/VBA程序读取内存时,默认是4字节对齐的,在tlb里可以强制修改对齐约定(tlb还有这个好处,哈哈)
但是VB6/VBA代码里是做不到的,所以我们在调用的时候,要特别注意这点
IFELanguage里两个核心的结构体如下:
typedef struct tagMORRSLT {
DWORD dwSize; // total size of this block.
WCHAR *pwchOutput; // conversion result string.
WORD cchOutput; // lengh of result string.
union {
WCHAR *pwchRead; // reading string
WCHAR *pwchComp;
};
union {
WORD cchRead; // length of reading string.
WORD cchComp;
};
WORD *pchInputPos; // index array of reading to input character.
WORD *pchOutputIdxWDD; // index array of output character to WDD
union {
WORD *pchReadIdxWDD; // index array of reading character to WDD
WORD *pchCompIdxWDD;
};
WORD *paMonoRubyPos; // array of position of monoruby
WDD *pWDD; // pointer to array of WDD
INT cWDD; // number of WDD
VOID *pPrivate; // pointer of private data area
WCHAR BLKBuff[]; // area for stored above members.
// WCHAR wchOutput[cchOutput];
// WCHAR wchRead[cchRead];
// CHAR chInputIdx[cwchInput];
// CHAR chOutputIdx[cchOutput];
// CHAR chReadIndx[cchRead];
// ???? Private
// WDD WDDBlk[cWDD];
}MORRSLT;
.
typedef struct tagWDD{
WORD wDispPos; // Offset of Output string
union {
WORD wReadPos; // Offset of Reading string
WORD wCompPos;
};
WORD cchDisp; //number of ptchDisp
union {
WORD cchRead; //number of ptchRead
WORD cchComp;
};
DWORD WDD_nReserve1; //reserved
WORD nPos; //part of speech
// implementation-defined
WORD fPhrase : 1;//start of phrase
WORD fAutoCorrect: 1;//auto-corrected
WORD fNumericPrefix: 1;//kansu-shi expansion(JPN)
WORD fUserRegistered: 1;//from user dictionary
WORD fUnknown: 1;//unknown word (duplicated information with nPos.)
WORD fRecentUsed: 1; //used recently flag
WORD :10; //
VOID *pReserved; //points directly to WORDITEM
} WDD;

翻译成VB6如下:
Private Type MORRSLT
dwSize As Long '4 存储结构长度
pwchOutput As Long '4 转换结果字符串指针,你转拼音就是拼音
cchOutput As Integer '2 转换结果字符串长度
pwchReadComp As Long '4 输入或对比字符串指针
cchReadComp As Integer '2 输入或对比字符串长度
pchInputPos As Long '4
pchOutputIdxWDD As Long '4
pchReadIdxWDD As Long '4
paMonoRubyPos As Long '4
pWDD As Long '4 WDD数组指针
cWDD As Integer '2 WDD结构的个数
pPrivate As Long '4 基本上都是零,这个声明不能删除
'BLKBuff As Long '4 这个没有存在的意义,只是个存储数据的缓冲区
End Type
.
Type WDD
wDispPos As Integer '输出字串偏移
wReadPos_CompPos As Integer '读取或比较字串的偏移
cchDisp As Integer '
cchRead_Comp As Integer
WDD_nReserve1 As Long
nPos As Integer '
fFlags As Integer '标志位,意思具体看上面
pReserved As Long '保留指针,指向WORDITEM
End Type
以上就是正常翻译过来的结构体声明,但是在实际使用中很明显是不能这样使用的
为什么呢,就是前面说的内存对齐,我们来看t.tui是怎么处理的:
Private Type VB_MORRSLT
dwSize As Long '4
pwchOutput As Long '4
cchOutput As Integer '2+(2),VBA内存对齐闹得,折腾了好一阵才确认问题所在,唉
Block1 As Long '4 如果要用这个,需要在使用时修正,其直接跳过了一个WORD
pchInputPos As Long '4
pchOutputIdxWDD As Long '4
pchReadIdxWDD As Long '4
paMonoRubyPos As Long '4
pWDD As Long '4
cWDD As Integer '2
pPrivate As Long '4 这个虽然没用,但是不能删除
'BLKBuff As Long '4 这个没有存在的意义,只是个存储数据的缓冲区
End Type
以上结构,除了第3个元素做了特殊处理以外,其他跟我上面的翻译一毛一样。
但是因为这么处理就干掉了原结构体中的4个元素,虽然我们暂时没用到。
为什么会干掉呢,简单解释一下,如下图:

内存对齐


4字节对齐的意思就是如右侧黄色标记所示,总是一次读取4个字节
每次读取的起点必然是0,4,8,C(16进制)
读取出来后,存到成员里时,成员只有2个字节,就只存储了低位,高位数据就丢失了。
黄色部分,就把左侧的4个元素都干掉了,当然这里问题不大,因为在转拼音的应用里,压根儿不需要这4个成员。
(老实说,目前我并不知道这4个成员是干啥的)
#############我是分割线#############
当然,我就是来提解决方案的,以WDD为例:
我们可以直接把这个结构体简化:
Enum WDDIndex
wDispPos = 1
wReadCompPos = 2
cchDisp = 3
cchReadComp = 4
WDD_nReserve1 = 5 'DWORD
nPos = 7
fFlags = 8
pReserved = 9 'DWORD
End Enum

Private Type VB_WDD
Data(1 To 10) As Integer
End Type
这样复杂的WDD结构体改成了一个数组成员,这样内存读取的时候就只需要读取一次,然后因为是数组,也不会发生错位的情况,哈哈。
这里数组的成员为什么用Integer,没有为什么,只是在我们如果需要分词的话,那两个Long的成员刚好用不上,而其他成员又都是Integer,我们就偷懒采用了Integer数组了。
实际使用时,根据结构体的实际情况来选定数据数组的类型,如下是使用Byte数组的情况:
Enum myIndex
index1 = 1
index2 = 2
index3 = 3
index4 = 5
index5 = 7
End Enum
Private Type MyType
Data(1 To 10) As Byte
End Type
使用第1个元素,直接myType.Data(1)即可
使用第3个元素,需要MakeInteger(myType.Data(3), myType.Data(4))
使用第5个元素,需要MakeLong(……) '有点长,省略了,哈哈
.
Public Function HiByte(ByVal w As Integer) As Byte
Dim hi As Integer
If w And &H8000 Then hi = &H4000
HiByte = (w And &H7FFE)  256
HiByte = (HiByte Or (hi  128))
End Function
Public Function LoByte(w As Integer) As Byte
LoByte = w And &HFF
End Function
Public Function HiWord(dw As Long) As Integer
If dw And &H80000000 Then
HiWord = (dw  65535) - 1
Else
HiWord = dw  65535
End If
End Function
Public Function LoWord(dw As Long) As Integer
If dw And &H8000& Then
LoWord = &H8000 Or (dw And &H7FFF&)
Else
LoWord = dw And &HFFFF&
End If
End Function
Public Function MakeInteger(ByVal LoByte As Byte, ByVal HiByte As Byte) As Integer
MakeInteger= ((HiByte * &H100) + LoByte)
End Function
Public Function MakeLong(ByVal LoWord As Integer, ByVal HiWord As Integer) As Long
MakeLong = ((HiWord * &H10000) + LoWord)
End Function
当然了,你还可以利用读取规则,来使用多个数组,只是这样就又需要多个偏移枚举了。

评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2019-5-21 17:32 | 显示全部楼层
另外,就是,IFELanguage目前我们只能使用微软拼音2003提供的接口
后续版本,尤其是Office2010自带的微软拼音,千万不要安装,会影响IFELanguage的使用
理论上win7自带的微软拼音也可以使用,但是需要修改一下注册表
win10也可以使用微软拼音2003,也是需要修改注册表,顺便吐槽一下win10,管理员权限修改微软拼音相关的注册表内容都万分困难
名称                                版本        捆绑
微软拼音输入法                1.0        Windows95 OSR2, Windows NT 4, Office 97
微软拼音输入法                1.5        Windows 98
微软拼音输入法                2.0        Windows 2000, Office 2000
微软拼音输入法3.0        3.0        Windows XP, Office XP
微软拼音输入法2003        4.0        Windows Vista, Office 2003
微软拼音输入法               10.1        Windows 7
微软拼音输入法2007        12.0        Office 2007
微软拼音输入法2010        14.0        Office 2010
微软拼音输入法2012        15.0        Windows 8
……

TA的精华主题

TA的得分主题

发表于 2019-5-21 20:41 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2020-5-15 23:19 | 显示全部楼层
超级高深,收藏学习,慢慢理解。。

TA的精华主题

TA的得分主题

发表于 2024-10-31 09:09 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
烧脑的好东西,希望老大给出成品,慢慢学习理解!!
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-21 16:52 , Processed in 0.032630 second(s), 10 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表