410 lines
8.5 KiB
Go
410 lines
8.5 KiB
Go
package rime
|
||
|
||
import (
|
||
"bufio"
|
||
"fmt"
|
||
"log"
|
||
"os"
|
||
"path"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
"unicode"
|
||
"unicode/utf8"
|
||
|
||
"github.com/yanyiwu/gojieba"
|
||
)
|
||
|
||
var jieba = gojieba.NewJieba()
|
||
|
||
// 汉字-拼音 映射
|
||
var hanziPinyin = make(map[string][]string) // 使用 check.go 中的 hanPinyin,再经过 onlyOne 替换
|
||
// 词组-拼音 映射
|
||
var wordPinyin = make(map[string][]string)
|
||
|
||
// 指定唯一读音
|
||
// 1. 同义多音字
|
||
// 2. 一般只在特定的词组中发某个音,如「浚xun县」的「浚jun」只在这里念 xun,已经包含了特定的映射,其余的均使用最常见的注音
|
||
// 3. 还有一些多音字,一个音是主流常用,一个音基本不会使用,如「奘」的 zhuang/zang,只保留 zang
|
||
var onlyOne = map[string]string{
|
||
// 同义多音字,但是两种读音是通用的(有的是全部通用,有的是部分含义通用),暂时只选取一种读音,由其他脚本完成多种注音的处理
|
||
"谁": "shei",
|
||
"血": "xue",
|
||
"熟": "shu",
|
||
"露": "lu",
|
||
"掴": "guai",
|
||
"棽": "shen",
|
||
"爪": "zhua",
|
||
"薄": "bo",
|
||
"剥": "bo",
|
||
"哟": "yo",
|
||
"嚼": "jiao",
|
||
"密钥": "mi yao",
|
||
"公钥": "gong yao",
|
||
"私钥": "si yao",
|
||
"甲壳": "jia ke",
|
||
"掉色": "diao se",
|
||
"怎么着": "zen me zhe",
|
||
"这么着": "zhe me zhe",
|
||
"那么着": "na me zhe",
|
||
// 其他多音字,指定唯一读音
|
||
"的": "de",
|
||
"了": "le",
|
||
"核儿": "he er",
|
||
"核": "he",
|
||
"褪下": "tui xia",
|
||
"褪": "tui",
|
||
"便便": "bian bian",
|
||
"便宜": "pian yi",
|
||
"便": "bian",
|
||
"尿尿": "niao niao",
|
||
"尿": "niao",
|
||
"衣裳": "yi shang",
|
||
"裳": "shang",
|
||
"喳喳": "zha zha",
|
||
"喳": "zha",
|
||
"脉脉": "mo mo",
|
||
"脉": "mai",
|
||
"呱呱": "gua gua",
|
||
"呱": "gua",
|
||
"咀": "ju",
|
||
"大王": "da wang",
|
||
"大伯": "da bo",
|
||
"大": "da",
|
||
"摩挲": "mo suo",
|
||
"摩": "mo",
|
||
"澄清": "cheng qing",
|
||
"澄": "cheng",
|
||
"出车": "chu che",
|
||
"车": "che",
|
||
"约一约": "yue yi yue",
|
||
"约": "yue",
|
||
"绿营": "lv ying",
|
||
"绿": "lv",
|
||
"圈里": "quan li",
|
||
"圈外": "quan wai",
|
||
"咱家": "zan jia",
|
||
"圈": "quan",
|
||
"伯": "bo",
|
||
"胖": "pang",
|
||
"南": "nan",
|
||
"颈": "jing",
|
||
"氏": "shi",
|
||
"度": "du",
|
||
"柜": "gui",
|
||
"奘": "zang",
|
||
"叶": "ye",
|
||
"吭": "keng",
|
||
"纶": "lun",
|
||
"莎": "sha",
|
||
"噌": "ceng",
|
||
"解": "jie",
|
||
"价": "jia",
|
||
"种": "zhong",
|
||
"嘚": "de",
|
||
"浚": "jun",
|
||
"枸": "gou",
|
||
"拾": "shi",
|
||
"塞": "sai",
|
||
"膻": "shan",
|
||
"数": "shu",
|
||
"媞": "ti",
|
||
"哦": "o",
|
||
"络": "luo",
|
||
"俩": "lia",
|
||
"咋": "za",
|
||
"否": "fou",
|
||
"尾": "wei",
|
||
"弄": "nong",
|
||
"强": "qiang",
|
||
"烙": "lao",
|
||
"卜": "bu",
|
||
"祭": "ji",
|
||
"缉": "ji",
|
||
"侥": "jiao",
|
||
"驮": "tuo",
|
||
"陆": "lu",
|
||
"盖": "gai",
|
||
"色": "se",
|
||
"涌": "yong",
|
||
"栅": "zha",
|
||
"啜": "chuo",
|
||
"涡": "wo",
|
||
"券": "quan",
|
||
"糜": "mi",
|
||
"焯": "chao",
|
||
"藉": "ji",
|
||
"蚌": "bang",
|
||
"沌": "dun",
|
||
"殷": "yin",
|
||
"翟": "zhai",
|
||
"腌": "yan",
|
||
"佛": "fo",
|
||
"合": "he",
|
||
"乘": "cheng",
|
||
"溃": "kui",
|
||
"牟": "mou",
|
||
"疟": "nve",
|
||
"雀": "que",
|
||
"虹": "hong",
|
||
"碌": "lu",
|
||
"捋": "lv",
|
||
"堡": "bao",
|
||
"读": "du",
|
||
"蛤": "ha",
|
||
"繁": "fan",
|
||
"巷": "xiang",
|
||
"磅": "bang",
|
||
"粘": "zhan",
|
||
"见": "jian",
|
||
"筠": "yun",
|
||
"会": "hui",
|
||
"铅": "qian",
|
||
"呢": "ne",
|
||
"栎": "li",
|
||
"咽": "yan",
|
||
"殖": "zhi",
|
||
"泷": "long",
|
||
"迫": "po",
|
||
"囤": "tun",
|
||
"娜": "na",
|
||
"纤": "xian",
|
||
"嘘": "xu",
|
||
"阿": "a",
|
||
"泌": "mi",
|
||
"咯": "lo",
|
||
"扁": "bian",
|
||
"综": "zong",
|
||
"哪": "na",
|
||
"艾": "ai",
|
||
"期": "qi",
|
||
"晟": "sheng",
|
||
"召": "zhao",
|
||
"瀑": "pu",
|
||
"棱": "leng",
|
||
"区": "qu",
|
||
"蔓": "man",
|
||
"亟": "ji",
|
||
"蔚": "wei",
|
||
"莘": "shen",
|
||
"石": "shi",
|
||
"炮": "pao",
|
||
"喋": "die",
|
||
"句": "ju",
|
||
"杉": "shan",
|
||
"臭": "chou",
|
||
"禅": "chan",
|
||
"埋": "mai",
|
||
"仇": "chou",
|
||
"和": "he",
|
||
"折": "zhe",
|
||
"单": "dan",
|
||
"臂": "bi",
|
||
"提": "ti",
|
||
"贾": "jia",
|
||
"澹": "dan",
|
||
"扛": "kang",
|
||
"员": "yuan",
|
||
"戌": "xu",
|
||
"楷": "kai",
|
||
"卒": "zu",
|
||
"兹": "zi",
|
||
"秘": "mi",
|
||
"洞": "dong",
|
||
"番": "fan",
|
||
"亲": "qin",
|
||
"洗": "xi",
|
||
"无": "wu",
|
||
"缩": "suo",
|
||
"尺": "chi",
|
||
"差": "cha",
|
||
"说": "shuo",
|
||
"貉": "hao",
|
||
"术": "shu",
|
||
"龟": "gui",
|
||
"万": "wan",
|
||
"没": "mei",
|
||
"查": "cha",
|
||
"省": "sheng",
|
||
"卡": "ka",
|
||
"奇": "qi",
|
||
"择": "ze",
|
||
"峙": "zhi",
|
||
"戛": "jia",
|
||
"绰": "chuo",
|
||
"葚": "shen",
|
||
"嘞": "lei",
|
||
"凹": "ao",
|
||
"给": "gei",
|
||
}
|
||
|
||
func init() {
|
||
// 从 base、ext 准备结巴的词典和词组拼音映射
|
||
for _, dictPath := range []string{BasePath, ExtPath} {
|
||
file, err := os.Open(dictPath)
|
||
if err != nil {
|
||
log.Fatalln(err)
|
||
}
|
||
|
||
sc := bufio.NewScanner(file)
|
||
isMark := false
|
||
for sc.Scan() {
|
||
line := sc.Text()
|
||
if !isMark {
|
||
if strings.HasPrefix(line, mark) {
|
||
isMark = true
|
||
}
|
||
continue
|
||
}
|
||
if strings.HasPrefix(line, "#") || line == "" {
|
||
continue
|
||
}
|
||
parts := strings.Split(line, "\t")
|
||
if len(parts) != 3 || !isAllLower(parts[1]) {
|
||
continue
|
||
}
|
||
text, code := parts[0], parts[1]
|
||
weight, err := strconv.Atoi(parts[2])
|
||
if err != nil {
|
||
log.Fatalln(err, line)
|
||
}
|
||
jieba.AddWordEx(text, weight, "")
|
||
wordPinyin[text] = append(wordPinyin[text], code)
|
||
}
|
||
|
||
file.Close()
|
||
}
|
||
|
||
// 拷贝 hanPinyin 到 hanziPinyin,再从 onlyOne 替换掉映射中的注音
|
||
for k, v := range hanPinyin {
|
||
hanziPinyin[k] = v
|
||
}
|
||
for text, code := range onlyOne {
|
||
if utf8.RuneCountInString(text) == 1 {
|
||
hanziPinyin[text] = []string{code}
|
||
} else {
|
||
wordPinyin[text] = []string{code}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Pinyin 半自动的注音
|
||
// 能准确注音的,注音;拿不准的,留着手动注音
|
||
func Pinyin(dictPath string) {
|
||
// 控制台输出
|
||
defer printlnTimeCost("注音\t"+path.Base(dictPath), time.Now())
|
||
|
||
// 读取到 lines 数组
|
||
file, err := os.ReadFile(dictPath)
|
||
if err != nil {
|
||
log.Fatalln(err)
|
||
}
|
||
lines := strings.Split(string(file), "\n")
|
||
|
||
// 遍历、注音
|
||
isMark := false
|
||
for i, line := range lines {
|
||
if strings.Contains(dictPath, "temp") {
|
||
isMark = true
|
||
}
|
||
if !isMark {
|
||
if strings.HasPrefix(line, mark) {
|
||
isMark = true
|
||
}
|
||
continue
|
||
}
|
||
if line == "" {
|
||
continue
|
||
}
|
||
|
||
parts := strings.Split(line, "\t")
|
||
text := parts[0]
|
||
var code string
|
||
// parts[1] 可能是:空、已经注音完成、注音到一半(含有未能自动注音的多音字汉字)
|
||
// 注音完成的,不再注音,其余的进行注音
|
||
if len(parts) == 1 { // 只有汉字
|
||
code = generatePinyin(text)
|
||
} else if len(parts) == 2 || len(parts) == 3 {
|
||
if isAllLower(parts[1]) { // 全小写,不包含汉字,代表已经注音完成
|
||
code = parts[1]
|
||
} else { // 注音到一半(含有汉字),重新注音
|
||
code = generatePinyin(text)
|
||
}
|
||
} else {
|
||
log.Fatalln("分割错误:", line)
|
||
}
|
||
lines[i] = text + "\t" + code
|
||
}
|
||
|
||
// 写入
|
||
resultString := strings.Join(lines, "\n")
|
||
err = os.WriteFile(dictPath, []byte(resultString), 0644)
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
}
|
||
|
||
// 生成拼音
|
||
// 多音字的处理:
|
||
// 如果 wordPinyin 没有包含多音字的映射, 返回 []string{"gao xing 地 beng qi lai"} 然后手动注音
|
||
// 如果 wordPinyin 包含「高兴地 gao xing de」,则将 "高兴地蹦起来" 返回 []string{"gao xing de beng qi lai"}
|
||
func generatePinyin(s string) string {
|
||
var r string
|
||
|
||
words := jieba.Cut(s, true)
|
||
for _, word := range words {
|
||
// 单字,且不是多音字
|
||
if utf8.RuneCountInString(word) == 1 && len(hanziPinyin[word]) == 1 {
|
||
r += hanziPinyin[word][0] + " "
|
||
continue
|
||
}
|
||
|
||
// 词组,且映射中没有多种注音
|
||
if len(wordPinyin[word]) == 1 {
|
||
r += wordPinyin[word][0] + " "
|
||
continue
|
||
}
|
||
|
||
// 词组,未能通过映射进行注音,但本身不包含多音字
|
||
notPolyphone := false
|
||
for _, char := range word {
|
||
if len(hanziPinyin[string(char)]) > 1 {
|
||
notPolyphone = true
|
||
break
|
||
}
|
||
}
|
||
if !notPolyphone {
|
||
for _, char := range word {
|
||
r += hanziPinyin[string(char)][0] + " "
|
||
}
|
||
continue
|
||
}
|
||
|
||
// 其他的不处理,直接返回汉字
|
||
|
||
r += word + " "
|
||
}
|
||
|
||
return strings.TrimSpace(r)
|
||
}
|
||
|
||
// GeneratePinyinTest 临时测试一个
|
||
func GeneratePinyinTest(s string) {
|
||
words := jieba.Cut(s, true)
|
||
r := generatePinyin(s)
|
||
fmt.Printf("%s %q\n", words, r)
|
||
}
|
||
|
||
// 判断 code 是否全小写,不判断空格
|
||
func isAllLower(s string) bool {
|
||
for _, ch := range s {
|
||
if ch == ' ' {
|
||
continue
|
||
}
|
||
if !unicode.IsLower(ch) {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|