68 lines
1.6 KiB
Go
68 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++
|
||
}
|
||
}
|
||
|
||
// 未命中汉字逐字转拼音:词典缺失字(生僻字等)SinglePinyin 返回空串,
|
||
// 该字自然保持 "",后续字不错位
|
||
args := pinyin.Args{Style: pinyin.Tone}
|
||
i := 0
|
||
for _, r := range text {
|
||
if out[i] == "" && unicode.Is(unicode.Han, r) {
|
||
if pys := pinyin.SinglePinyin(r, args); len(pys) > 0 {
|
||
out[i] = pys[0]
|
||
}
|
||
}
|
||
i++
|
||
}
|
||
|
||
b, _ := json.Marshal(out)
|
||
return string(b)
|
||
}
|