mirror of
https://github.com/fumiama/emozi.git
synced 2026-06-05 00:32:48 +08:00
feat(coder): finish decode
This commit is contained in:
@@ -15,7 +15,7 @@ package emozi
|
|||||||
|
|
||||||
// 部首后备 内嵌的部首到颜文字的映射, 是第二优先查询顺位. 第一顺位是数据库的部首表. 第三位是回落到 🈳️.
|
// 部首后备 内嵌的部首到颜文字的映射, 是第二优先查询顺位. 第一顺位是数据库的部首表. 第三位是回落到 🈳️.
|
||||||
var 部首后备 = map[rune]string{ {{range .}}
|
var 部首后备 = map[rune]string{ {{range .}}
|
||||||
'{{.R}}': "🈳️",{{end}}
|
'{{.R}}': 空,{{end}}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
116
coder.go
116
coder.go
@@ -13,15 +13,16 @@ import (
|
|||||||
|
|
||||||
// Coder encoder/decoder
|
// Coder encoder/decoder
|
||||||
type Coder struct {
|
type Coder struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
db sql.Sqlite
|
db sql.Sqlite
|
||||||
字表缓存 map[rune][]字表
|
字表缓存 map[rune][]字表
|
||||||
部首缓存 map[rune]string
|
逆字表缓存 map[int64][]rune
|
||||||
isRandom bool
|
部首缓存 map[rune]string
|
||||||
|
逆部首缓存 map[string][]rune
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCoder israndom 随机挑选声母韵母的颜文字, 否则固定使用第一个
|
// NewCoder israndom 随机挑选声母韵母的颜文字, 否则固定使用第一个
|
||||||
func NewCoder(israndom bool, cachettl time.Duration) (c Coder, err error) {
|
func NewCoder(cachettl time.Duration) (c Coder, err error) {
|
||||||
if _, err = os.Stat(EmoziDatabasePath); err != nil {
|
if _, err = os.Stat(EmoziDatabasePath); err != nil {
|
||||||
err = os.WriteFile(EmoziDatabasePath, 字数据库, 0644)
|
err = os.WriteFile(EmoziDatabasePath, 字数据库, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -30,8 +31,9 @@ func NewCoder(israndom bool, cachettl time.Duration) (c Coder, err error) {
|
|||||||
}
|
}
|
||||||
c.db.DBPath = EmoziDatabasePath
|
c.db.DBPath = EmoziDatabasePath
|
||||||
c.字表缓存 = make(map[rune][]字表, 4096)
|
c.字表缓存 = make(map[rune][]字表, 4096)
|
||||||
|
c.逆字表缓存 = make(map[int64][]rune, 4096)
|
||||||
c.部首缓存 = make(map[rune]string, 4096)
|
c.部首缓存 = make(map[rune]string, 4096)
|
||||||
c.isRandom = israndom
|
c.逆部首缓存 = make(map[string][]rune, 4096)
|
||||||
err = c.db.Open(cachettl)
|
err = c.db.Open(cachettl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -56,19 +58,23 @@ func NewCoder(israndom bool, cachettl time.Duration) (c Coder, err error) {
|
|||||||
func (c *Coder) Close() error {
|
func (c *Coder) Close() error {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
|
c.字表缓存 = nil
|
||||||
|
c.逆字表缓存 = nil
|
||||||
|
c.部首缓存 = nil
|
||||||
|
c.逆部首缓存 = nil
|
||||||
return c.db.Close()
|
return c.db.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode 从汉字序列生成 EmoziString 返回 EmoziString 多音字选择数列表
|
// Encode 从汉字序列生成 EmoziString 返回 EmoziString 多音字选择数列表
|
||||||
func (c *Coder) Encode(s string, selections ...int) (EmoziString, []int, error) {
|
func (c *Coder) Encode(enableRandom bool, s string, selections ...int) (EmoziString, []int, error) {
|
||||||
sb := strings.Builder{}
|
sb := strings.Builder{}
|
||||||
lstbuf := make([]字表, 0, len(s)/2)
|
lstbuf := make([]字表, 0, len(s)/2)
|
||||||
var lst []字表
|
var lst []字表
|
||||||
write := func(x *字表) {
|
write := func(x *字表) {
|
||||||
sb.WriteString(c.查声母(x.S))
|
sb.WriteString(c.声母(enableRandom, x.S))
|
||||||
sb.WriteString(c.查韵母(x.Y))
|
sb.WriteString(c.韵母(enableRandom, x.Y))
|
||||||
sb.WriteString(c.查声调(x.T))
|
sb.WriteString(c.声调(enableRandom, x.T))
|
||||||
sb.WriteString(c.查部首(x.R))
|
sb.WriteString(c.部首(x.R))
|
||||||
}
|
}
|
||||||
多音字计数 := 0
|
多音字计数 := 0
|
||||||
多音字数表 := []int{}
|
多音字数表 := []int{}
|
||||||
@@ -104,6 +110,92 @@ func (c *Coder) Encode(s string, selections ...int) (EmoziString, []int, error)
|
|||||||
return WrapRawEmoziString(sb.String()), 多音字数表, nil
|
return WrapRawEmoziString(sb.String()), 多音字数表, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decode 从 EmoziString 解码得到可能的文字序列
|
||||||
|
func (c *Coder) Decode(es EmoziString, forcedecode bool) (string, error) {
|
||||||
|
if !es.IsValid() && !forcedecode {
|
||||||
|
return "", ErrInvalidEmoziString
|
||||||
|
}
|
||||||
|
s := ""
|
||||||
|
if forcedecode {
|
||||||
|
s = string(es)
|
||||||
|
} else {
|
||||||
|
s = es.String()
|
||||||
|
}
|
||||||
|
lstbuf := make([]字表, 0, len(s)/8)
|
||||||
|
read := func(s string) (string, int) {
|
||||||
|
sum := 0
|
||||||
|
sm, n := c.逆声母(s)
|
||||||
|
if n == 0 {
|
||||||
|
return "", 0
|
||||||
|
}
|
||||||
|
sum += n
|
||||||
|
ym, n := c.逆韵母(s[sum:])
|
||||||
|
if n == 0 {
|
||||||
|
return "", 0
|
||||||
|
}
|
||||||
|
sum += n
|
||||||
|
t, n := c.逆声调(s[sum:])
|
||||||
|
if n == 0 {
|
||||||
|
return "", 0
|
||||||
|
}
|
||||||
|
sum += n
|
||||||
|
rs, n := c.逆部首(s[sum:])
|
||||||
|
if n == 0 {
|
||||||
|
return "", 0
|
||||||
|
}
|
||||||
|
sum += n
|
||||||
|
var possibles []rune
|
||||||
|
var err error
|
||||||
|
if len(rs) == 0 { // 意符为空
|
||||||
|
possibles, lstbuf, err = c.逆字(sm, ym, t, 0, lstbuf)
|
||||||
|
if err != nil {
|
||||||
|
return "[]", sum
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var revr []rune
|
||||||
|
for i := 0; i < len(rs); i++ {
|
||||||
|
revr, lstbuf, err = c.逆字(sm, ym, t, rs[i], lstbuf)
|
||||||
|
if err != nil || len(revr) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(possibles) == 0 {
|
||||||
|
possibles = revr
|
||||||
|
} else {
|
||||||
|
possibles = append(possibles, revr...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(possibles) == 0 {
|
||||||
|
return "[]", sum
|
||||||
|
}
|
||||||
|
if len(possibles) == 1 {
|
||||||
|
return string(possibles[0]), sum
|
||||||
|
}
|
||||||
|
sb := strings.Builder{}
|
||||||
|
sb.WriteString("[")
|
||||||
|
sb.WriteRune(possibles[0])
|
||||||
|
for _, r := range possibles[1:] {
|
||||||
|
sb.WriteString("|")
|
||||||
|
sb.WriteRune(r)
|
||||||
|
}
|
||||||
|
sb.WriteString("]")
|
||||||
|
return sb.String(), sum
|
||||||
|
}
|
||||||
|
sb := strings.Builder{}
|
||||||
|
sum := 0
|
||||||
|
for sum < len(s) {
|
||||||
|
ch, n := read(s[sum:])
|
||||||
|
if n <= 0 {
|
||||||
|
sb.WriteByte(s[sum])
|
||||||
|
sum++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sum += n
|
||||||
|
sb.WriteString(ch)
|
||||||
|
}
|
||||||
|
return sb.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// AddChar 向主库添加一个新字
|
// AddChar 向主库添加一个新字
|
||||||
//
|
//
|
||||||
// w: 字, r: 部首, p: 不带声调的拼音(可空), f: 带声调的拼音
|
// w: 字, r: 部首, p: 不带声调的拼音(可空), f: 带声调的拼音
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestEncode(t *testing.T) {
|
func TestEncode(t *testing.T) {
|
||||||
c, err := NewCoder(false, time.Minute)
|
c, err := NewCoder(time.Minute)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
es, lst, err := c.Encode("你好,世界!看看多音字:行。")
|
es, lst, err := c.Encode(false, "你好,世界!看看多音字:行。")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,7 @@ func TestEncode(t *testing.T) {
|
|||||||
if len(lst) != 1 && lst[0] != 2 {
|
if len(lst) != 1 && lst[0] != 2 {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
es, lst, err = c.Encode("你好,世界!指定多音字:银行行。", 1, 0)
|
es, lst, err = c.Encode(false, "你好,世界!指定多音字:银行行。", 1, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -33,3 +33,37 @@ func TestEncode(t *testing.T) {
|
|||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDecode(t *testing.T) {
|
||||||
|
c, err := NewCoder(time.Minute)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
s := "你好,世界!看看多音字:行。"
|
||||||
|
es, lst, err := c.Encode(false, s)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(es.String(), lst)
|
||||||
|
ds, err := c.Decode(es, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(ds)
|
||||||
|
if ds != "[你|儗]好,世[界|畍]!看看多音字:[行|行]。" {
|
||||||
|
t.Fatal("got", ds)
|
||||||
|
}
|
||||||
|
es, lst, err = c.Encode(false, "你好,世界!指定多音字:银行行。", 1, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(es.String(), lst)
|
||||||
|
ds, err = c.Decode(es, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(ds)
|
||||||
|
if ds != "[你|儗]好,世[界|畍]![指|抧|扺]定多音字:[銀|银]行行。" {
|
||||||
|
t.Fatal("got", ds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
86
data.go
86
data.go
@@ -2,7 +2,9 @@ package emozi
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 字数据库 数据来自 https://github.com/shuowenjiezi/shuowen
|
// 字数据库 数据来自 https://github.com/shuowenjiezi/shuowen
|
||||||
@@ -11,7 +13,7 @@ import (
|
|||||||
var 字数据库 []byte
|
var 字数据库 []byte
|
||||||
|
|
||||||
// DatabasePath 字数据库的路径 如找不到会向对应路径写入内嵌的字数据库
|
// DatabasePath 字数据库的路径 如找不到会向对应路径写入内嵌的字数据库
|
||||||
var EmoziDatabasePath = "字a.db"
|
var EmoziDatabasePath = "字.db"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
主字表名 = "emozi"
|
主字表名 = "emozi"
|
||||||
@@ -19,6 +21,10 @@ const (
|
|||||||
部首表名 = "radcl"
|
部首表名 = "radcl"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNoSuchChar = errors.New("no such char")
|
||||||
|
)
|
||||||
|
|
||||||
// 字表 emozi表 定义
|
// 字表 emozi表 定义
|
||||||
type 字表 struct {
|
type 字表 struct {
|
||||||
ID int64 // ID 高 32 位 W 的 rune, 低 32 位 保留8 S8 Y8 T8
|
ID int64 // ID 高 32 位 W 的 rune, 低 32 位 保留8 S8 Y8 T8
|
||||||
@@ -35,33 +41,105 @@ func 字表ID(w rune, s 声母枚举, y 韵母枚举, t 声调枚举) int64 {
|
|||||||
return int64((uint64(w) << 32) | (uint64(s) << 16) | (uint64(y) << 8) | (uint64(t)))
|
return int64((uint64(w) << 32) | (uint64(s) << 16) | (uint64(y) << 8) | (uint64(t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 逆字ID 同声母 韵母 声调 部首的字的集合
|
||||||
|
func 逆字ID(s 声母枚举, y 韵母枚举, t 声调枚举, r rune) int64 {
|
||||||
|
return int64((uint64(r) << 32) | (uint64(s) << 16) | (uint64(y) << 8) | (uint64(t)))
|
||||||
|
}
|
||||||
|
|
||||||
// 查字 返回 lst lstbuf error
|
// 查字 返回 lst lstbuf error
|
||||||
func (c *Coder) 查字(ch rune, lstbuf []字表) ([]字表, []字表, error) {
|
func (c *Coder) 查字(ch rune, lstbuf []字表) ([]字表, []字表, error) {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
lst, ok := c.字表缓存[ch]
|
lst, ok := c.字表缓存[ch]
|
||||||
c.mu.RUnlock()
|
c.mu.RUnlock()
|
||||||
if ok {
|
if ok {
|
||||||
|
if len(lst) == 0 {
|
||||||
|
return nil, lstbuf, ErrNoSuchChar
|
||||||
|
}
|
||||||
return lst, lstbuf, nil
|
return lst, lstbuf, nil
|
||||||
}
|
}
|
||||||
lstbuf = lstbuf[:0]
|
lstbuf = lstbuf[:0]
|
||||||
x := 字表{}
|
x := 字表{}
|
||||||
|
q := "WHERE W=" + strconv.Itoa(int(ch))
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
err := c.db.FindFor(附字表名, &x, "WHERE W="+strconv.Itoa(int(ch)), func() error {
|
err := c.db.FindFor(附字表名, &x, q, func() error {
|
||||||
lstbuf = append(lstbuf, x)
|
lstbuf = append(lstbuf, x)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lstbuf = lstbuf[:0]
|
lstbuf = lstbuf[:0]
|
||||||
err = c.db.FindFor(主字表名, &x, "WHERE W="+strconv.Itoa(int(ch)), func() error {
|
err = c.db.FindFor(主字表名, &x, q, func() error {
|
||||||
lstbuf = append(lstbuf, x)
|
lstbuf = append(lstbuf, x)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
c.字表缓存[ch] = nil
|
||||||
|
return nil, lstbuf, err
|
||||||
|
}
|
||||||
|
if len(lstbuf) == 0 {
|
||||||
|
c.字表缓存[ch] = nil
|
||||||
|
return nil, lstbuf, ErrNoSuchChar
|
||||||
|
}
|
||||||
lstsave := make([]字表, len(lstbuf))
|
lstsave := make([]字表, len(lstbuf))
|
||||||
copy(lstsave, lstbuf)
|
copy(lstsave, lstbuf)
|
||||||
c.字表缓存[ch] = lstsave
|
c.字表缓存[ch] = lstsave
|
||||||
return lstbuf, lstbuf, err
|
return lstbuf, lstbuf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 逆字 逆查匹配的字
|
||||||
|
func (c *Coder) 逆字(s 声母枚举, y 韵母枚举, t 声调枚举, r rune, lstbuf []字表) ([]rune, []字表, error) {
|
||||||
|
id := 逆字ID(s, y, t, r)
|
||||||
|
c.mu.RLock()
|
||||||
|
matches, ok := c.逆字表缓存[id]
|
||||||
|
c.mu.RUnlock()
|
||||||
|
if ok {
|
||||||
|
if len(matches) == 0 {
|
||||||
|
return nil, lstbuf, ErrNoSuchChar
|
||||||
|
}
|
||||||
|
return matches, lstbuf, nil
|
||||||
|
}
|
||||||
|
lstbuf = lstbuf[:0]
|
||||||
|
x := 字表{}
|
||||||
|
sb := strings.Builder{}
|
||||||
|
sb.WriteString("WHERE S=")
|
||||||
|
sb.WriteString(strconv.Itoa(int(s)))
|
||||||
|
sb.WriteString(" AND Y=")
|
||||||
|
sb.WriteString(strconv.Itoa(int(y)))
|
||||||
|
sb.WriteString(" AND T=")
|
||||||
|
sb.WriteString(strconv.Itoa(int(t)))
|
||||||
|
if r != 0 {
|
||||||
|
sb.WriteString(" AND R=")
|
||||||
|
sb.WriteString(strconv.Itoa(int(r)))
|
||||||
|
}
|
||||||
|
q := sb.String()
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
err := c.db.FindFor(附字表名, &x, q, func() error {
|
||||||
|
lstbuf = append(lstbuf, x)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
lstbuf = lstbuf[:0]
|
||||||
|
err = c.db.FindFor(主字表名, &x, q, func() error {
|
||||||
|
lstbuf = append(lstbuf, x)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
c.逆字表缓存[id] = nil
|
||||||
|
return nil, lstbuf, err
|
||||||
|
}
|
||||||
|
if len(lstbuf) == 0 {
|
||||||
|
c.逆字表缓存[id] = nil
|
||||||
|
return nil, lstbuf, ErrNoSuchChar
|
||||||
|
}
|
||||||
|
rs := make([]rune, len(lstbuf))
|
||||||
|
for i, x := range lstbuf {
|
||||||
|
rs[i] = x.W
|
||||||
|
}
|
||||||
|
c.逆字表缓存[id] = rs
|
||||||
|
return rs, lstbuf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从表 从部首表
|
// 从表 从部首表
|
||||||
|
|||||||
56
define.go
56
define.go
@@ -1,8 +1,6 @@
|
|||||||
package emozi
|
package emozi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
base14 "github.com/fumiama/go-base16384"
|
base14 "github.com/fumiama/go-base16384"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -11,9 +9,9 @@ type 声母枚举 uint8
|
|||||||
// String 国际音标
|
// String 国际音标
|
||||||
func (sm 声母枚举) String() string {
|
func (sm 声母枚举) String() string {
|
||||||
a := sm * 3
|
a := sm * 3
|
||||||
b := a + 3
|
b := a + [...]声母枚举{1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 2, 3, 1, 2, 3, 1, 1, 2, 3, 1, 1, 1, 1, 1}[sm]
|
||||||
s := []rune("p pʰ m f t tʰ n l k kʰ x tɕ tɕʰɕ ʈʂ ʈʂʰʂ ɻ ts tsʰs j w ɥ 0 ")[a:b]
|
s := []rune("p pʰ m f t tʰ n l k kʰ x tɕ tɕʰɕ ʈʂ ʈʂʰʂ ɻ ts tsʰs j w ɥ 0 ")[a:b]
|
||||||
return strings.TrimSpace(string(s))
|
return string(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -88,6 +86,33 @@ var 声母 = [...][]string{
|
|||||||
{"🐑", "🐐", "🦷", "💊", "🧂"}, {"🧦", "🌫️", "🕸️", "🥣"}, {"🐟", "🌧️", "🐠"}, {"🪑", "💺"},
|
{"🐑", "🐐", "🦷", "💊", "🧂"}, {"🧦", "🌫️", "🕸️", "🥣"}, {"🐟", "🌧️", "🐠"}, {"🪑", "💺"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func 低阶逆初始化(表 [][]string, 倍数 int) map[rune][]string {
|
||||||
|
m := make(map[rune][]string, len(表)*倍数)
|
||||||
|
for _, lst := range 表 {
|
||||||
|
for _, s := range lst {
|
||||||
|
k := []rune(s)[0]
|
||||||
|
m[k] = append(m[k], s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// 低阶逆声母 rune声母到声母的逆映射
|
||||||
|
var 低阶逆声母 = 低阶逆初始化(声母[:], 32)
|
||||||
|
|
||||||
|
func 逆初始化[T ~uint8](表 [][]string, 倍数 int) map[string]T {
|
||||||
|
m := make(map[string]T, len(表)*倍数)
|
||||||
|
for i, lst := range 表 {
|
||||||
|
for _, s := range lst {
|
||||||
|
m[s] = T(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// 逆声母 声母到枚举的逆映射
|
||||||
|
var 逆声母 = 逆初始化[声母枚举](声母[:], 32)
|
||||||
|
|
||||||
type 韵母枚举 uint8
|
type 韵母枚举 uint8
|
||||||
|
|
||||||
// String 国际音标
|
// String 国际音标
|
||||||
@@ -150,6 +175,12 @@ var 韵母 = [...][]string{
|
|||||||
{string([]rune("🕸️")[0]), "🕸️"}, {"💡", "🪔"}, {"🦅"}, {"🐝"}, {"🌈"}, {"😳"}, // jiong 囧
|
{string([]rune("🕸️")[0]), "🕸️"}, {"💡", "🪔"}, {"🦅"}, {"🐝"}, {"🌈"}, {"😳"}, // jiong 囧
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 低阶逆韵母 rune韵母到韵母的逆映射
|
||||||
|
var 低阶逆韵母 = 低阶逆初始化(韵母[:], 8)
|
||||||
|
|
||||||
|
// 逆韵母 韵母到枚举的逆映射
|
||||||
|
var 逆韵母 = 逆初始化[韵母枚举](韵母[:], 8)
|
||||||
|
|
||||||
type 声调枚举 uint8
|
type 声调枚举 uint8
|
||||||
|
|
||||||
// String 返回传统调名
|
// String 返回传统调名
|
||||||
@@ -169,6 +200,12 @@ const (
|
|||||||
|
|
||||||
var 声调 = [...][]string{{"🍉"}, {"🧗", "🧗♀️", "🧗♂️", "🦎"}, {"🐴", "🐎"}, {"😨"}, {"😯"}}
|
var 声调 = [...][]string{{"🍉"}, {"🧗", "🧗♀️", "🧗♂️", "🦎"}, {"🐴", "🐎"}, {"😨"}, {"😯"}}
|
||||||
|
|
||||||
|
// 低阶逆声调 rune声调到声调的逆映射
|
||||||
|
var 低阶逆声调 = 低阶逆初始化(声调[:], 4)
|
||||||
|
|
||||||
|
// 逆声调 声调到枚举的逆映射
|
||||||
|
var 逆声调 = 逆初始化[声调枚举](声调[:], 4)
|
||||||
|
|
||||||
// 校验表 用 校验表长度 个unicode控制字符做校验和验证此序列是由本程序生成而非手写的
|
// 校验表 用 校验表长度 个unicode控制字符做校验和验证此序列是由本程序生成而非手写的
|
||||||
//
|
//
|
||||||
// 具体做法是先对后面的文本做crc32然后取 校验表长度^校验字节数 的模
|
// 具体做法是先对后面的文本做crc32然后取 校验表长度^校验字节数 的模
|
||||||
@@ -208,3 +245,14 @@ var 逆校验表 = func() map[rune]uint8 {
|
|||||||
}
|
}
|
||||||
return m
|
return m
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// 逆部首后备 部首后备逆查表 一对多
|
||||||
|
var 逆部首后备 = func() map[string][]rune {
|
||||||
|
m := make(map[string][]rune, len(部首后备)*4)
|
||||||
|
for r, e := range 部首后备 {
|
||||||
|
if 无此字符(m[e], r) {
|
||||||
|
m[e] = append(m[e], r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}()
|
||||||
|
|||||||
149
lookup.go
149
lookup.go
@@ -3,44 +3,35 @@ package emozi
|
|||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var 空 = "🈳️"
|
var 空 = "🈳️"
|
||||||
|
|
||||||
func (c *Coder) 查声母(s 声母枚举) string {
|
func 随机正查(m [][]string, isRandom bool, i uint8) string {
|
||||||
lst := 声母[s]
|
lst := m[i]
|
||||||
if len(lst) == 0 {
|
if len(lst) == 0 {
|
||||||
return 空
|
return 空
|
||||||
}
|
}
|
||||||
if len(lst) == 1 || !c.isRandom {
|
if len(lst) == 1 || !isRandom {
|
||||||
return lst[0]
|
return lst[0]
|
||||||
}
|
}
|
||||||
return lst[rand.Intn(len(lst))]
|
return lst[rand.Intn(len(lst))]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Coder) 查韵母(y 韵母枚举) string {
|
func (c *Coder) 声母(isRandom bool, s 声母枚举) string {
|
||||||
lst := 韵母[y]
|
return 随机正查(声母[:], isRandom, uint8(s))
|
||||||
if len(lst) == 0 {
|
|
||||||
return 空
|
|
||||||
}
|
|
||||||
if len(lst) == 1 || !c.isRandom {
|
|
||||||
return lst[0]
|
|
||||||
}
|
|
||||||
return lst[rand.Intn(len(lst))]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Coder) 查声调(t 声调枚举) string {
|
func (c *Coder) 韵母(isRandom bool, y 韵母枚举) string {
|
||||||
lst := 声调[t]
|
return 随机正查(韵母[:], isRandom, uint8(y))
|
||||||
if len(lst) == 0 {
|
|
||||||
return 空
|
|
||||||
}
|
|
||||||
if len(lst) == 1 || !c.isRandom {
|
|
||||||
return lst[0]
|
|
||||||
}
|
|
||||||
return lst[rand.Intn(len(lst))]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Coder) 查部首(r rune) string {
|
func (c *Coder) 声调(isRandom bool, t 声调枚举) string {
|
||||||
|
return 随机正查(声调[:], isRandom, uint8(t))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Coder) 部首(r rune) string {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
e, ok := c.部首缓存[r]
|
e, ok := c.部首缓存[r]
|
||||||
c.mu.RUnlock()
|
c.mu.RUnlock()
|
||||||
@@ -62,3 +53,117 @@ func (c *Coder) 查部首(r rune) string {
|
|||||||
c.部首缓存[r] = 空
|
c.部首缓存[r] = 空
|
||||||
return 空
|
return 空
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func 二阶逆查[E ~uint8](lowm map[rune][]string, m map[string]E, s string) (enum E, n int) {
|
||||||
|
lowk := rune(0)
|
||||||
|
lows := s
|
||||||
|
if len(lows) > 12 {
|
||||||
|
lows = lows[:12]
|
||||||
|
}
|
||||||
|
r := []rune(lows)
|
||||||
|
if len(r) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lowk = r[0]
|
||||||
|
ks := lowm[lowk]
|
||||||
|
if len(ks) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 寻找最长匹配 T
|
||||||
|
matchp := -1
|
||||||
|
matchl := 0
|
||||||
|
for i, k := range ks {
|
||||||
|
if strings.HasPrefix(s, k) {
|
||||||
|
if len(k) > matchl {
|
||||||
|
matchl = len(k)
|
||||||
|
matchp = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if matchp < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
enum, ok := m[ks[matchp]]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n = matchl
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Coder) 逆声母(s string) (声母枚举, int) {
|
||||||
|
return 二阶逆查[声母枚举](低阶逆声母, 逆声母, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Coder) 逆韵母(s string) (韵母枚举, int) {
|
||||||
|
return 二阶逆查[韵母枚举](低阶逆韵母, 逆韵母, s)
|
||||||
|
}
|
||||||
|
func (c *Coder) 逆声调(s string) (声调枚举, int) {
|
||||||
|
return 二阶逆查[声调枚举](低阶逆声调, 逆声调, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Coder) 逆部首(s string) (rs []rune, n int) {
|
||||||
|
lim := len(s)
|
||||||
|
if lim > 32 {
|
||||||
|
lim = 32
|
||||||
|
}
|
||||||
|
c.mu.RLock()
|
||||||
|
for i := 1; i < lim; i++ {
|
||||||
|
l := c.逆部首缓存[s[:i]]
|
||||||
|
if len(l) > 0 {
|
||||||
|
rs = l
|
||||||
|
n = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.mu.RUnlock()
|
||||||
|
if n > 0 && len(rs) > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
x := &部首表{}
|
||||||
|
sb := strings.Builder{}
|
||||||
|
sb.WriteString("WHERE ")
|
||||||
|
for i := 1; i < lim; i++ {
|
||||||
|
sb.WriteString("E='")
|
||||||
|
sb.WriteString(s[:i])
|
||||||
|
sb.WriteString("' OR ")
|
||||||
|
}
|
||||||
|
q := sb.String()[:sb.Len()-4]
|
||||||
|
n = 0
|
||||||
|
e := ""
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
err := c.db.FindFor(部首表名, x, q, func() error {
|
||||||
|
if len(x.E) > n {
|
||||||
|
n = len(x.E)
|
||||||
|
rs = rs[:0]
|
||||||
|
e = x.E
|
||||||
|
}
|
||||||
|
if len(x.E) == n && 无此字符(rs, x.R) {
|
||||||
|
rs = append(rs, x.R)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err == nil && len(rs) > 0 && n > 0 {
|
||||||
|
c.逆部首缓存[e] = rs
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 1; i < lim; i++ {
|
||||||
|
k := s[:i]
|
||||||
|
innerrs, ok := 逆部首后备[k]
|
||||||
|
c.逆部首缓存[k] = innerrs
|
||||||
|
if ok && len(innerrs) > 0 {
|
||||||
|
n = i
|
||||||
|
rs = innerrs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func 无此字符(runes []rune, ch rune) bool {
|
||||||
|
for _, r := range runes {
|
||||||
|
if ch == r {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,18 @@
|
|||||||
package emozi
|
package emozi
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestShengmuString(t *testing.T) {
|
||||||
|
for i := 0; i < len(声母); i++ {
|
||||||
|
t.Log(声母枚举(i).String())
|
||||||
|
if 声母枚举(i).String() != strings.TrimSpace(声母枚举(i).String()) {
|
||||||
|
t.Fatal("声母: '", 声母枚举(i), "'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSplitPinyin(t *testing.T) {
|
func TestSplitPinyin(t *testing.T) {
|
||||||
s, y, tone, err := SplitPinyin("yōng")
|
s, y, tone, err := SplitPinyin("yōng")
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func main() {
|
|||||||
panic(fmt.Sprintf("ERROR: creating gocc: %v", err))
|
panic(fmt.Sprintf("ERROR: creating gocc: %v", err))
|
||||||
}
|
}
|
||||||
_ = os.RemoveAll(emozi.EmoziDatabasePath)
|
_ = os.RemoveAll(emozi.EmoziDatabasePath)
|
||||||
c, err := emozi.NewCoder(false, time.Minute)
|
c, err := emozi.NewCoder(time.Minute)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("ERROR: creating emozi coder: %v", err))
|
panic(fmt.Sprintf("ERROR: creating emozi coder: %v", err))
|
||||||
}
|
}
|
||||||
|
|||||||
920
radical.go
920
radical.go
@@ -40,508 +40,508 @@ var 部首后备 = map[rune]string{
|
|||||||
'辵': "🚶",
|
'辵': "🚶",
|
||||||
'彳': "🚦",
|
'彳': "🚦",
|
||||||
'廴': "🚥",
|
'廴': "🚥",
|
||||||
'㢟': "🈳️",
|
'㢟': 空,
|
||||||
'行': "⛕",
|
'行': "⛕",
|
||||||
'齒': "🈳️",
|
'齒': 空,
|
||||||
'牙': "🈳️",
|
'牙': 空,
|
||||||
'足': "🈳️",
|
'足': 空,
|
||||||
'疋': "🈳️",
|
'疋': 空,
|
||||||
'品': "🈳️",
|
'品': 空,
|
||||||
'龠': "🈳️",
|
'龠': 空,
|
||||||
'冊': "🈳️",
|
'冊': 空,
|
||||||
'㗊': "🈳️",
|
'㗊': 空,
|
||||||
'舌': "👅",
|
'舌': "👅",
|
||||||
'干': "🈳️",
|
'干': 空,
|
||||||
'𧮫': "🈳️",
|
'𧮫': 空,
|
||||||
'只': "🈳️",
|
'只': 空,
|
||||||
'㕯': "🈳️",
|
'㕯': 空,
|
||||||
'句': "🈳️",
|
'句': 空,
|
||||||
'丩': "🈳️",
|
'丩': 空,
|
||||||
'古': "🏺",
|
'古': "🏺",
|
||||||
'十': "🔟",
|
'十': "🔟",
|
||||||
'卅': "🌍",
|
'卅': "🌍",
|
||||||
'言': "💬",
|
'言': "💬",
|
||||||
'田': "👨🌾",
|
'田': "👨🌾",
|
||||||
'誩': "🈳️",
|
'誩': 空,
|
||||||
'音': "🎵",
|
'音': "🎵",
|
||||||
'䇂': "🈳️",
|
'䇂': 空,
|
||||||
'丵': "🈳️",
|
'丵': 空,
|
||||||
'菐': "🈳️",
|
'菐': 空,
|
||||||
'𠬞': "🈳️",
|
'𠬞': 空,
|
||||||
'𠬜': "🈳️",
|
'𠬜': 空,
|
||||||
'共': "🈳️",
|
'共': 空,
|
||||||
'異': "🈳️",
|
'異': 空,
|
||||||
'舁': "🈳️",
|
'舁': 空,
|
||||||
'𦥑': "🈳️",
|
'𦥑': 空,
|
||||||
'䢅': "🈳️",
|
'䢅': 空,
|
||||||
'爨': "🈳️",
|
'爨': 空,
|
||||||
'革': "🈳️",
|
'革': 空,
|
||||||
'鬲': "🈳️",
|
'鬲': 空,
|
||||||
'䰜': "🈳️",
|
'䰜': 空,
|
||||||
'爪': "🤏",
|
'爪': "🤏",
|
||||||
'丮': "🈳️",
|
'丮': 空,
|
||||||
'鬥': "👊",
|
'鬥': "👊",
|
||||||
'又': "🈳️",
|
'又': 空,
|
||||||
'𠂇': "🈳️",
|
'𠂇': 空,
|
||||||
'史': "🈳️",
|
'史': 空,
|
||||||
'支': "🈳️",
|
'支': 空,
|
||||||
'𦘒': "🈳️",
|
'𦘒': 空,
|
||||||
'聿': "✍",
|
'聿': "✍",
|
||||||
'畫': "🎨",
|
'畫': "🎨",
|
||||||
'隶': "🈳️",
|
'隶': 空,
|
||||||
'臤': "🈳️",
|
'臤': 空,
|
||||||
'臣': "🈳️",
|
'臣': 空,
|
||||||
'殳': "🈳️",
|
'殳': 空,
|
||||||
'殺': "🪚",
|
'殺': "🪚",
|
||||||
'𠘧': "🈳️",
|
'𠘧': 空,
|
||||||
'寸': "🈳️",
|
'寸': 空,
|
||||||
'皮': "🈳️",
|
'皮': 空,
|
||||||
'㼱': "🈳️",
|
'㼱': 空,
|
||||||
'攴': "🈳️",
|
'攴': 空,
|
||||||
'女': "👩",
|
'女': "👩",
|
||||||
'教': "🈳️",
|
'教': 空,
|
||||||
'卜': "🔮",
|
'卜': "🔮",
|
||||||
'用': "🈳️",
|
'用': 空,
|
||||||
'爻': "🈳️",
|
'爻': 空,
|
||||||
'㸚': "🈳️",
|
'㸚': 空,
|
||||||
'𡕥': "🈳️",
|
'𡕥': 空,
|
||||||
'目': "👁️",
|
'目': "👁️",
|
||||||
'䀠': "👀",
|
'䀠': "👀",
|
||||||
'眉': "🤨",
|
'眉': "🤨",
|
||||||
'盾': "🈳️",
|
'盾': 空,
|
||||||
'自': "👃",
|
'自': "👃",
|
||||||
'𪞶': "🈳️",
|
'𪞶': 空,
|
||||||
'鼻': "👃",
|
'鼻': "👃",
|
||||||
'皕': "🈳️",
|
'皕': 空,
|
||||||
'習': "🈳️",
|
'習': 空,
|
||||||
'羽': "🪶",
|
'羽': "🪶",
|
||||||
'隹': "🐦",
|
'隹': "🐦",
|
||||||
'奞': "🈳️",
|
'奞': 空,
|
||||||
'雈': "🈳️",
|
'雈': 空,
|
||||||
'𦫳': "🈳️",
|
'𦫳': 空,
|
||||||
'𥄕': "🈳️",
|
'𥄕': 空,
|
||||||
'羊': "🐑",
|
'羊': "🐑",
|
||||||
'羴': "🈳️",
|
'羴': 空,
|
||||||
'瞿': "🈳️",
|
'瞿': 空,
|
||||||
'雔': "🈳️",
|
'雔': 空,
|
||||||
'雥': "🈳️",
|
'雥': 空,
|
||||||
'鳥': "🦢",
|
'鳥': "🦢",
|
||||||
'烏': "🐦⬛",
|
'烏': "🐦⬛",
|
||||||
'𠦒': "🈳",
|
'𠦒': 空,
|
||||||
'冓': "🈳️",
|
'冓': 空,
|
||||||
'幺': "🈳️",
|
'幺': 空,
|
||||||
'𢆶': "🈳️",
|
'𢆶': 空,
|
||||||
'叀': "🈳️",
|
'叀': 空,
|
||||||
'玄': "🈳️",
|
'玄': 空,
|
||||||
'予': "🈳️",
|
'予': 空,
|
||||||
'放': "🈳️",
|
'放': 空,
|
||||||
'𠬪': "🈳️",
|
'𠬪': 空,
|
||||||
'𣦼': "🈳️",
|
'𣦼': 空,
|
||||||
'歺': "🈳️",
|
'歺': 空,
|
||||||
'死': "🈳️",
|
'死': 空,
|
||||||
'冎': "🈳️",
|
'冎': 空,
|
||||||
'骨': "🈳️",
|
'骨': 空,
|
||||||
'肉': "🈳️",
|
'肉': 空,
|
||||||
'筋': "🈳️",
|
'筋': 空,
|
||||||
'刀': "🔪",
|
'刀': "🔪",
|
||||||
'刃': "🔪",
|
'刃': "🔪",
|
||||||
'㓞': "🈳️",
|
'㓞': 空,
|
||||||
'丯': "🈳️",
|
'丯': 空,
|
||||||
'耒': "🈳️",
|
'耒': 空,
|
||||||
'角': "🥐",
|
'角': "🥐",
|
||||||
'竹': "🎋",
|
'竹': "🎋",
|
||||||
'箕': "🈳️",
|
'箕': 空,
|
||||||
'丌': "🈳️",
|
'丌': 空,
|
||||||
'左': "👈",
|
'左': "👈",
|
||||||
'工': "🈳️",
|
'工': 空,
|
||||||
'㠭': "🈳️",
|
'㠭': 空,
|
||||||
'巫': "🈳️",
|
'巫': 空,
|
||||||
'甘': "🈳️",
|
'甘': 空,
|
||||||
'曰': "🈳️",
|
'曰': 空,
|
||||||
'乃': "🈳️",
|
'乃': 空,
|
||||||
'丂': "🈳️",
|
'丂': 空,
|
||||||
'可': "🈳️",
|
'可': 空,
|
||||||
'兮': "🈳️",
|
'兮': 空,
|
||||||
'号': "🈳️",
|
'号': 空,
|
||||||
'亏': "🈳️",
|
'亏': 空,
|
||||||
'旨': "🈳️",
|
'旨': 空,
|
||||||
'喜': "🈳️",
|
'喜': 空,
|
||||||
'壴': "🈳️",
|
'壴': 空,
|
||||||
'鼓': "🈳️",
|
'鼓': 空,
|
||||||
'豈': "🈳️",
|
'豈': 空,
|
||||||
'豆': "🈳️",
|
'豆': 空,
|
||||||
'豊': "🈳️",
|
'豊': 空,
|
||||||
'豐': "🈳️",
|
'豐': 空,
|
||||||
'䖒': "🈳️",
|
'䖒': 空,
|
||||||
'虍': "🈳️",
|
'虍': 空,
|
||||||
'虎': "🈳️",
|
'虎': 空,
|
||||||
'虤': "🈳️",
|
'虤': 空,
|
||||||
'皿': "🈳️",
|
'皿': 空,
|
||||||
'𠙴': "🈳️",
|
'𠙴': 空,
|
||||||
'去': "🈳️",
|
'去': 空,
|
||||||
'血': "🈳️",
|
'血': 空,
|
||||||
'丶': "🈳️",
|
'丶': 空,
|
||||||
'丹': "🈳️",
|
'丹': 空,
|
||||||
'青': "🈳️",
|
'青': 空,
|
||||||
'井': "🈳️",
|
'井': 空,
|
||||||
'皀': "🈳️",
|
'皀': 空,
|
||||||
'鬯': "🈳️",
|
'鬯': 空,
|
||||||
'食': "🈳️",
|
'食': 空,
|
||||||
'亼': "🈳️",
|
'亼': 空,
|
||||||
'會': "🈳️",
|
'會': 空,
|
||||||
'倉': "🈳️",
|
'倉': 空,
|
||||||
'入': "🈳️",
|
'入': 空,
|
||||||
'缶': "🈳️",
|
'缶': 空,
|
||||||
'矢': "🈳️",
|
'矢': 空,
|
||||||
'高': "🈳️",
|
'高': 空,
|
||||||
'冂': "🈳️",
|
'冂': 空,
|
||||||
'𩫖': "🈳️",
|
'𩫖': 空,
|
||||||
'京': "🈳️",
|
'京': 空,
|
||||||
'亯': "🈳️",
|
'亯': 空,
|
||||||
'㫗': "🈳️",
|
'㫗': 空,
|
||||||
'畗': "🈳️",
|
'畗': 空,
|
||||||
'㐭': "🈳️",
|
'㐭': 空,
|
||||||
'嗇': "🈳️",
|
'嗇': 空,
|
||||||
'來': "🈳️",
|
'來': 空,
|
||||||
'麥': "🈳️",
|
'麥': 空,
|
||||||
'夊': "🈳️",
|
'夊': 空,
|
||||||
'舛': "🈳️",
|
'舛': 空,
|
||||||
'舜': "🈳️",
|
'舜': 空,
|
||||||
'韋': "🈳️",
|
'韋': 空,
|
||||||
'弟': "🈳️",
|
'弟': 空,
|
||||||
'夂': "🈳️",
|
'夂': 空,
|
||||||
'久': "🈳️",
|
'久': 空,
|
||||||
'桀': "🈳️",
|
'桀': 空,
|
||||||
'木': "🈳️",
|
'木': 空,
|
||||||
'東': "🈳️",
|
'東': 空,
|
||||||
'林': "🈳️",
|
'林': 空,
|
||||||
'才': "🈳️",
|
'才': 空,
|
||||||
'叒': "🈳️",
|
'叒': 空,
|
||||||
'之': "🈳️",
|
'之': 空,
|
||||||
'帀': "🈳️",
|
'帀': 空,
|
||||||
'出': "🈳️",
|
'出': 空,
|
||||||
'𣎵': "🈳️",
|
'𣎵': 空,
|
||||||
'生': "🈳️",
|
'生': 空,
|
||||||
'乇': "🈳️",
|
'乇': 空,
|
||||||
'𠂹': "🈳️",
|
'𠂹': 空,
|
||||||
'𠌶': "🈳️",
|
'𠌶': 空,
|
||||||
'華': "🈳️",
|
'華': 空,
|
||||||
'𥝌': "🈳️",
|
'𥝌': 空,
|
||||||
'稽': "🈳️",
|
'稽': 空,
|
||||||
'巢': "🈳️",
|
'巢': 空,
|
||||||
'桼': "🈳️",
|
'桼': 空,
|
||||||
'束': "🈳️",
|
'束': 空,
|
||||||
'㯻': "🈳️",
|
'㯻': 空,
|
||||||
'囗': "🈳️",
|
'囗': 空,
|
||||||
'員': "🈳️",
|
'員': 空,
|
||||||
'貝': "🈳️",
|
'貝': 空,
|
||||||
'邑': "🈳️",
|
'邑': 空,
|
||||||
'𨛜': "🈳️",
|
'𨛜': 空,
|
||||||
'日': "🈳️",
|
'日': 空,
|
||||||
'旦': "🈳️",
|
'旦': 空,
|
||||||
'倝': "🈳️",
|
'倝': 空,
|
||||||
'㫃': "🈳️",
|
'㫃': 空,
|
||||||
'冥': "🈳️",
|
'冥': 空,
|
||||||
'晶': "🈳️",
|
'晶': 空,
|
||||||
'月': "🈳️",
|
'月': 空,
|
||||||
'有': "🈳️",
|
'有': 空,
|
||||||
'朙': "🈳️",
|
'朙': 空,
|
||||||
'囧': "🈳️",
|
'囧': 空,
|
||||||
'夕': "🌇",
|
'夕': "🌇",
|
||||||
'多': "🪩",
|
'多': "🪩",
|
||||||
'毌': "🈳️",
|
'毌': 空,
|
||||||
'𢎘': "🈳️",
|
'𢎘': 空,
|
||||||
'𣐺': "🈳️",
|
'𣐺': 空,
|
||||||
'𠧪': "🈳️",
|
'𠧪': 空,
|
||||||
'齊': "🈳️",
|
'齊': 空,
|
||||||
'朿': "🈳️",
|
'朿': 空,
|
||||||
'片': "🈳️",
|
'片': 空,
|
||||||
'鼎': "🈳️",
|
'鼎': 空,
|
||||||
'克': "🈳️",
|
'克': 空,
|
||||||
'彔': "🈳️",
|
'彔': 空,
|
||||||
'禾': "🈳️",
|
'禾': 空,
|
||||||
'秝': "🈳️",
|
'秝': 空,
|
||||||
'黍': "🈳️",
|
'黍': 空,
|
||||||
'香': "🈳️",
|
'香': 空,
|
||||||
'米': "🈳️",
|
'米': 空,
|
||||||
'毇': "🈳️",
|
'毇': 空,
|
||||||
'臼': "🈳️",
|
'臼': 空,
|
||||||
'凶': "🈳️",
|
'凶': 空,
|
||||||
'朩': "🈳️",
|
'朩': 空,
|
||||||
'𣏟': "🈳️",
|
'𣏟': 空,
|
||||||
'麻': "🈳️",
|
'麻': 空,
|
||||||
'尗': "🈳️",
|
'尗': 空,
|
||||||
'耑': "🈳️",
|
'耑': 空,
|
||||||
'韭': "🈳️",
|
'韭': 空,
|
||||||
'瓜': "🈳️",
|
'瓜': 空,
|
||||||
'瓠': "🈳️",
|
'瓠': 空,
|
||||||
'宀': "🏠",
|
'宀': "🏠",
|
||||||
'宮': "🏛",
|
'宮': "🏛",
|
||||||
'呂': "🩻",
|
'呂': "🩻",
|
||||||
'穴': "🕳️",
|
'穴': "🕳️",
|
||||||
'㝱': "🈳️",
|
'㝱': 空,
|
||||||
'疒': "😷",
|
'疒': "😷",
|
||||||
'冖': "🗃",
|
'冖': "🗃",
|
||||||
'𠔼': "🈳️",
|
'𠔼': 空,
|
||||||
'冃': "🈳️",
|
'冃': 空,
|
||||||
'㒳': "🈳️",
|
'㒳': 空,
|
||||||
'网': "🈳️",
|
'网': 空,
|
||||||
'襾': "🈳️",
|
'襾': 空,
|
||||||
'巾': "🧣",
|
'巾': "🧣",
|
||||||
'巿': "🈳️",
|
'巿': 空,
|
||||||
'帛': "🈳️",
|
'帛': 空,
|
||||||
'白': "⚪",
|
'白': "⚪",
|
||||||
'㡀': "🈳️",
|
'㡀': 空,
|
||||||
'黹': "🈳️",
|
'黹': 空,
|
||||||
'𠤎': "🈳️",
|
'𠤎': 空,
|
||||||
'匕': "🈳️",
|
'匕': 空,
|
||||||
'从': "🈳️",
|
'从': 空,
|
||||||
'比': "🈳️",
|
'比': 空,
|
||||||
'北': "🈳️",
|
'北': 空,
|
||||||
'丘': "🈳️",
|
'丘': 空,
|
||||||
'㐺': "🈳️",
|
'㐺': 空,
|
||||||
'𡈼': "🈳️",
|
'𡈼': 空,
|
||||||
'重': "🈳️",
|
'重': 空,
|
||||||
'臥': "🈳️",
|
'臥': 空,
|
||||||
'身': "🈳️",
|
'身': 空,
|
||||||
'㐆': "🈳️",
|
'㐆': 空,
|
||||||
'衣': "🈳️",
|
'衣': 空,
|
||||||
'裘': "🈳️",
|
'裘': 空,
|
||||||
'老': "🈳️",
|
'老': 空,
|
||||||
'毛': "🈳️",
|
'毛': 空,
|
||||||
'毳': "🈳️",
|
'毳': 空,
|
||||||
'尸': "🈳️",
|
'尸': 空,
|
||||||
'尺': "🈳️",
|
'尺': 空,
|
||||||
'尾': "🈳️",
|
'尾': 空,
|
||||||
'履': "🈳️",
|
'履': 空,
|
||||||
'舟': "🛶",
|
'舟': "🛶",
|
||||||
'方': "🈳️",
|
'方': 空,
|
||||||
'儿': "🈳️",
|
'儿': 空,
|
||||||
'兄': "🈳️",
|
'兄': 空,
|
||||||
'兂': "🈳️",
|
'兂': 空,
|
||||||
'皃': "🈳️",
|
'皃': 空,
|
||||||
'𠑹': "🈳️",
|
'𠑹': 空,
|
||||||
'先': "🈳️",
|
'先': 空,
|
||||||
'禿': "🈳️",
|
'禿': 空,
|
||||||
'見': "🈳️",
|
'見': 空,
|
||||||
'覞': "🈳️",
|
'覞': 空,
|
||||||
'欠': "🈳️",
|
'欠': 空,
|
||||||
'㱃': "🈳️",
|
'㱃': 空,
|
||||||
'㳄': "🈳️",
|
'㳄': 空,
|
||||||
'旡': "🈳️",
|
'旡': 空,
|
||||||
'頁': "🈳️",
|
'頁': 空,
|
||||||
'𦣻': "🈳️",
|
'𦣻': 空,
|
||||||
'面': "🈳️",
|
'面': 空,
|
||||||
'丏': "🈳️",
|
'丏': 空,
|
||||||
'首': "🈳️",
|
'首': 空,
|
||||||
'𥄉': "🈳️",
|
'𥄉': 空,
|
||||||
'須': "🈳️",
|
'須': 空,
|
||||||
'彡': "🈳️",
|
'彡': 空,
|
||||||
'彣': "🈳️",
|
'彣': 空,
|
||||||
'文': "🈳️",
|
'文': 空,
|
||||||
'髟': "🈳️",
|
'髟': 空,
|
||||||
'后': "🈳️",
|
'后': 空,
|
||||||
'司': "🈳️",
|
'司': 空,
|
||||||
'卮': "🈳️",
|
'卮': 空,
|
||||||
'卩': "🈳️",
|
'卩': 空,
|
||||||
'印': "🈳️",
|
'印': 空,
|
||||||
'色': "🈳️",
|
'色': 空,
|
||||||
'𠨍': "🈳️",
|
'𠨍': 空,
|
||||||
'辟': "🈳️",
|
'辟': 空,
|
||||||
'勹': "🈳️",
|
'勹': 空,
|
||||||
'包': "🈳️",
|
'包': 空,
|
||||||
'茍': "🈳️",
|
'茍': 空,
|
||||||
'鬼': "🈳️",
|
'鬼': 空,
|
||||||
'甶': "🈳️",
|
'甶': 空,
|
||||||
'厶': "🈳️",
|
'厶': 空,
|
||||||
'嵬': "🈳️",
|
'嵬': 空,
|
||||||
'山': "🈳️",
|
'山': 空,
|
||||||
'屾': "🈳️",
|
'屾': 空,
|
||||||
'屵': "🈳️",
|
'屵': 空,
|
||||||
'广': "🈳️",
|
'广': 空,
|
||||||
'厂': "🈳️",
|
'厂': 空,
|
||||||
'丸': "🈳️",
|
'丸': 空,
|
||||||
'危': "🈳️",
|
'危': 空,
|
||||||
'石': "🈳️",
|
'石': 空,
|
||||||
'長': "🈳️",
|
'長': 空,
|
||||||
'勿': "🈳️",
|
'勿': 空,
|
||||||
'冄': "🈳️",
|
'冄': 空,
|
||||||
'而': "🈳️",
|
'而': 空,
|
||||||
'豕': "🈳️",
|
'豕': 空,
|
||||||
'㣇': "🈳️",
|
'㣇': 空,
|
||||||
'彑': "🈳️",
|
'彑': 空,
|
||||||
'豚': "🈳️",
|
'豚': 空,
|
||||||
'豸': "🈳️",
|
'豸': 空,
|
||||||
'𤉡': "🈳️",
|
'𤉡': 空,
|
||||||
'易': "🈳️",
|
'易': 空,
|
||||||
'象': "🈳️",
|
'象': 空,
|
||||||
'馬': "🈳️",
|
'馬': 空,
|
||||||
'𢊁': "🈳️",
|
'𢊁': 空,
|
||||||
'鹿': "🈳️",
|
'鹿': 空,
|
||||||
'麤': "🈳️",
|
'麤': 空,
|
||||||
'㲋': "🈳️",
|
'㲋': 空,
|
||||||
'兔': "🈳️",
|
'兔': 空,
|
||||||
'萈': "🈳️",
|
'萈': 空,
|
||||||
'犬': "🈳️",
|
'犬': 空,
|
||||||
'㹜': "🈳️",
|
'㹜': 空,
|
||||||
'鼠': "🈳️",
|
'鼠': 空,
|
||||||
'能': "🈳️",
|
'能': 空,
|
||||||
'熊': "🈳️",
|
'熊': 空,
|
||||||
'火': "🈳️",
|
'火': 空,
|
||||||
'炎': "🈳️",
|
'炎': 空,
|
||||||
'黑': "🈳️",
|
'黑': 空,
|
||||||
'囪': "🈳️",
|
'囪': 空,
|
||||||
'焱': "🈳️",
|
'焱': 空,
|
||||||
'炙': "🈳️",
|
'炙': 空,
|
||||||
'赤': "🈳️",
|
'赤': 空,
|
||||||
'大': "🈳️",
|
'大': 空,
|
||||||
'亦': "🈳️",
|
'亦': 空,
|
||||||
'夨': "🈳️",
|
'夨': 空,
|
||||||
'夭': "🈳️",
|
'夭': 空,
|
||||||
'交': "🈳️",
|
'交': 空,
|
||||||
'尣': "🈳️",
|
'尣': 空,
|
||||||
'壺': "🈳️",
|
'壺': 空,
|
||||||
'壹': "🈳️",
|
'壹': 空,
|
||||||
'㚔': "🈳️",
|
'㚔': 空,
|
||||||
'奢': "🈳️",
|
'奢': 空,
|
||||||
'亢': "🈳️",
|
'亢': 空,
|
||||||
'夲': "🈳️",
|
'夲': 空,
|
||||||
'夰': "🈳️",
|
'夰': 空,
|
||||||
'亣': "🈳️",
|
'亣': 空,
|
||||||
'夫': "🈳️",
|
'夫': 空,
|
||||||
'立': "🈳️",
|
'立': 空,
|
||||||
'竝': "🈳️",
|
'竝': 空,
|
||||||
'囟': "🈳️",
|
'囟': 空,
|
||||||
'思': "🈳️",
|
'思': 空,
|
||||||
'心': "🈳️",
|
'心': 空,
|
||||||
'惢': "🈳️",
|
'惢': 空,
|
||||||
'水': "🈳️",
|
'水': 空,
|
||||||
'沝': "🈳️",
|
'沝': 空,
|
||||||
'瀕': "🈳️",
|
'瀕': 空,
|
||||||
'𡿨': "🈳️",
|
'𡿨': 空,
|
||||||
'巜': "🈳️",
|
'巜': 空,
|
||||||
'川': "🈳️",
|
'川': 空,
|
||||||
'泉': "🈳️",
|
'泉': 空,
|
||||||
'灥': "🈳️",
|
'灥': 空,
|
||||||
'永': "🈳️",
|
'永': 空,
|
||||||
'𠂢': "🈳️",
|
'𠂢': 空,
|
||||||
'谷': "🈳️",
|
'谷': 空,
|
||||||
'仌': "🈳️",
|
'仌': 空,
|
||||||
'雨': "🈳️",
|
'雨': 空,
|
||||||
'雲': "🈳️",
|
'雲': 空,
|
||||||
'魚': "🈳️",
|
'魚': 空,
|
||||||
'𩺰': "🈳️",
|
'𩺰': 空,
|
||||||
'燕': "🈳️",
|
'燕': 空,
|
||||||
'龍': "🈳️",
|
'龍': 空,
|
||||||
'飛': "🈳️",
|
'飛': 空,
|
||||||
'非': "🈳️",
|
'非': 空,
|
||||||
'卂': "🈳️",
|
'卂': 空,
|
||||||
'𠃉': "🈳️",
|
'𠃉': 空,
|
||||||
'不': "🈳️",
|
'不': 空,
|
||||||
'至': "🈳️",
|
'至': 空,
|
||||||
'西': "🈳️",
|
'西': 空,
|
||||||
'鹵': "🈳️",
|
'鹵': 空,
|
||||||
'鹽': "🈳️",
|
'鹽': 空,
|
||||||
'戶': "🈳️",
|
'戶': 空,
|
||||||
'門': "🈳️",
|
'門': 空,
|
||||||
'耳': "🈳️",
|
'耳': 空,
|
||||||
'𦣞': "🈳️",
|
'𦣞': 空,
|
||||||
'手': "✋",
|
'手': "✋",
|
||||||
'𠦬': "🈳️",
|
'𠦬': 空,
|
||||||
'毋': "🈳️",
|
'毋': 空,
|
||||||
'民': "🈳️",
|
'民': 空,
|
||||||
'丿': "🈳️",
|
'丿': 空,
|
||||||
'𠂆': "🈳️",
|
'𠂆': 空,
|
||||||
'乁': "🈳️",
|
'乁': 空,
|
||||||
'氏': "🈳️",
|
'氏': 空,
|
||||||
'氐': "🈳️",
|
'氐': 空,
|
||||||
'戈': "🈳️",
|
'戈': 空,
|
||||||
'戉': "🈳️",
|
'戉': 空,
|
||||||
'我': "🈳️",
|
'我': 空,
|
||||||
'亅': "🈳️",
|
'亅': 空,
|
||||||
'珡': "🈳️",
|
'珡': 空,
|
||||||
'𠃊': "🈳️",
|
'𠃊': 空,
|
||||||
'亾': "🈳️",
|
'亾': 空,
|
||||||
'匸': "🈳️",
|
'匸': 空,
|
||||||
'匚': "🈳️",
|
'匚': 空,
|
||||||
'曲': "🈳️",
|
'曲': 空,
|
||||||
'甾': "🈳️",
|
'甾': 空,
|
||||||
'瓦': "🈳️",
|
'瓦': 空,
|
||||||
'弓': "🈳️",
|
'弓': 空,
|
||||||
'弜': "🈳️",
|
'弜': 空,
|
||||||
'弦': "🈳️",
|
'弦': 空,
|
||||||
'系': "🈳️",
|
'系': 空,
|
||||||
'糸': "🈳️",
|
'糸': 空,
|
||||||
'素': "🈳️",
|
'素': 空,
|
||||||
'絲': "🈳️",
|
'絲': 空,
|
||||||
'率': "🈳️",
|
'率': 空,
|
||||||
'虫': "🈳️",
|
'虫': 空,
|
||||||
'䖵': "🈳️",
|
'䖵': 空,
|
||||||
'蟲': "🈳️",
|
'蟲': 空,
|
||||||
'風': "🈳️",
|
'風': 空,
|
||||||
'它': "🈳️",
|
'它': 空,
|
||||||
'龜': "🈳️",
|
'龜': 空,
|
||||||
'黽': "🈳️",
|
'黽': 空,
|
||||||
'卵': "🈳️",
|
'卵': 空,
|
||||||
'二': "🈳️",
|
'二': 空,
|
||||||
'土': "🈳️",
|
'土': 空,
|
||||||
'垚': "🈳️",
|
'垚': 空,
|
||||||
'堇': "🈳️",
|
'堇': 空,
|
||||||
'里': "🈳️",
|
'里': 空,
|
||||||
'畕': "🈳️",
|
'畕': 空,
|
||||||
'黃': "🈳️",
|
'黃': 空,
|
||||||
'男': "🈳️",
|
'男': 空,
|
||||||
'力': "🈳️",
|
'力': 空,
|
||||||
'劦': "🈳️",
|
'劦': 空,
|
||||||
'金': "💰",
|
'金': "💰",
|
||||||
'幵': "🈳️",
|
'幵': 空,
|
||||||
'勺': "🈳️",
|
'勺': 空,
|
||||||
'几': "🈳️",
|
'几': 空,
|
||||||
'且': "🈳️",
|
'且': 空,
|
||||||
'斤': "🈳️",
|
'斤': 空,
|
||||||
'斗': "🈳️",
|
'斗': 空,
|
||||||
'矛': "🈳️",
|
'矛': 空,
|
||||||
'車': "🈳️",
|
'車': 空,
|
||||||
'𠂤': "🈳️",
|
'𠂤': 空,
|
||||||
'𨸏': "🈳️",
|
'𨸏': 空,
|
||||||
'𨺅': "🈳️",
|
'𨺅': 空,
|
||||||
'厽': "🈳️",
|
'厽': 空,
|
||||||
'四': "🈳️",
|
'四': 空,
|
||||||
'宁': "🈳️",
|
'宁': 空,
|
||||||
'叕': "🈳️",
|
'叕': 空,
|
||||||
'亞': "🈳️",
|
'亞': 空,
|
||||||
'五': "🈳️",
|
'五': 空,
|
||||||
'六': "🈳️",
|
'六': 空,
|
||||||
'七': "🈳️",
|
'七': 空,
|
||||||
'九': "🈳️",
|
'九': 空,
|
||||||
'禸': "🈳️",
|
'禸': 空,
|
||||||
'嘼': "🈳️",
|
'嘼': 空,
|
||||||
'甲': "🈳️",
|
'甲': 空,
|
||||||
'乙': "🈳️",
|
'乙': 空,
|
||||||
'丙': "🈳️",
|
'丙': 空,
|
||||||
'丁': "🈳️",
|
'丁': 空,
|
||||||
'戊': "🈳️",
|
'戊': 空,
|
||||||
'己': "🈳️",
|
'己': 空,
|
||||||
'巴': "🈳️",
|
'巴': 空,
|
||||||
'庚': "🈳️",
|
'庚': 空,
|
||||||
'辛': "🈳️",
|
'辛': 空,
|
||||||
'辡': "🈳️",
|
'辡': 空,
|
||||||
'壬': "🈳️",
|
'壬': 空,
|
||||||
'癸': "🈳️",
|
'癸': 空,
|
||||||
'子': "🚼",
|
'子': "🚼",
|
||||||
'了': "🈳️",
|
'了': 空,
|
||||||
'孨': "🈳️",
|
'孨': 空,
|
||||||
'𠫓': "🈳️",
|
'𠫓': 空,
|
||||||
'丑': "🈳️",
|
'丑': 空,
|
||||||
'寅': "🈳️",
|
'寅': 空,
|
||||||
'戼': "🈳️",
|
'戼': 空,
|
||||||
'辰': "🈳️",
|
'辰': 空,
|
||||||
'巳': "🈳️",
|
'巳': 空,
|
||||||
'午': "🈳️",
|
'午': 空,
|
||||||
'未': "🈳️",
|
'未': 空,
|
||||||
'申': "🈳️",
|
'申': 空,
|
||||||
'酉': "🈳️",
|
'酉': 空,
|
||||||
'酋': "🈳️",
|
'酋': 空,
|
||||||
'戌': "🈳️",
|
'戌': 空,
|
||||||
'亥': "🈳️",
|
'亥': 空,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
package emozi
|
package emozi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
base14 "github.com/fumiama/go-base16384"
|
base14 "github.com/fumiama/go-base16384"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidEmoziString = errors.New("invalid EmoziString")
|
||||||
|
)
|
||||||
|
|
||||||
// EmoziString 一个颜文字汉字转写串, 包含串头 校验字节数*2 字节校验和
|
// EmoziString 一个颜文字汉字转写串, 包含串头 校验字节数*2 字节校验和
|
||||||
type EmoziString string
|
type EmoziString string
|
||||||
|
|
||||||
@@ -35,7 +40,7 @@ func WrapRawEmoziString(s string) EmoziString {
|
|||||||
// String 输出不包含串头的转写串
|
// String 输出不包含串头的转写串
|
||||||
func (es EmoziString) String() string {
|
func (es EmoziString) String() string {
|
||||||
if !es.IsValid() {
|
if !es.IsValid() {
|
||||||
return "ERROR: invalid EmoziString"
|
return ErrInvalidEmoziString.Error()
|
||||||
}
|
}
|
||||||
rs := []rune(es)
|
rs := []rune(es)
|
||||||
sb := strings.Builder{}
|
sb := strings.Builder{}
|
||||||
|
|||||||
Reference in New Issue
Block a user