mirror of
https://github.com/fumiama/emozi.git
synced 2026-06-05 00:32:48 +08:00
init
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -19,3 +19,9 @@
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
# MacOS
|
||||
.DS_Store
|
||||
|
||||
# json folder
|
||||
/data
|
||||
|
||||
21
README.md
Normal file
21
README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
<div align="center">
|
||||
|
||||
<h1>EMOZI</h1>
|
||||
参考古埃及圣书体设计的一种基于颜文字的汉字抽象转写法<br>🐑🚬🧗👤🕸️😐🧗✍️👈🌞😨🏠🌹🧦😨👥🌹🔐😨💦⬅️☀️😨🏡💦💡🍉🌱🍵💡🧗🪓🍆👔😨🐶<br><br>
|
||||
|
||||
<img src="https://counter.seku.su/cmoe?name=emozi&theme=r34" /><br>
|
||||
|
||||
</div>
|
||||
|
||||
W.I.P.
|
||||
|
||||
## 实用工具
|
||||
### 拼音识别拆分
|
||||
将带声调的拼音拆分为以国际音标表示的声母韵母。
|
||||
```go
|
||||
s, y, t, err := emozi.SplitPinyin("jiǒng")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(s, y, tone) // tɕ i̯ʊŋ 上声
|
||||
```
|
||||
54
codegen/radical/main.go
Normal file
54
codegen/radical/main.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
sql "github.com/FloatTech/sqlite"
|
||||
"github.com/fumiama/emozi"
|
||||
)
|
||||
|
||||
const head = `// Code generated by codegen/radical. 已经经过修改, 不要手动重新运行.
|
||||
|
||||
package emozi
|
||||
|
||||
// 部首后备 内嵌的部首到颜文字的映射, 是第二优先查询顺位. 第一顺位是数据库的部首表. 第三位是回落到 🈳️.
|
||||
var 部首后备 = map[rune]string{ {{range .}}
|
||||
'{{.R}}': "🈳️",{{end}}
|
||||
}
|
||||
`
|
||||
|
||||
func main() {
|
||||
db := sql.Sqlite{DBPath: emozi.EmoziDatabasePath}
|
||||
err := db.Open(time.Minute)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer db.Close()
|
||||
type r struct {
|
||||
R rune
|
||||
}
|
||||
type sr struct {
|
||||
R string
|
||||
}
|
||||
var s r
|
||||
var ss = []sr{}
|
||||
db.QueryFor("SELECT DISTINCT R FROM emozi;", &s, func() error {
|
||||
ss = append(ss, sr{R: string(s.R)})
|
||||
return nil
|
||||
})
|
||||
t, err := template.New("list").Parse(head)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
f, err := os.Create("radical.go")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer f.Close()
|
||||
err = t.Execute(f, ss)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
178
coder.go
Normal file
178
coder.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package emozi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
sql "github.com/FloatTech/sqlite"
|
||||
)
|
||||
|
||||
// Coder encoder/decoder
|
||||
type Coder struct {
|
||||
db sql.Sqlite
|
||||
isRandom bool
|
||||
}
|
||||
|
||||
// NewCoder israndom 随机挑选声母韵母的颜文字, 否则固定使用第一个
|
||||
func NewCoder(israndom bool, cachettl time.Duration) (c Coder, err error) {
|
||||
c.db.DBPath = EmoziDatabasePath
|
||||
c.isRandom = israndom
|
||||
err = c.db.Open(cachettl)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = c.db.Create(主字表名, &字表{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = c.db.Create(附字表名, &字表{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = c.db.Create(部首表名, &部首表{})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = c.db.Query("CREATE INDEX IF NOT EXISTS IE ON "+部首表名+" (E);", nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Close ...
|
||||
func (c *Coder) Close() error {
|
||||
return c.db.Close()
|
||||
}
|
||||
|
||||
// Encode 从汉字序列生成 EmoziString
|
||||
func (c *Coder) Encode(s string, selections ...int) (EmoziString, error) {
|
||||
sb := strings.Builder{}
|
||||
x := &字表{}
|
||||
lst := []字表{}
|
||||
write := func(x *字表) {
|
||||
sb.WriteString(c.查声母(x.S))
|
||||
sb.WriteString(c.查韵母(x.Y))
|
||||
sb.WriteString(c.查声调(x.T))
|
||||
sb.WriteString(c.查部首(x.R))
|
||||
}
|
||||
多音字计数 := 0
|
||||
for _, ch := range s { // nolint: go-staticcheck
|
||||
lst = lst[:0]
|
||||
err := c.db.FindFor(附字表名, x, "WHERE W="+strconv.Itoa(int(ch)), func() error {
|
||||
lst = append(lst, *x)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
lst = lst[:0]
|
||||
err = c.db.FindFor(主字表名, x, "WHERE W="+strconv.Itoa(int(ch)), func() error {
|
||||
lst = append(lst, *x)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
if err != nil || len(lst) == 0 {
|
||||
sb.WriteRune(ch)
|
||||
continue
|
||||
}
|
||||
if len(lst) == 1 {
|
||||
write(x)
|
||||
continue
|
||||
}
|
||||
if len(selections) > 多音字计数 {
|
||||
idx := selections[多音字计数]
|
||||
多音字计数++
|
||||
if idx >= 0 && idx < len(lst) {
|
||||
write(&lst[idx])
|
||||
continue
|
||||
}
|
||||
}
|
||||
// 多音字
|
||||
sb.WriteString("[")
|
||||
write(&lst[0])
|
||||
for _, x := range lst[1:] {
|
||||
sb.WriteString("|")
|
||||
write(&x)
|
||||
}
|
||||
sb.WriteString("]")
|
||||
}
|
||||
return WrapRawEmoziString(sb.String()), nil
|
||||
}
|
||||
|
||||
// Add 向主库添加一个新字
|
||||
//
|
||||
// w: 字, r: 部首, p: 不带声调的拼音(可空), f: 带声调的拼音
|
||||
func (c *Coder) Add(w, r, p, f string) error {
|
||||
if p == "" {
|
||||
p = 去调(f)
|
||||
}
|
||||
s, y, t, rw, rr, err := 拆音识字(w, r, p, f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.db.InsertUnique(主字表名, &字表{
|
||||
ID: 颜表ID(rw, s, y, t),
|
||||
W: rw, S: s, Y: y, T: t,
|
||||
R: rr, P: p, F: f,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.New("已有同音同形的字 '" + w + "'")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Overlay 向附加库添加一个新字, 覆盖在主库之上
|
||||
//
|
||||
// w: 字, r: 部首, p: 不带声调的拼音(可空), f: 带声调的拼音
|
||||
func (c *Coder) Overlay(w, r, p, f string) error {
|
||||
if p == "" {
|
||||
p = 去调(f)
|
||||
}
|
||||
s, y, t, rw, rr, err := 拆音识字(w, r, p, f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.overlay(w, p, f, s, y, t, rw, rr)
|
||||
}
|
||||
|
||||
func (c *Coder) overlay(w, p, f string, s 声母枚举, y 韵母枚举, t 声调枚举, rw rune, rr rune) error {
|
||||
err := c.db.InsertUnique(附字表名, &字表{
|
||||
ID: 颜表ID(rw, s, y, t),
|
||||
W: rw, S: s, Y: y, T: t,
|
||||
R: rr, P: p, F: f,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.New("已有同音同形的字 '" + w + "'")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChangeOverlay 更改附加库的一项
|
||||
func (c *Coder) ChangeOverlay(oldw, oldr, oldf, neww, newr, newf string) error {
|
||||
s, y, t, rw, rr, err := 拆音识字(oldw, oldr, 去调(oldf), oldf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newp := 去调(newf)
|
||||
ns, ny, nt, nrw, nrr, err := 拆音识字(neww, newr, newp, newf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
q := "WHERE ID=" + strconv.FormatInt(颜表ID(rw, s, y, t), 10)
|
||||
x := 字表{}
|
||||
err = c.db.Find(附字表名, &x, q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if x.R != rr {
|
||||
return errors.New("提供的旧部首 '" + string(rr) + "' 与记载的 '" + string(x.R) + "' 不符")
|
||||
}
|
||||
err = c.db.Del(附字表名, q)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.overlay(neww, newp, newf, ns, ny, nt, nrw, nrr)
|
||||
}
|
||||
|
||||
// OverlayRadical 添加一个部首
|
||||
func (c *Coder) OverlayRadical(r rune, e string) error {
|
||||
return c.db.InsertUnique(部首表名, &部首表{R: r, E: e})
|
||||
}
|
||||
29
coder_test.go
Normal file
29
coder_test.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package emozi
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
c, err := NewCoder(false, time.Minute)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
es, err := c.Encode("你好,世界!看看多音字:行。")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(es.String())
|
||||
if es.String() != "🥛👔🐴👤🌹🐱🐴👩,💦🌞😨🌍➕✌😨👨🌾!😭🔐🍉👁️😭🔐🍉👁️🔪🌀🍉🪩🐑🎵🍉🎵👈🌞😨🚼:[👇🦅🧗⛕|🌹👍🧗⛕]。" {
|
||||
t.Fatal("got", es.String())
|
||||
}
|
||||
es, err = c.Encode("你好,世界!指定多音字:银行行。", 1, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(es.String())
|
||||
if es.String() != "🥛👔🐴👤🌹🐱🐴👩,💦🌞😨🌍➕✌😨👨🌾!🐽🌞🐴✋🔪🦅😨🏠🔪🌀🍉🪩🐑🎵🍉🎵👈🌞😨🚼:🐑🎵🧗💰🌹👍🧗⛕👇🦅🧗⛕。" {
|
||||
t.Fatal("got", es.String())
|
||||
}
|
||||
}
|
||||
38
data.go
Normal file
38
data.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package emozi
|
||||
|
||||
import _ "embed"
|
||||
|
||||
// 字数据库 数据来自 https://github.com/shuowenjiezi/shuowen
|
||||
//
|
||||
//var 字数据库 []byte
|
||||
|
||||
// DatabasePath 字数据库的路径 如找不到会向对应路径写入内嵌的字数据库
|
||||
var EmoziDatabasePath = "字.db"
|
||||
|
||||
const (
|
||||
主字表名 = "emozi"
|
||||
附字表名 = "altzi"
|
||||
部首表名 = "radcl"
|
||||
)
|
||||
|
||||
// 字表 emozi表 定义
|
||||
type 字表 struct {
|
||||
ID int64 // ID 高 32 位 W 的 rune, 低 32 位 保留8 S8 Y8 T8
|
||||
W rune
|
||||
S 声母枚举
|
||||
Y 韵母枚举
|
||||
T 声调枚举
|
||||
R rune
|
||||
P string
|
||||
F string
|
||||
}
|
||||
|
||||
func 颜表ID(w rune, s 声母枚举, y 韵母枚举, t 声调枚举) int64 {
|
||||
return int64((uint64(w) << 32) | (uint64(s) << 16) | (uint64(y) << 8) | (uint64(t)))
|
||||
}
|
||||
|
||||
// 从表 从部首表
|
||||
type 部首表 struct {
|
||||
R rune // R 该部首
|
||||
E string `db:"E,UNIQUE"` // E 该部首对应的颜文字
|
||||
}
|
||||
210
define.go
Normal file
210
define.go
Normal file
@@ -0,0 +1,210 @@
|
||||
package emozi
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
base14 "github.com/fumiama/go-base16384"
|
||||
)
|
||||
|
||||
type 声母枚举 uint8
|
||||
|
||||
// String 国际音标
|
||||
func (sm 声母枚举) String() string {
|
||||
a := sm * 3
|
||||
b := a + 3
|
||||
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))
|
||||
}
|
||||
|
||||
const (
|
||||
声母b 声母枚举 = iota
|
||||
声母p
|
||||
声母m
|
||||
声母f
|
||||
声母d
|
||||
声母t
|
||||
声母n
|
||||
声母l
|
||||
声母g
|
||||
声母k
|
||||
声母h
|
||||
声母j
|
||||
声母q
|
||||
声母x
|
||||
声母zh
|
||||
声母ch
|
||||
声母sh
|
||||
声母r
|
||||
声母z
|
||||
声母c
|
||||
声母s
|
||||
声母yi
|
||||
声母w
|
||||
声母yu
|
||||
声母0
|
||||
)
|
||||
|
||||
// 声母 可以有多个颜文字与之对应 但有时系统可能无法显示
|
||||
var 声母 = [...][]string{
|
||||
// b: 鼻笔八
|
||||
{"👃", "🖊️", "🎱"},
|
||||
// p: 跑跑跑票票牌牌
|
||||
{"🏃", "🏃♀️", "🏃♂️", "🎫", "🎟️", "🃏", "🎴"},
|
||||
// m: 马马猫猫猫猫锚面面门麦米木蜜帽帽帽麦
|
||||
{"🐴", "🐎", "🐱", "🐈", "🐈⬛", "😺", "⚓️", "🍜", "🍝", "🚪", "🌾", "🍚", "🪵", "🍯", "🎩", "👒", "🧢", "🎙️"},
|
||||
// f: 斧肺
|
||||
{"🪓", "🫁"},
|
||||
// d: 刀豆蛋灯灯洞
|
||||
{"🔪", "🫘", "🥚", "💡", "🪔", "🕳️"},
|
||||
// t: 腿糖兔兔桶桃
|
||||
{"🦵", "🍬", "🐰", "🐇", "🪣", "🍑"},
|
||||
// n: 奶鸟牛
|
||||
{"🥛", "🐦", "🐮"},
|
||||
// l: 鹿鹿冷龙龙莲
|
||||
{"🦌", "🫎", "🥶", "🐲", "🐉", "🪷"},
|
||||
// g: 瓜鼓龟狗勾跪跪跪
|
||||
{"🍉", "🥁", "🐢", "🐶", "🪝", "🧎", "🧎♀️", "🧎♂️"},
|
||||
// k: 哭裤
|
||||
{"😭", "👖"},
|
||||
// h: 花花花花花花花花火虎虎猴猴
|
||||
{"🌹", "🌼", "💐", "🌷", "🌸", "🌺", "🥀", "🪻", "🔥", "🐯", "🐅", "🐒", "🐵"},
|
||||
// j: 加减酒酒鸡鸡剑姜镜脚
|
||||
{"➕", "➖", "🍷", "🍺", "🐔", "🐓", "🗡️", "🫚", "🪞", "🦶"},
|
||||
// q: 茄钱钱钱钱钱棋
|
||||
{"🍆", "💰", "💴", "💵", "💶", "💸", "♟️"},
|
||||
// x: 下下虾虾虾雪星星星星熊象蟹
|
||||
{"👇", "⬇️", "🦐", "🍤", "🦞", "❄️", "🌟", "✨", "⭐️", "💫", "🐻", "🐘", "🦀️"},
|
||||
// zh: 猪猪针针
|
||||
{"🐽", "🐷", "🪡", "💉"},
|
||||
// ch: 茶茶车车车车床床除虫
|
||||
{"🍵", "☕️", "🚗", "🚘", "🚙", "🛻", "🛏️", "🛌", "➗", "🐛"},
|
||||
// sh: 水水书书书书书书上上上石山山山树树树蛇鼠鼠鼠珊
|
||||
{"💦", "💧", "📖", "📕", "📗", "📘", "📙", "📚", "⬆️", "☝️", "👆", "🪨", "⛰️", "🏔️", "🗻", "🌲", "🌳", "🌴", "🐍", "🐭", "🐀", "🐁", "🪸"},
|
||||
// r: 肉日日日
|
||||
{"🥩", "🌞", "☀️", "🌅"},
|
||||
// z: 左左左 c: 错错错错 s: 锁伞伞伞伞蒜
|
||||
{"👈", "⬅️", "⬅"}, {"❌", "🙅", "🙅♀️", "🙅♂️"}, {"🔒", "🌂", "☔️", "⛱️", "☂️", "🧄"},
|
||||
// yi: 羊羊牙药盐 w: 袜雾网碗 yu: 雨鱼鱼 0: 两种椅子
|
||||
{"🐑", "🐐", "🦷", "💊", "🧂"}, {"🧦", "🌫️", "🕸️", "🥣"}, {"🐟", "🌧️", "🐠"}, {"🪑", "💺"},
|
||||
}
|
||||
|
||||
type 韵母枚举 uint8
|
||||
|
||||
// String 国际音标
|
||||
func (ym 韵母枚举) String() string {
|
||||
return [...]string{
|
||||
"ä", "ɔ", "ɤ", "i", "ɿ", "u", "y", "ɐɚ̯", "aɪ̯", "u̯aɪ̯", "eɪ̯", "u̯eɪ̯", "ɑʊ̯", "i̯ɑʊ̯",
|
||||
"oʊ̯", "i̯oʊ̯", "i̯ä", "u̯ä", "u̯o", "i̯ɛ", "y̯ɛ", "an", "i̯ɛn", "u̯an", "y̯ɛn", "ən", "u̯ən",
|
||||
"in", "yn", "ɑŋ", "i̯ɑŋ", "u̯ɑŋ", "ɤŋ", "iŋ", "u̯əŋ", "ʊŋ", "i̯ʊŋ",
|
||||
}[ym]
|
||||
}
|
||||
|
||||
const (
|
||||
韵母a 韵母枚举 = iota
|
||||
韵母o
|
||||
韵母e
|
||||
韵母yi
|
||||
韵母ri
|
||||
韵母wu
|
||||
韵母yu
|
||||
韵母er
|
||||
韵母ai
|
||||
韵母uai
|
||||
韵母ei
|
||||
韵母ui
|
||||
韵母ao
|
||||
韵母iao
|
||||
韵母ou
|
||||
韵母iu
|
||||
韵母ia
|
||||
韵母ua
|
||||
韵母uo
|
||||
韵母ie
|
||||
韵母yue
|
||||
韵母an
|
||||
韵母ian
|
||||
韵母uan
|
||||
韵母yuan
|
||||
韵母en
|
||||
韵母un
|
||||
韵母in
|
||||
韵母yun
|
||||
韵母ang
|
||||
韵母iang
|
||||
韵母uang
|
||||
韵母eng
|
||||
韵母ing
|
||||
韵母ueng
|
||||
韵母ong
|
||||
韵母iong
|
||||
)
|
||||
|
||||
// 韵母 仅由一种意象表示
|
||||
var 韵母 = [...][]string{
|
||||
{"😧", "🤔️"}, {"😲"}, {"😋"}, {"👔", "👗", "👕", "👚"}, {"🌞", "☀️", "🌅"},
|
||||
{string([]rune("🌫️")[0]), "🌫️"}, {"🐟", "🌧️"}, {"👂"},
|
||||
{string([]rune("❤️")[0]), "❤️", "💗", "💓", "🫶", "💕", "💖", "💘", "💙", "💚", "💛", "💜", "💝", "💞", "🧡"}, {"🥢"}, {"😩"}, {"🐢"},
|
||||
{"🐱", "🐈", "🐈⬛", "😺"}, {"💊"}, {"🤮", "🤢"}, {"👉", "➡️"},
|
||||
{"🦷"}, {"🧦"}, {"🌀"}, {string([]rune("✌️")[0]), "✌️"}, {"🌙", "🌕", "🌛", "🌝"}, {"🔐"}, {"🚬"}, {"🥣", "🍚"},
|
||||
{string([]rune("⭕️")[0]), "⭕️"}, {"😐"}, {"😘", "💋"}, {"🎵"}, {string([]rune("☁️")[0]), "☁️"}, {"👍"}, {"🐑"},
|
||||
{string([]rune("🕸️")[0]), "🕸️"}, {"💡", "🪔"}, {"🦅"}, {"🐝"}, {"🌈"}, {"😳"}, // jiong 囧
|
||||
}
|
||||
|
||||
type 声调枚举 uint8
|
||||
|
||||
// String 返回传统调名
|
||||
func (t 声调枚举) String() string {
|
||||
return [...]string{
|
||||
"阴平", "阳平", "上声", "去声", "轻声",
|
||||
}[t]
|
||||
}
|
||||
|
||||
const (
|
||||
阴平 声调枚举 = iota
|
||||
阳平
|
||||
上声
|
||||
去声
|
||||
轻声
|
||||
)
|
||||
|
||||
var 声调 = [...][]string{{"🍉"}, {"🧗", "🧗♀️", "🧗♂️", "🦎"}, {"🐴", "🐎"}, {"😨"}, {"😯"}}
|
||||
|
||||
// 校验表 用 校验表长度 个unicode控制字符做校验和验证此序列是由本程序生成而非手写的
|
||||
//
|
||||
// 具体做法是先对后面的文本做crc32然后取 校验表长度^校验字节数 的模
|
||||
var 校验表 = func() []rune {
|
||||
x, err := base14.UTF16BE2UTF8(base14.StringToBytes("\x20\x0b\x20\x0c\x20\x0d\x20\x0e\x20\x0f\x20\x2a\x20\x2b\x20\x2c\x20\x2d\x20\x2e\x20\x60\x20\x61\x20\x62\x20\x63\x20\x64\x20\x65\x20\x66\x20\x68\x20\x69\x20\x6a\x20\x6b\x20\x6c\x20\x6d\x20\x6e\x20\x6f"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return []rune(base14.BytesToString(x))
|
||||
}()
|
||||
|
||||
// 校验表长度 25
|
||||
var 校验表长度 = len(校验表)
|
||||
|
||||
// 校验字节数 默认为 3, 不得大于 3
|
||||
const 校验字节数 = 3
|
||||
|
||||
// 校验模 校验表长度^校验字节数
|
||||
var 校验模 = uint32(0)
|
||||
|
||||
// 校验倍数 校验表长度乘方表
|
||||
var 校验倍数 = func() []uint32 {
|
||||
tab := make([]uint32, 校验字节数)
|
||||
tab[0] = 1
|
||||
for i := 1; i < 校验字节数; i++ {
|
||||
tab[i] = tab[i-1] * uint32(校验表长度)
|
||||
}
|
||||
校验模 = tab[校验字节数-1] * uint32(校验表长度)
|
||||
return tab
|
||||
}()
|
||||
|
||||
// 逆校验表 还原校验和用
|
||||
var 逆校验表 = func() map[rune]uint8 {
|
||||
m := make(map[rune]uint8, 64)
|
||||
for i, c := range 校验表 {
|
||||
m[c] = uint8(i)
|
||||
}
|
||||
return m
|
||||
}()
|
||||
22
define_test.go
Normal file
22
define_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package emozi
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestFirstEmojiSingle(t *testing.T) {
|
||||
for i, lst := range 声母 {
|
||||
if len([]rune(lst[0])) != 1 {
|
||||
t.Fatal("声母", i, "长度", len([]rune(lst[0])), "字", lst[0])
|
||||
}
|
||||
}
|
||||
t.Log(string([]rune("🌫️")[0]), string([]rune("❤️")[0]), string([]rune("✌️")[0]), string([]rune("⭕️")[0]), string([]rune("☁️")[0]), string([]rune("🕸️")[0]))
|
||||
for i, lst := range 韵母 {
|
||||
if len([]rune(lst[0])) != 1 {
|
||||
t.Fatal("韵母", i, "长度", len([]rune(lst[0])), "字", lst[0])
|
||||
}
|
||||
}
|
||||
for i, lst := range 声调 {
|
||||
if len([]rune(lst[0])) != 1 {
|
||||
t.Fatal("声调", i, "长度", len([]rune(lst[0])), "字", lst[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
29
go.mod
Normal file
29
go.mod
Normal file
@@ -0,0 +1,29 @@
|
||||
module github.com/fumiama/emozi
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/FloatTech/sqlite v1.6.3
|
||||
github.com/fumiama/go-base16384 v1.7.0
|
||||
github.com/liuzl/gocc v0.0.0-20231231122217-0372e1059ca5
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b // indirect
|
||||
github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/liuzl/cedar-go v0.0.0-20170805034717-80a9c64b256d // indirect
|
||||
github.com/liuzl/da v0.0.0-20180704015230-14771aad5b1d // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
modernc.org/libc v1.21.5 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.4.0 // indirect
|
||||
modernc.org/sqlite v1.20.0 // indirect
|
||||
)
|
||||
|
||||
replace modernc.org/sqlite => github.com/fumiama/sqlite3 v1.20.0-with-win386
|
||||
|
||||
replace github.com/remyoudompheng/bigfft => github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b
|
||||
43
go.sum
Normal file
43
go.sum
Normal file
@@ -0,0 +1,43 @@
|
||||
github.com/FloatTech/sqlite v1.6.3 h1:MQkqBNlkPuCoKQQgoNLuTL/2Ci3tBTFAnVYBdD0Wy4M=
|
||||
github.com/FloatTech/sqlite v1.6.3/go.mod h1:zFbHzRfB+CJ+VidfjuVbrcin3DAz283F7hF1hIeHzpY=
|
||||
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b h1:tvciXWq2nuvTbFeJGLDNIdRX3BI546D3O7k7vrVueZw=
|
||||
github.com/FloatTech/ttl v0.0.0-20220715042055-15612be72f5b/go.mod h1:fHZFWGquNXuHttu9dUYoKuNbm3dzLETnIOnm1muSfDs=
|
||||
github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d h1:ir/IFJU5xbja5UaBEQLjcvn7aAU01nqU/NUyOBEU+ew=
|
||||
github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d/go.mod h1:PRWNwWq0yifz6XDPZu48aSld8BWwBfr2JKB2bGWiEd4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b h1:Zt3pFQditAdWTHCOVkiloc9ZauBoWrb37guFV4iIRvE=
|
||||
github.com/fumiama/bigfft v0.0.0-20211011143303-6e0bfa3c836b/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/fumiama/go-base16384 v1.7.0 h1:6fep7XPQWxRlh4Hu+KsdH+6+YdUp+w6CwRXtMWSsXCA=
|
||||
github.com/fumiama/go-base16384 v1.7.0/go.mod h1:OEn+947GV5gsbTAnyuUW/SrfxJYUdYupSIQXOuGOcXM=
|
||||
github.com/fumiama/sqlite3 v1.20.0-with-win386 h1:ZR1AXGBEtkfq9GAXehOVcwn+aaCG8itrkgEsz4ggx5k=
|
||||
github.com/fumiama/sqlite3 v1.20.0-with-win386/go.mod h1:Os58MHwYCcYZCy2PGChBrQtBAw5/LS1ZZOkfc+C/I7s=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/liuzl/cedar-go v0.0.0-20170805034717-80a9c64b256d h1:qSmEGTgjkESUX5kPMSGJ4pcBUtYVDdkNzMrjQyvRvp0=
|
||||
github.com/liuzl/cedar-go v0.0.0-20170805034717-80a9c64b256d/go.mod h1:x7SghIWwLVcJObXbjK7S2ENsT1cAcdJcPl7dRaSFog0=
|
||||
github.com/liuzl/da v0.0.0-20180704015230-14771aad5b1d h1:hTRDIpJ1FjS9ULJuEzu69n3qTgc18eI+ztw/pJv47hs=
|
||||
github.com/liuzl/da v0.0.0-20180704015230-14771aad5b1d/go.mod h1:7xD3p0XnHvJFQ3t/stEJd877CSIMkH/fACVWen5pYnc=
|
||||
github.com/liuzl/gocc v0.0.0-20231231122217-0372e1059ca5 h1:wnbHIeP1UX8ClYEWKGnw66PfYvReCHu9G5lXSte3Sqc=
|
||||
github.com/liuzl/gocc v0.0.0-20231231122217-0372e1059ca5/go.mod h1:7KaV9YIR92M1FpbczAcfYQ3UZ5ayT27pNtunDmXvLBo=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
modernc.org/libc v1.21.5 h1:xBkU9fnHV+hvZuPSRszN0AXDG4M7nwPLwTWwkYcvLCI=
|
||||
modernc.org/libc v1.21.5/go.mod h1:przBsL5RDOZajTVslkugzLBj1evTue36jEomFQOoYuI=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.4.0 h1:crykUfNSnMAXaOJnnxcSzbUGMqkLWjklJKkBK2nwZwk=
|
||||
modernc.org/memory v1.4.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
53
lookup.go
Normal file
53
lookup.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package emozi
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var 空 = "🈳️"
|
||||
|
||||
func (c *Coder) 查声母(s 声母枚举) string {
|
||||
lst := 声母[s]
|
||||
if len(lst) == 0 {
|
||||
return 空
|
||||
}
|
||||
if len(lst) == 1 || !c.isRandom {
|
||||
return lst[0]
|
||||
}
|
||||
return lst[rand.Intn(len(lst))]
|
||||
}
|
||||
|
||||
func (c *Coder) 查韵母(y 韵母枚举) string {
|
||||
lst := 韵母[y]
|
||||
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 {
|
||||
lst := 声调[t]
|
||||
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 {
|
||||
x := &部首表{}
|
||||
err := c.db.Find(部首表名, x, "WHERE R="+strconv.Itoa(int(r)))
|
||||
if err == nil && len(x.E) > 0 && x.E != 空 {
|
||||
return x.E
|
||||
}
|
||||
if e, ok := 部首后备[r]; ok {
|
||||
return e
|
||||
}
|
||||
return 空
|
||||
}
|
||||
336
pinyin.go
Normal file
336
pinyin.go
Normal file
@@ -0,0 +1,336 @@
|
||||
package emozi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var smap = map[string]声母枚举{
|
||||
"b": 声母b,
|
||||
"p": 声母p,
|
||||
"m": 声母m,
|
||||
"f": 声母f,
|
||||
"d": 声母d,
|
||||
"t": 声母t,
|
||||
"n": 声母n,
|
||||
"l": 声母l,
|
||||
"g": 声母g,
|
||||
"k": 声母k,
|
||||
"h": 声母h,
|
||||
"j": 声母j,
|
||||
"q": 声母q,
|
||||
"x": 声母x,
|
||||
"r": 声母r,
|
||||
"w": 声母w,
|
||||
}
|
||||
|
||||
const 双字声母 = "zcs"
|
||||
|
||||
var amap = map[string]韵母枚举{
|
||||
"a": 韵母a,
|
||||
"ai": 韵母ai,
|
||||
"ao": 韵母ao,
|
||||
"an": 韵母an,
|
||||
"ang": 韵母ang,
|
||||
}
|
||||
|
||||
var wamap = map[string]韵母枚举{
|
||||
"a": 韵母ua,
|
||||
"ai": 韵母uai,
|
||||
"an": 韵母uan,
|
||||
"ang": 韵母uang,
|
||||
}
|
||||
|
||||
var omap = map[string]韵母枚举{
|
||||
"o": 韵母o,
|
||||
"ou": 韵母ou,
|
||||
"ong": 韵母ong,
|
||||
}
|
||||
|
||||
var emap = map[string]韵母枚举{
|
||||
"e": 韵母e,
|
||||
"er": 韵母er,
|
||||
"ei": 韵母ei,
|
||||
"en": 韵母en,
|
||||
"eng": 韵母eng,
|
||||
}
|
||||
|
||||
var wemap = map[string]韵母枚举{
|
||||
"ei": 韵母ei,
|
||||
"en": 韵母en,
|
||||
"eng": 韵母ueng,
|
||||
}
|
||||
|
||||
var imap = map[string]韵母枚举{
|
||||
"i": 韵母yi,
|
||||
"iao": 韵母iao,
|
||||
"iu": 韵母iu,
|
||||
"ia": 韵母ia,
|
||||
"ie": 韵母ie,
|
||||
"ian": 韵母ian,
|
||||
"in": 韵母in,
|
||||
"iang": 韵母iang,
|
||||
"ing": 韵母ing,
|
||||
"iong": 韵母iong,
|
||||
}
|
||||
|
||||
var umap = map[string]韵母枚举{
|
||||
"u": 韵母wu,
|
||||
"uai": 韵母uai,
|
||||
"ui": 韵母ui,
|
||||
"ua": 韵母ua,
|
||||
"uo": 韵母uo,
|
||||
"uan": 韵母uan,
|
||||
"un": 韵母un,
|
||||
"uang": 韵母uang,
|
||||
"ueng": 韵母ueng,
|
||||
}
|
||||
|
||||
var yumap = map[string]韵母枚举{
|
||||
"u": 韵母yu,
|
||||
"ue": 韵母yue,
|
||||
"uan": 韵母yuan,
|
||||
"un": 韵母yun,
|
||||
"ü": 韵母yu,
|
||||
"üe": 韵母yue,
|
||||
"üan": 韵母yuan,
|
||||
"ün": 韵母yun,
|
||||
"v": 韵母yu,
|
||||
"ve": 韵母yue,
|
||||
"van": 韵母yuan,
|
||||
"vn": 韵母yun,
|
||||
}
|
||||
|
||||
func combine(maps ...map[string]韵母枚举) map[string]韵母枚举 {
|
||||
newmap := make(map[string]韵母枚举, 128)
|
||||
for _, m := range maps {
|
||||
for k, v := range m {
|
||||
newmap[k] = v
|
||||
}
|
||||
}
|
||||
return newmap
|
||||
}
|
||||
|
||||
var aoeiu = combine(amap, omap, emap, imap, umap)
|
||||
|
||||
var aoeu = combine(amap, omap, emap, umap)
|
||||
|
||||
const (
|
||||
阴平字母 = "āōēīūǖ"
|
||||
阳平字母 = "áóéíúǘń"
|
||||
上声字母 = "ǎǒěǐǔǚň"
|
||||
去声字母 = "àòèìùǜ"
|
||||
G = 'ɡ'
|
||||
A = 'ɑ'
|
||||
)
|
||||
|
||||
var notonemap = map[rune]string{
|
||||
'ā': "a", 'á': "a", 'ǎ': "a", 'à': "a",
|
||||
'ō': "o", 'ó': "o", 'ǒ': "o", 'ò': "o",
|
||||
'ē': "e", 'é': "e", 'ě': "e", 'è': "e",
|
||||
'ī': "i", 'í': "i", 'ǐ': "i", 'ì': "i",
|
||||
'ū': "u", 'ú': "u", 'ǔ': "u", 'ù': "u",
|
||||
'ǖ': "ü", 'ǘ': "ü", 'ǚ': "ü", 'ǜ': "ü",
|
||||
G: "g", A: "a", 'ń': "n", 'ň': "n",
|
||||
}
|
||||
|
||||
func 去调(pf string) string {
|
||||
sb := strings.Builder{}
|
||||
for _, c := range pf {
|
||||
if nc, ok := notonemap[c]; ok {
|
||||
sb.WriteString(nc)
|
||||
} else {
|
||||
sb.WriteRune(c)
|
||||
}
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// 识调 从拼音获得声调
|
||||
func 识调(pf string) 声调枚举 {
|
||||
switch {
|
||||
case strings.ContainsAny(pf, 阴平字母):
|
||||
return 阴平
|
||||
case strings.ContainsAny(pf, 阳平字母):
|
||||
return 阳平
|
||||
case strings.ContainsAny(pf, 上声字母):
|
||||
return 上声
|
||||
case strings.ContainsAny(pf, 去声字母):
|
||||
return 去声
|
||||
}
|
||||
return 轻声
|
||||
}
|
||||
|
||||
// 拆音 拆分拼音为声母韵母
|
||||
func 拆音(p string) (sm 声母枚举, ym 韵母枚举, err error) {
|
||||
if len(p) == 1 {
|
||||
sm = 声母0
|
||||
// 韵母ê, 因为文字稀少, 并入 ie
|
||||
if p == "ê" {
|
||||
ym = 韵母ie
|
||||
return
|
||||
}
|
||||
if y, ok := aoeiu[p]; ok {
|
||||
ym = y
|
||||
return
|
||||
}
|
||||
err = errors.New("无法识别零声母拼音" + p)
|
||||
return
|
||||
}
|
||||
if s, ok := smap[p[:1]]; ok {
|
||||
sm = s
|
||||
ok = false
|
||||
switch p[1:2] {
|
||||
case "a":
|
||||
if p[:1] == "w" {
|
||||
ym, ok = wamap[p[1:]]
|
||||
} else {
|
||||
ym, ok = amap[p[1:]]
|
||||
}
|
||||
case "o":
|
||||
if p[:1] == "w" {
|
||||
ym = 韵母uo
|
||||
ok = true
|
||||
} else {
|
||||
ym, ok = omap[p[1:]]
|
||||
}
|
||||
case "e":
|
||||
if p[:1] == "w" {
|
||||
ym, ok = wemap[p[1:]]
|
||||
} else {
|
||||
ym, ok = emap[p[1:]]
|
||||
}
|
||||
case "i":
|
||||
if p[:1] == "r" {
|
||||
ym = 韵母ri
|
||||
ok = true
|
||||
} else {
|
||||
ym, ok = imap[p[1:]]
|
||||
}
|
||||
case "u":
|
||||
if strings.Contains("jqx", p[:1]) {
|
||||
ym, ok = yumap[p[1:]]
|
||||
} else {
|
||||
ym, ok = umap[p[1:]]
|
||||
if !ok {
|
||||
ym, ok = yumap[p[1:]]
|
||||
}
|
||||
}
|
||||
case "v", "ü"[:1]:
|
||||
ym, ok = yumap[p[1:]]
|
||||
}
|
||||
if !ok {
|
||||
err = errors.New("无法识别拼音" + p + "的韵母部分" + p[1:])
|
||||
}
|
||||
return
|
||||
}
|
||||
ok := false
|
||||
if strings.Contains(双字声母, p[:1]) {
|
||||
if p[1:2] == "h" { // zh ch sh
|
||||
switch p[:1] {
|
||||
case "z":
|
||||
sm = 声母zh
|
||||
case "c":
|
||||
sm = 声母ch
|
||||
case "s":
|
||||
sm = 声母sh
|
||||
}
|
||||
ym, ok = aoeu[p[2:]]
|
||||
if !ok {
|
||||
if p[2:] == "i" {
|
||||
ym = 韵母ri
|
||||
} else {
|
||||
err = errors.New("无法识别拼音" + p)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
switch p[:1] {
|
||||
case "z":
|
||||
sm = 声母z
|
||||
case "c":
|
||||
sm = 声母c
|
||||
case "s":
|
||||
sm = 声母s
|
||||
}
|
||||
ym, ok = aoeu[p[1:]]
|
||||
if !ok {
|
||||
if p[1:] == "i" {
|
||||
ym = 韵母ri
|
||||
} else {
|
||||
err = errors.New("无法识别拼音" + p)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if p[:1] == "y" { // /j/ or /y/
|
||||
if strings.Contains("uvü"[:3], p[1:2]) { // /y/
|
||||
sm = 声母yu
|
||||
ym, ok = yumap[p[1:]]
|
||||
if !ok {
|
||||
err = errors.New("无法识别拼音" + p)
|
||||
}
|
||||
return
|
||||
}
|
||||
if p[1:] == "ong" { // yong
|
||||
sm = 声母yu
|
||||
ym = 韵母iong
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
sm = 声母yi
|
||||
ym, ok = aoeiu[p[1:]]
|
||||
if !ok {
|
||||
err = errors.New("无法识别拼音" + p)
|
||||
}
|
||||
return
|
||||
}
|
||||
sm = 声母0
|
||||
ym, ok = aoeiu[p]
|
||||
if !ok {
|
||||
err = errors.New("无法识别拼音" + p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func 拆音识字(w, r, p, f string) (s 声母枚举, y 韵母枚举, t 声调枚举, rw, rr rune, err error) {
|
||||
myp := 去调(f)
|
||||
s, y, err = 拆音(p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
mys, myy, err := 拆音(myp)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if mys != s || myy != y {
|
||||
err = errors.New("无声调拼音" + p + "与有声调拼音" + f + "不符")
|
||||
return
|
||||
}
|
||||
t = 识调(f)
|
||||
rws := []rune(w)
|
||||
if len(rws) != 1 {
|
||||
err = errors.New("无法正确识别文字" + w)
|
||||
return
|
||||
}
|
||||
rw = rws[0]
|
||||
rrs := []rune(r)
|
||||
if len(rrs) != 1 {
|
||||
err = errors.New("无法正确识别部首" + w)
|
||||
return
|
||||
}
|
||||
rr = rrs[0]
|
||||
return
|
||||
}
|
||||
|
||||
// SplitPinyin 拆分带声调拼音
|
||||
func SplitPinyin(pf string) (sm, ym, sd string, err error) {
|
||||
p := 去调(pf)
|
||||
s, y, err := 拆音(p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
t := 识调(pf)
|
||||
println(s, y, t)
|
||||
return s.String(), y.String(), t.String(), nil
|
||||
}
|
||||
46
pinyin_test.go
Normal file
46
pinyin_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package emozi
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestSplitPinyin(t *testing.T) {
|
||||
s, y, tone, err := SplitPinyin("yōng")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(s, y, tone)
|
||||
if s+y+tone != "ɥi̯ʊŋ阴平" {
|
||||
t.Fail()
|
||||
}
|
||||
s, y, tone, err = SplitPinyin("hóng")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(s, y, tone)
|
||||
if s+y+tone != "xʊŋ阳平" {
|
||||
t.Fail()
|
||||
}
|
||||
s, y, tone, err = SplitPinyin("yǜn")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(s, y, tone)
|
||||
if s+y+tone != "ɥyn去声" {
|
||||
t.Fail()
|
||||
}
|
||||
s, y, tone, err = SplitPinyin("jiǒng")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(s, y, tone)
|
||||
if s+y+tone != "tɕi̯ʊŋ上声" {
|
||||
t.Fail()
|
||||
}
|
||||
s, y, tone, err = SplitPinyin("e")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(s, y, tone)
|
||||
if s+y+tone != "0ɤ轻声" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
81
preprocess/main.go
Normal file
81
preprocess/main.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package main
|
||||
|
||||
// 数据来自 https://github.com/shuowenjiezi/shuowen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/liuzl/gocc"
|
||||
|
||||
"github.com/fumiama/emozi"
|
||||
)
|
||||
|
||||
type data struct {
|
||||
W string `json:"wordhead"`
|
||||
R string `json:"radical"`
|
||||
P string `json:"pinyin"`
|
||||
F string `json:"pinyin_full"`
|
||||
A []string `json:"pinyin_alternative"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
x := data{}
|
||||
t2s, err := gocc.New("t2s")
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("ERROR: creating gocc: %v", err))
|
||||
}
|
||||
_ = os.RemoveAll(emozi.EmoziDatabasePath)
|
||||
c, err := emozi.NewCoder(false, time.Minute)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("ERROR: creating emozi coder: %v", err))
|
||||
}
|
||||
for i := 1; i <= 9833; i++ {
|
||||
f, err := os.Open(fmt.Sprintf("./data/%d.json", i))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("ERROR: opening data/%d.json: %v", i, err))
|
||||
}
|
||||
x.A = x.A[:0]
|
||||
err = json.NewDecoder(f).Decode(&x)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("ERROR: decoding data/%d.json: %v", i, err))
|
||||
}
|
||||
_ = f.Close()
|
||||
x.P = strings.ReplaceAll(x.P, string(emozi.G), "g")
|
||||
if len(x.P) == 0 {
|
||||
panic(fmt.Sprintf("ERROR: decoding data/%d.json: p: %s, f: %s", i, x.P, x.F))
|
||||
}
|
||||
insert := func(w string) error {
|
||||
err = c.Add(w, x.R, x.P, x.F)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inserting table emozi of data/%d.json: %v", i, err)
|
||||
}
|
||||
for _, a := range x.A {
|
||||
err = c.Add(w, x.R, "", a)
|
||||
if err != nil {
|
||||
return fmt.Errorf("inserting table emozi of data/%d.json, alter %s: %v", i, a, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
fmt.Print("\r[", i, "/9833] Insert char: ", x.W, " ")
|
||||
err = insert(x.W)
|
||||
if err != nil {
|
||||
fmt.Println("\n\t\tWARN:", err)
|
||||
}
|
||||
sc, err := t2s.Convert(x.W)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if sc != x.W {
|
||||
fmt.Print("\r[", i, "/9833] insert simplified char: ", sc, " ")
|
||||
err = insert(sc)
|
||||
if err != nil {
|
||||
fmt.Println("\n\t\tWARN:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
547
radical.go
Normal file
547
radical.go
Normal file
@@ -0,0 +1,547 @@
|
||||
// Code generated by codegen/radical. 已经经过修改, 不要手动重新运行.
|
||||
|
||||
package emozi
|
||||
|
||||
// 部首后备 内嵌的部首到颜文字的映射, 是第二优先查询顺位. 第一顺位是数据库的部首表. 第三位是回落到 🈳️.
|
||||
var 部首后备 = map[rune]string{
|
||||
'一': "1⃣️",
|
||||
'丄': "☝️",
|
||||
'示': "😇",
|
||||
'三': "3⃣️",
|
||||
'王': "🫅",
|
||||
'玉': "📿️",
|
||||
'玨': "💎",
|
||||
'气': "🌬",
|
||||
'士': "💼",
|
||||
'丨': "🥖",
|
||||
'屮': "🌱",
|
||||
'艸': "🍀",
|
||||
'蓐': "🧣",
|
||||
'茻': "🌿",
|
||||
'小': "⏳",
|
||||
'八': "8⃣️",
|
||||
'釆': "🙌",
|
||||
'半': "🐂",
|
||||
'牛': "🐮",
|
||||
'犛': "🦬",
|
||||
'告': "🗣️",
|
||||
'口': "👄",
|
||||
'人': "👤",
|
||||
'凵': "💋",
|
||||
'吅': "😱",
|
||||
'哭': "😭",
|
||||
'走': "🏃",
|
||||
'止': "🦶",
|
||||
'癶': "🐾",
|
||||
'步': "👣",
|
||||
'此': "⛷️",
|
||||
'正': "🎯",
|
||||
'是': "⚖",
|
||||
'辵': "🚶",
|
||||
'彳': "🚦",
|
||||
'廴': "🚥",
|
||||
'㢟': "🈳️",
|
||||
'行': "⛕",
|
||||
'齒': "🈳️",
|
||||
'牙': "🈳️",
|
||||
'足': "🈳️",
|
||||
'疋': "🈳️",
|
||||
'品': "🈳️",
|
||||
'龠': "🈳️",
|
||||
'冊': "🈳️",
|
||||
'㗊': "🈳️",
|
||||
'舌': "👅",
|
||||
'干': "🈳️",
|
||||
'𧮫': "🈳️",
|
||||
'只': "🈳️",
|
||||
'㕯': "🈳️",
|
||||
'句': "🈳️",
|
||||
'丩': "🈳️",
|
||||
'古': "🏺",
|
||||
'十': "🔟",
|
||||
'卅': "🌍",
|
||||
'言': "💬",
|
||||
'田': "👨🌾",
|
||||
'誩': "🈳️",
|
||||
'音': "🎵",
|
||||
'䇂': "🈳️",
|
||||
'丵': "🈳️",
|
||||
'菐': "🈳️",
|
||||
'𠬞': "🈳️",
|
||||
'𠬜': "🈳️",
|
||||
'共': "🈳️",
|
||||
'異': "🈳️",
|
||||
'舁': "🈳️",
|
||||
'𦥑': "🈳️",
|
||||
'䢅': "🈳️",
|
||||
'爨': "🈳️",
|
||||
'革': "🈳️",
|
||||
'鬲': "🈳️",
|
||||
'䰜': "🈳️",
|
||||
'爪': "🤏",
|
||||
'丮': "🈳️",
|
||||
'鬥': "👊",
|
||||
'又': "🈳️",
|
||||
'𠂇': "🈳️",
|
||||
'史': "🈳️",
|
||||
'支': "🈳️",
|
||||
'𦘒': "🈳️",
|
||||
'聿': "✍",
|
||||
'畫': "🎨",
|
||||
'隶': "🈳️",
|
||||
'臤': "🈳️",
|
||||
'臣': "🈳️",
|
||||
'殳': "🈳️",
|
||||
'殺': "🪚",
|
||||
'𠘧': "🈳️",
|
||||
'寸': "🈳️",
|
||||
'皮': "🈳️",
|
||||
'㼱': "🈳️",
|
||||
'攴': "🈳️",
|
||||
'女': "👩",
|
||||
'教': "🈳️",
|
||||
'卜': "🔮",
|
||||
'用': "🈳️",
|
||||
'爻': "🈳️",
|
||||
'㸚': "🈳️",
|
||||
'𡕥': "🈳️",
|
||||
'目': "👁️",
|
||||
'䀠': "👀",
|
||||
'眉': "🤨",
|
||||
'盾': "🈳️",
|
||||
'自': "👃",
|
||||
'𪞶': "🈳️",
|
||||
'鼻': "👃",
|
||||
'皕': "🈳️",
|
||||
'習': "🈳️",
|
||||
'羽': "🪶",
|
||||
'隹': "🐦",
|
||||
'奞': "🈳️",
|
||||
'雈': "🈳️",
|
||||
'𦫳': "🈳️",
|
||||
'𥄕': "🈳️",
|
||||
'羊': "🐑",
|
||||
'羴': "🈳️",
|
||||
'瞿': "🈳️",
|
||||
'雔': "🈳️",
|
||||
'雥': "🈳️",
|
||||
'鳥': "🦢",
|
||||
'烏': "🐦⬛",
|
||||
'𠦒': "🈳",
|
||||
'冓': "🈳️",
|
||||
'幺': "🈳️",
|
||||
'𢆶': "🈳️",
|
||||
'叀': "🈳️",
|
||||
'玄': "🈳️",
|
||||
'予': "🈳️",
|
||||
'放': "🈳️",
|
||||
'𠬪': "🈳️",
|
||||
'𣦼': "🈳️",
|
||||
'歺': "🈳️",
|
||||
'死': "🈳️",
|
||||
'冎': "🈳️",
|
||||
'骨': "🈳️",
|
||||
'肉': "🈳️",
|
||||
'筋': "🈳️",
|
||||
'刀': "🔪",
|
||||
'刃': "🔪",
|
||||
'㓞': "🈳️",
|
||||
'丯': "🈳️",
|
||||
'耒': "🈳️",
|
||||
'角': "🥐",
|
||||
'竹': "🎋",
|
||||
'箕': "🈳️",
|
||||
'丌': "🈳️",
|
||||
'左': "👈",
|
||||
'工': "🈳️",
|
||||
'㠭': "🈳️",
|
||||
'巫': "🈳️",
|
||||
'甘': "🈳️",
|
||||
'曰': "🈳️",
|
||||
'乃': "🈳️",
|
||||
'丂': "🈳️",
|
||||
'可': "🈳️",
|
||||
'兮': "🈳️",
|
||||
'号': "🈳️",
|
||||
'亏': "🈳️",
|
||||
'旨': "🈳️",
|
||||
'喜': "🈳️",
|
||||
'壴': "🈳️",
|
||||
'鼓': "🈳️",
|
||||
'豈': "🈳️",
|
||||
'豆': "🈳️",
|
||||
'豊': "🈳️",
|
||||
'豐': "🈳️",
|
||||
'䖒': "🈳️",
|
||||
'虍': "🈳️",
|
||||
'虎': "🈳️",
|
||||
'虤': "🈳️",
|
||||
'皿': "🈳️",
|
||||
'𠙴': "🈳️",
|
||||
'去': "🈳️",
|
||||
'血': "🈳️",
|
||||
'丶': "🈳️",
|
||||
'丹': "🈳️",
|
||||
'青': "🈳️",
|
||||
'井': "🈳️",
|
||||
'皀': "🈳️",
|
||||
'鬯': "🈳️",
|
||||
'食': "🈳️",
|
||||
'亼': "🈳️",
|
||||
'會': "🈳️",
|
||||
'倉': "🈳️",
|
||||
'入': "🈳️",
|
||||
'缶': "🈳️",
|
||||
'矢': "🈳️",
|
||||
'高': "🈳️",
|
||||
'冂': "🈳️",
|
||||
'𩫖': "🈳️",
|
||||
'京': "🈳️",
|
||||
'亯': "🈳️",
|
||||
'㫗': "🈳️",
|
||||
'畗': "🈳️",
|
||||
'㐭': "🈳️",
|
||||
'嗇': "🈳️",
|
||||
'來': "🈳️",
|
||||
'麥': "🈳️",
|
||||
'夊': "🈳️",
|
||||
'舛': "🈳️",
|
||||
'舜': "🈳️",
|
||||
'韋': "🈳️",
|
||||
'弟': "🈳️",
|
||||
'夂': "🈳️",
|
||||
'久': "🈳️",
|
||||
'桀': "🈳️",
|
||||
'木': "🈳️",
|
||||
'東': "🈳️",
|
||||
'林': "🈳️",
|
||||
'才': "🈳️",
|
||||
'叒': "🈳️",
|
||||
'之': "🈳️",
|
||||
'帀': "🈳️",
|
||||
'出': "🈳️",
|
||||
'𣎵': "🈳️",
|
||||
'生': "🈳️",
|
||||
'乇': "🈳️",
|
||||
'𠂹': "🈳️",
|
||||
'𠌶': "🈳️",
|
||||
'華': "🈳️",
|
||||
'𥝌': "🈳️",
|
||||
'稽': "🈳️",
|
||||
'巢': "🈳️",
|
||||
'桼': "🈳️",
|
||||
'束': "🈳️",
|
||||
'㯻': "🈳️",
|
||||
'囗': "🈳️",
|
||||
'員': "🈳️",
|
||||
'貝': "🈳️",
|
||||
'邑': "🈳️",
|
||||
'𨛜': "🈳️",
|
||||
'日': "🈳️",
|
||||
'旦': "🈳️",
|
||||
'倝': "🈳️",
|
||||
'㫃': "🈳️",
|
||||
'冥': "🈳️",
|
||||
'晶': "🈳️",
|
||||
'月': "🈳️",
|
||||
'有': "🈳️",
|
||||
'朙': "🈳️",
|
||||
'囧': "🈳️",
|
||||
'夕': "🌇",
|
||||
'多': "🪩",
|
||||
'毌': "🈳️",
|
||||
'𢎘': "🈳️",
|
||||
'𣐺': "🈳️",
|
||||
'𠧪': "🈳️",
|
||||
'齊': "🈳️",
|
||||
'朿': "🈳️",
|
||||
'片': "🈳️",
|
||||
'鼎': "🈳️",
|
||||
'克': "🈳️",
|
||||
'彔': "🈳️",
|
||||
'禾': "🈳️",
|
||||
'秝': "🈳️",
|
||||
'黍': "🈳️",
|
||||
'香': "🈳️",
|
||||
'米': "🈳️",
|
||||
'毇': "🈳️",
|
||||
'臼': "🈳️",
|
||||
'凶': "🈳️",
|
||||
'朩': "🈳️",
|
||||
'𣏟': "🈳️",
|
||||
'麻': "🈳️",
|
||||
'尗': "🈳️",
|
||||
'耑': "🈳️",
|
||||
'韭': "🈳️",
|
||||
'瓜': "🈳️",
|
||||
'瓠': "🈳️",
|
||||
'宀': "🏠",
|
||||
'宮': "🏛",
|
||||
'呂': "🩻",
|
||||
'穴': "🕳️",
|
||||
'㝱': "🈳️",
|
||||
'疒': "😷",
|
||||
'冖': "🗃",
|
||||
'𠔼': "🈳️",
|
||||
'冃': "🈳️",
|
||||
'㒳': "🈳️",
|
||||
'网': "🈳️",
|
||||
'襾': "🈳️",
|
||||
'巾': "🧣",
|
||||
'巿': "🈳️",
|
||||
'帛': "🈳️",
|
||||
'白': "⚪",
|
||||
'㡀': "🈳️",
|
||||
'黹': "🈳️",
|
||||
'𠤎': "🈳️",
|
||||
'匕': "🈳️",
|
||||
'从': "🈳️",
|
||||
'比': "🈳️",
|
||||
'北': "🈳️",
|
||||
'丘': "🈳️",
|
||||
'㐺': "🈳️",
|
||||
'𡈼': "🈳️",
|
||||
'重': "🈳️",
|
||||
'臥': "🈳️",
|
||||
'身': "🈳️",
|
||||
'㐆': "🈳️",
|
||||
'衣': "🈳️",
|
||||
'裘': "🈳️",
|
||||
'老': "🈳️",
|
||||
'毛': "🈳️",
|
||||
'毳': "🈳️",
|
||||
'尸': "🈳️",
|
||||
'尺': "🈳️",
|
||||
'尾': "🈳️",
|
||||
'履': "🈳️",
|
||||
'舟': "🛶",
|
||||
'方': "🈳️",
|
||||
'儿': "🈳️",
|
||||
'兄': "🈳️",
|
||||
'兂': "🈳️",
|
||||
'皃': "🈳️",
|
||||
'𠑹': "🈳️",
|
||||
'先': "🈳️",
|
||||
'禿': "🈳️",
|
||||
'見': "🈳️",
|
||||
'覞': "🈳️",
|
||||
'欠': "🈳️",
|
||||
'㱃': "🈳️",
|
||||
'㳄': "🈳️",
|
||||
'旡': "🈳️",
|
||||
'頁': "🈳️",
|
||||
'𦣻': "🈳️",
|
||||
'面': "🈳️",
|
||||
'丏': "🈳️",
|
||||
'首': "🈳️",
|
||||
'𥄉': "🈳️",
|
||||
'須': "🈳️",
|
||||
'彡': "🈳️",
|
||||
'彣': "🈳️",
|
||||
'文': "🈳️",
|
||||
'髟': "🈳️",
|
||||
'后': "🈳️",
|
||||
'司': "🈳️",
|
||||
'卮': "🈳️",
|
||||
'卩': "🈳️",
|
||||
'印': "🈳️",
|
||||
'色': "🈳️",
|
||||
'𠨍': "🈳️",
|
||||
'辟': "🈳️",
|
||||
'勹': "🈳️",
|
||||
'包': "🈳️",
|
||||
'茍': "🈳️",
|
||||
'鬼': "🈳️",
|
||||
'甶': "🈳️",
|
||||
'厶': "🈳️",
|
||||
'嵬': "🈳️",
|
||||
'山': "🈳️",
|
||||
'屾': "🈳️",
|
||||
'屵': "🈳️",
|
||||
'广': "🈳️",
|
||||
'厂': "🈳️",
|
||||
'丸': "🈳️",
|
||||
'危': "🈳️",
|
||||
'石': "🈳️",
|
||||
'長': "🈳️",
|
||||
'勿': "🈳️",
|
||||
'冄': "🈳️",
|
||||
'而': "🈳️",
|
||||
'豕': "🈳️",
|
||||
'㣇': "🈳️",
|
||||
'彑': "🈳️",
|
||||
'豚': "🈳️",
|
||||
'豸': "🈳️",
|
||||
'𤉡': "🈳️",
|
||||
'易': "🈳️",
|
||||
'象': "🈳️",
|
||||
'馬': "🈳️",
|
||||
'𢊁': "🈳️",
|
||||
'鹿': "🈳️",
|
||||
'麤': "🈳️",
|
||||
'㲋': "🈳️",
|
||||
'兔': "🈳️",
|
||||
'萈': "🈳️",
|
||||
'犬': "🈳️",
|
||||
'㹜': "🈳️",
|
||||
'鼠': "🈳️",
|
||||
'能': "🈳️",
|
||||
'熊': "🈳️",
|
||||
'火': "🈳️",
|
||||
'炎': "🈳️",
|
||||
'黑': "🈳️",
|
||||
'囪': "🈳️",
|
||||
'焱': "🈳️",
|
||||
'炙': "🈳️",
|
||||
'赤': "🈳️",
|
||||
'大': "🈳️",
|
||||
'亦': "🈳️",
|
||||
'夨': "🈳️",
|
||||
'夭': "🈳️",
|
||||
'交': "🈳️",
|
||||
'尣': "🈳️",
|
||||
'壺': "🈳️",
|
||||
'壹': "🈳️",
|
||||
'㚔': "🈳️",
|
||||
'奢': "🈳️",
|
||||
'亢': "🈳️",
|
||||
'夲': "🈳️",
|
||||
'夰': "🈳️",
|
||||
'亣': "🈳️",
|
||||
'夫': "🈳️",
|
||||
'立': "🈳️",
|
||||
'竝': "🈳️",
|
||||
'囟': "🈳️",
|
||||
'思': "🈳️",
|
||||
'心': "🈳️",
|
||||
'惢': "🈳️",
|
||||
'水': "🈳️",
|
||||
'沝': "🈳️",
|
||||
'瀕': "🈳️",
|
||||
'𡿨': "🈳️",
|
||||
'巜': "🈳️",
|
||||
'川': "🈳️",
|
||||
'泉': "🈳️",
|
||||
'灥': "🈳️",
|
||||
'永': "🈳️",
|
||||
'𠂢': "🈳️",
|
||||
'谷': "🈳️",
|
||||
'仌': "🈳️",
|
||||
'雨': "🈳️",
|
||||
'雲': "🈳️",
|
||||
'魚': "🈳️",
|
||||
'𩺰': "🈳️",
|
||||
'燕': "🈳️",
|
||||
'龍': "🈳️",
|
||||
'飛': "🈳️",
|
||||
'非': "🈳️",
|
||||
'卂': "🈳️",
|
||||
'𠃉': "🈳️",
|
||||
'不': "🈳️",
|
||||
'至': "🈳️",
|
||||
'西': "🈳️",
|
||||
'鹵': "🈳️",
|
||||
'鹽': "🈳️",
|
||||
'戶': "🈳️",
|
||||
'門': "🈳️",
|
||||
'耳': "🈳️",
|
||||
'𦣞': "🈳️",
|
||||
'手': "✋",
|
||||
'𠦬': "🈳️",
|
||||
'毋': "🈳️",
|
||||
'民': "🈳️",
|
||||
'丿': "🈳️",
|
||||
'𠂆': "🈳️",
|
||||
'乁': "🈳️",
|
||||
'氏': "🈳️",
|
||||
'氐': "🈳️",
|
||||
'戈': "🈳️",
|
||||
'戉': "🈳️",
|
||||
'我': "🈳️",
|
||||
'亅': "🈳️",
|
||||
'珡': "🈳️",
|
||||
'𠃊': "🈳️",
|
||||
'亾': "🈳️",
|
||||
'匸': "🈳️",
|
||||
'匚': "🈳️",
|
||||
'曲': "🈳️",
|
||||
'甾': "🈳️",
|
||||
'瓦': "🈳️",
|
||||
'弓': "🈳️",
|
||||
'弜': "🈳️",
|
||||
'弦': "🈳️",
|
||||
'系': "🈳️",
|
||||
'糸': "🈳️",
|
||||
'素': "🈳️",
|
||||
'絲': "🈳️",
|
||||
'率': "🈳️",
|
||||
'虫': "🈳️",
|
||||
'䖵': "🈳️",
|
||||
'蟲': "🈳️",
|
||||
'風': "🈳️",
|
||||
'它': "🈳️",
|
||||
'龜': "🈳️",
|
||||
'黽': "🈳️",
|
||||
'卵': "🈳️",
|
||||
'二': "🈳️",
|
||||
'土': "🈳️",
|
||||
'垚': "🈳️",
|
||||
'堇': "🈳️",
|
||||
'里': "🈳️",
|
||||
'畕': "🈳️",
|
||||
'黃': "🈳️",
|
||||
'男': "🈳️",
|
||||
'力': "🈳️",
|
||||
'劦': "🈳️",
|
||||
'金': "💰",
|
||||
'幵': "🈳️",
|
||||
'勺': "🈳️",
|
||||
'几': "🈳️",
|
||||
'且': "🈳️",
|
||||
'斤': "🈳️",
|
||||
'斗': "🈳️",
|
||||
'矛': "🈳️",
|
||||
'車': "🈳️",
|
||||
'𠂤': "🈳️",
|
||||
'𨸏': "🈳️",
|
||||
'𨺅': "🈳️",
|
||||
'厽': "🈳️",
|
||||
'四': "🈳️",
|
||||
'宁': "🈳️",
|
||||
'叕': "🈳️",
|
||||
'亞': "🈳️",
|
||||
'五': "🈳️",
|
||||
'六': "🈳️",
|
||||
'七': "🈳️",
|
||||
'九': "🈳️",
|
||||
'禸': "🈳️",
|
||||
'嘼': "🈳️",
|
||||
'甲': "🈳️",
|
||||
'乙': "🈳️",
|
||||
'丙': "🈳️",
|
||||
'丁': "🈳️",
|
||||
'戊': "🈳️",
|
||||
'己': "🈳️",
|
||||
'巴': "🈳️",
|
||||
'庚': "🈳️",
|
||||
'辛': "🈳️",
|
||||
'辡': "🈳️",
|
||||
'壬': "🈳️",
|
||||
'癸': "🈳️",
|
||||
'子': "🚼",
|
||||
'了': "🈳️",
|
||||
'孨': "🈳️",
|
||||
'𠫓': "🈳️",
|
||||
'丑': "🈳️",
|
||||
'寅': "🈳️",
|
||||
'戼': "🈳️",
|
||||
'辰': "🈳️",
|
||||
'巳': "🈳️",
|
||||
'午': "🈳️",
|
||||
'未': "🈳️",
|
||||
'申': "🈳️",
|
||||
'酉': "🈳️",
|
||||
'酋': "🈳️",
|
||||
'戌': "🈳️",
|
||||
'亥': "🈳️",
|
||||
}
|
||||
63
string.go
Normal file
63
string.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package emozi
|
||||
|
||||
import (
|
||||
"hash/crc32"
|
||||
"strings"
|
||||
|
||||
base14 "github.com/fumiama/go-base16384"
|
||||
)
|
||||
|
||||
// EmoziString 一个颜文字汉字转写串, 包含串头 校验字节数*2 字节校验和
|
||||
type EmoziString string
|
||||
|
||||
// WrapRawEmoziString 为不包含串头的转写串加一个头使其成为合法 EmoziString
|
||||
func WrapRawEmoziString(s string) EmoziString {
|
||||
rs := []rune(s)
|
||||
if len(rs) < 4 {
|
||||
return ""
|
||||
}
|
||||
h := crc32.NewIEEE()
|
||||
h.Write(base14.StringToBytes(s))
|
||||
sum := h.Sum32() % 校验模
|
||||
sb := strings.Builder{}
|
||||
buf := [校验字节数]uint8{}
|
||||
for i := 校验字节数 - 1; i >= 0; i-- {
|
||||
buf[i] = uint8((sum / 校验倍数[i]) % uint32(校验表长度))
|
||||
}
|
||||
for i, n := range buf {
|
||||
sb.WriteRune(rs[i])
|
||||
sb.WriteRune(校验表[n])
|
||||
}
|
||||
sb.WriteString(string(rs[len(buf):]))
|
||||
return EmoziString(sb.String())
|
||||
}
|
||||
|
||||
// String 输出不包含串头的转写串
|
||||
func (es EmoziString) String() string {
|
||||
if !es.IsValid() {
|
||||
return "ERROR: invalid EmoziString"
|
||||
}
|
||||
rs := []rune(es)
|
||||
sb := strings.Builder{}
|
||||
for i := 0; i < 校验字节数; i++ {
|
||||
sb.WriteRune(rs[i*2])
|
||||
}
|
||||
sb.WriteString(string(rs[校验字节数*2:]))
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// IsValid 判断是否是合法 EmoziString
|
||||
func (es EmoziString) IsValid() bool {
|
||||
rs := []rune(es)
|
||||
if len(rs) < 校验字节数+4 {
|
||||
return false
|
||||
}
|
||||
h := crc32.NewIEEE()
|
||||
sum := uint32(0)
|
||||
for i := 0; i < 校验字节数; i++ {
|
||||
h.Write(base14.StringToBytes(string(rs[i*2])))
|
||||
sum += 校验倍数[i] * uint32(逆校验表[rs[i*2+1]])
|
||||
}
|
||||
h.Write(base14.StringToBytes(string(rs[校验字节数*2:])))
|
||||
return h.Sum32()%校验模 == sum
|
||||
}
|
||||
20
string_test.go
Normal file
20
string_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package emozi
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestWrapUnwrap(t *testing.T) {
|
||||
t.Log(校验表长度)
|
||||
if !WrapRawEmoziString("😨😨😨😨").IsValid() {
|
||||
t.Fail()
|
||||
}
|
||||
if EmoziString("😨😨😨😨😨😨😨").IsValid() {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
t.Log(校验表长度)
|
||||
if WrapRawEmoziString("😨😨😨😨").String() != "😨😨😨😨" {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user