70 lines
1.6 KiB
Go
70 lines
1.6 KiB
Go
package common
|
||
|
||
import (
|
||
"encoding/json"
|
||
"sort"
|
||
"strings"
|
||
"unicode"
|
||
|
||
"github.com/mozillazg/go-pinyin"
|
||
|
||
"36wisdom/biz/consts"
|
||
)
|
||
|
||
// AnnotatePinyin 写时一次性标注:词表最长匹配优先(整体转拼音),未命中逐字转
|
||
// (pinyin.Tone 带声调);返回逐字对齐的 JSON 数组字符串,非汉字对应空串,
|
||
// 下标与原文 rune 一一对应。纯函数,文本未变结果不变(幂等)。
|
||
func AnnotatePinyin(text string) string {
|
||
runes := []rune(text)
|
||
out := make([]string, len(runes))
|
||
if len(runes) == 0 {
|
||
return "[]"
|
||
}
|
||
|
||
words := make([]string, 0, len(consts.PinyinOverrides))
|
||
for w := range consts.PinyinOverrides {
|
||
words = append(words, w)
|
||
}
|
||
sort.Slice(words, func(i, j int) bool { return len(words[i]) > len(words[j]) })
|
||
|
||
// 词表最长匹配切分:命中片段整体标注(词表拼音音节数须与字数一致)
|
||
for i := 0; i < len(runes); {
|
||
matched := false
|
||
for _, w := range words {
|
||
n := len([]rune(w))
|
||
if i+n > len(runes) || string(runes[i:i+n]) != w {
|
||
continue
|
||
}
|
||
pys := strings.Fields(consts.PinyinOverrides[w])
|
||
if len(pys) != n {
|
||
continue
|
||
}
|
||
copy(out[i:i+n], pys)
|
||
i += n
|
||
matched = true
|
||
break
|
||
}
|
||
if !matched {
|
||
i++
|
||
}
|
||
}
|
||
|
||
// 未命中汉字逐字转拼音;pi 与 pinyin.Pinyin 的汉字输出顺序同步推进
|
||
args := pinyin.NewArgs()
|
||
args.Style = pinyin.Tone
|
||
all := pinyin.Pinyin(string(runes), args)
|
||
pi := 0
|
||
for idx, r := range runes {
|
||
if !unicode.Is(unicode.Han, r) {
|
||
continue
|
||
}
|
||
if out[idx] == "" && pi < len(all) && len(all[pi]) > 0 {
|
||
out[idx] = all[pi][0]
|
||
}
|
||
pi++
|
||
}
|
||
|
||
b, _ := json.Marshal(out)
|
||
return string(b)
|
||
}
|