-
现在使用五笔输入法的人是越来越少了,像我一样在Linux系统上使用五笔的,就更是少得可怜。想当年,五笔输入法还是一个比较流行的输入法的时候,在Windows上有一个非常火爆的软件:极点五笔输入法。后来有人把它移植到了Linux系统,但这种没有大公司支持的项目,是没有持续更新支持的,所以,这么多年来,我一直使用着一个过时的词库,如果遇到词库里没有的专用词,我就自己用快捷键把它加到词库里,多年累积下来,也算是更新了一小部分词库。但实际上,还是有很多覆盖不到的词,比如原始词库连"支付宝"都不认识。 -
为了解决这个问题,我在AI的辅助下做了一些调研,并在此基础上对Ubuntu上使用的极点五笔输入法词库做了一次与时俱进的更新。 -
说得简单点,就是如下这个过程:从公网找词库 → 筛选 → 转成 fcitx4 二进制码表 → 校验 → 上线 → 回滚预案。 -
配套代码已开源:LINK -
文中只展示核心代码片段;片段为示意节选,与仓库里的文件不完全逐行一致,请以仓库中的实际代码为准(每节标注了文件名并附链接)。 -
以下所有命令都假设你已克隆并进入仓库目录: -
git clone https://github.com/codelast/update-freewb-dict-on-linux.git && cd update-freewb-dict-on-linux -
适用:fcitx 4.x + fcitx-table-wbpy(「五笔拼音」,词库源自极点五笔的极爽词库)。
0. 速览
| 环节 | 要点 |
|---|---|
| 我的Ubuntu机器在用的东西 | Linux 上没有 Windows 版极点五笔;实际是 fcitx4 的 table 引擎 + wbpy 码表,词库是「极爽词库 6.0」 |
| 词库文件 |
~/.config/fcitx/table/wbpy.mb(二进制,约 10MB,30 万条量级) |
| 能不能直接编辑 | 不能。私有二进制格式,必须解析→改→重新序列化 |
| 权威格式来源 |
fcitx4 源码 src/im/table/tabledict.c、tools/cli/mb2txt.c、tools/cli/txt2mb.c |
| 关键工具 |
用 build_mb2txt.sh 编译官方 mb2txt 做回读校验 |
| 上线流程 | 停 fcitx → 备份 → 基于最新文件重建 → 官方工具全量 diff → 替换 → 启动 → 实打验证 |
| 回滚 |
./rollback.sh <备份>,一分钟 |
本次实际成果(示例):词库 312,315 → 336,897 条,新增五笔 10,203 条、拼音 14,378 条,原有记录零丢失。
文章来源:https://www.codelast.com/
仓库文件清单
下表文件均位于仓库 main 分支,点击文件名可直达源码。
| 文件 | 作用 | 依赖 |
|---|---|---|
build_mb2txt.sh |
编译官方 mb2txt(码表 → 文本) |
gcc、curl |
tabledict.h |
编译用的极简头文件 | — |
stub.c |
fcitx_utils_read_uint32 的替代实现 |
— |
mb_table.py |
码表读写库(解析 / 序列化 / 有序插入) | 仅标准库 |
merge_wubi.py |
并入五笔词条(type=0) | mb_table.py |
gen_pinyin.py |
生成拼音码(含多音备选) | pypinyin |
merge_pinyin.py |
并入拼音记录(type=1) | mb_table.py |
filter_candidates.py |
候选筛选(差集 + 质量判据) | 词库文件、字频表 |
apply.sh |
停服→备份→校验→替换→重启 | mb2txt |
rollback.sh |
回滚到备份 | — |
1. 背景与原理
1.1 「极点五笔」在 Linux 上到底是什么
| 平台 | 实现 | 词库载体 |
|---|---|---|
| Windows | 极点五笔(FreeWB,杜志民) |
freeime\mb\ 下的纯文本码表目录 |
| Ubuntu(本文) |
fcitx4 的 table 引擎 + 码表 wbpy |
~/.config/fcitx/table/wbpy.mb(二进制) |
所以「更新极点五笔词库」在 Linux 上等于「替换 wbpy.mb 里的词条数据」。引擎、快捷键、皮肤、用户造词习惯全部不变,只是数据变了。
wbpy.conf 里几个决定格式的关键参数:
[CodeTable]
UniqueName=wbpy
Name[zh_CN]=五笔拼音
File=wbpy.mb
ExactMatch=False ; 前缀匹配 → 拼音码被截断也照样能用
UsePY=False
PYKey=z
1.2 为什么不直接用文本编辑器
.mb 是 fcitx 私有的二进制表:头部含按键字符集、码长、组词规则,记录区是按码排序的定长头 + 变长词条。 用文本工具改,轻则加载失败,重则索引错乱(能启动但查不到词)。
文章来源:https://www.codelast.com/
2. 二进制格式(权威来源 + 实测)
2.1 源码位置
GitHub fcitx/fcitx(fcitx4,已归档):
| 文件 | 作用 |
|---|---|
src/im/table/tabledict.c |
TableData::load/save,格式的唯一权威 |
tools/cli/mb2txt.c |
官方 二进制 → 文本 |
tools/cli/txt2mb.c |
官方 文本 → 二进制 |
⚠️ 不要参考 nopdan/rose 的 format/fcitx4_mb:它假设码字段宽度 = codeLen + 1(=5),实际是 pyCodeLen + 1(本文示例 = 13)。按它的写法解析会整体错位一条记录。
2.2 文件结构
┌── 头部 ─────────────────────────────────────────────┐
│ u32 0 新格式标志 │
│ i8 version 3 = 当前版本(<3 为旧版) │
│ u32 keyLen │
│ u8 KeyCode[keyLen+1] NUL 结尾,如 "abc...y" │
│ u8 codeLen 码长(本文示例 4) │
│ u8 pyCodeLen 拼音码长(本文示例 12) │
│ u32 ignoreLen │
│ u8 InvalidChar[ignoreLen+1] │
│ u8 hasRule 1 = 后面跟组词规则表 │
│ (codeLen-1) × [ u8 flag, u8 words, │
│ codeLen × (u8 flag, u8 which, u8 idx) ]│
│ u32 recordCount │
├── 记录区(按码排序,共 recordCount 条)──────────────┤
│ 每条: │
│ 码 pyCodeLen+1 字节(NUL 填充;本文 13 字节) │
│ u32 词长(**含**结尾 NUL) │
│ 词 UTF-8 字节 + NUL │
│ i8 type 0=五笔 1=拼音 2=自造词 3=提示 │
│ u32 iHit 用户命中次数(新词写 0) │
│ u32 iIndex 全局递增序号(新词取 max+1) │
└─────────────────────────────────────────────────────┘
2.3 三条硬约束(踩过才知道)
-
记录必须按「码的首字符」分组排序。 table.c(上游源码,仅阅读参考) 建索引时只比较cChar != strCode[0],顺序乱了就查不到。 -
同码候选的先后 = 记录在文件里的先后。想让它排最后,就插到该码组的末尾;想排第一,插到组首。 -
前 4 字节不是词长,而是码字段的第 5~8 字节。这是最容易搞错的地方(第一版解析器就栽在这)。
2.4 匹配行为(决定拼音码能不能截断)
tabledict.c::TableCompareCode() 只按用户输入长度逐字节比较,wbpy.conf 里 ExactMatch=False,所以是前缀匹配:
size_t tmp_len = strlen(strUser);
for (i = 0; i < tmp_len; i++) { if (strUser[i] != strDict[i]) return …; }
if (exactMatch && strlen(strUser) != strlen(strDict)) return -999;
含义:拼音码被截断到 12 字母也照样能用(打前几个字母就出候选);五笔 4 码同理。
文章来源:https://www.codelast.com/
3. 工具链:编译官方 mb2txt
文件:build_mb2txt.sh(配合 tabledict.h、stub.c)
mb2txt.c(脚本自动下载) 只外部依赖一个 fcitx_utils_read_uint32,所以可以脱离 fcitx 开发环境单独编译。脚本做的事就是:放好极简头文件 → 下载官方 mb2txt.c → 用 stub 编译。
核心三行:
mkdir -p im/table && cp -f tabledict.h im/table/tabledict.h
curl -fsSL -o mb2txt.c https://raw.githubusercontent.com/fcitx/fcitx/master/tools/cli/mb2txt.c
gcc -O2 -I. -o mb2txt mb2txt.c stub.c
产出用法:
./mb2txt ~/.config/fcitx/table/wbpy.mb > table.txt # 二进制 → 文本
./mb2txt -o 旧版.mb > old.txt # 旧格式(version<3)
输出形如:
;fcitx Version 0x03 Table file
KeyCode=abcdefghijklmnopqrstuvwxy
Length=4
Pinyin=@
PinyinLength=12
Prompt=&
ConstructPhrase=^
[Rule]
e2=p11+p12+p21+p22
e3=p11+p21+p31+p32
a4=p11+p21+p31+n11
[Data]
a 工
a 戈
@a 阿 ← @ 开头 = type=1(拼音记录)
文章来源:https://www.codelast.com/
4. 从公网找词库 + 筛选
4.1 数据源(全部开源可下载)
| 来源 | 地址(标识) | 内容 | 备注 |
|---|---|---|---|
| 白霜五笔 |
GitHub gaboolic/rime-frost → cn_dicts_wb/{chars,words}.dict.yaml |
词 + 五笔码 + 现代语料词频 | 词频用 7 亿字语料重算(GPL-3.0) |
| 墨奇五笔 |
GitHub gaboolic/rime-wubi-sentence → cn_dicts_wb/base.dict.yaml |
36 万条,含大量新词/专名 | 码字段是"整句码",需要还原(MIT) |
| 现代汉语常用词表 |
gist indiejoseph/eae09c673460aa0b56db |
5.6 万条规范词表 | 判定"是不是一个词"(无显式许可,仅作判据) |
| 成语库 |
GitHub pwxcoo/chinese-xinhua → data/idiom.json |
3.09 万条成语 | 4 字词精确筛选(MIT) |
| 本机表 |
自己 mb2txt 出来的 dump |
已有词、单字五笔全码、单字读音 | 差集 + 编码校验基准 |
4.2 两个词库的码字段怎么还原
白霜:词 \t 4字母码 \t 词频,直接用。 墨奇:词 \t "xx,ax xx,ax …" \t 权重,每段对应一个字、逗号前是该字前两码,按五笔词组规则还原:
def moqi_to_code(s):
segs = [x.split(',')[0] for x in s.split()]
n = len(segs)
if n == 2: return segs[0][:2] + segs[1][:2]
if n == 3: return segs[0][0] + segs[1][0] + segs[2][:2]
return segs[0][0] + segs[1][0] + segs[2][0] + segs[-1][0]
(2 字=各前两码;3 字=首字首码+次字首码+末字前两码;4 字以上=前三字首码+末字首码)
4.3 质量判据:用「凝固度」把搭配垃圾挡在门外
拼音词库是给整句解码用的,天然塞满 有多、人是、的时候 这类高频二元组。它们对拼音输入有意义,对五笔是纯噪音。
-
PMI(内部凝固度): PMI = log2( P(w) / Π P(c) ),再除以(词长-1)做长度归一。 -
阈值怎么定:拿"本机已收录的 2 字词"反推分布——实测 P25 ≈ 5.0、中位数 ≈ 8.0 → 取 归一化 PMI ≥ 5.0。 -
对照: 人是-2.5、中有-0.9、是最-0.2、有多1.7(都在底部分位);题主11.5、刷题8.2、算力6.8。
PMI 不是充分条件(的时候 PMI 18.1 也不是词),所以再叠三层:虚词字过滤 → 方位/副词短语过滤 → 词典交叉(把"确认是词"和"疑似新词"分成两档)。
核心片段(filter_candidates.py):
def word_code(w):
"""用本机单字全码拼词码(比信外部词库更一致)"""
cs = [wubi_single.get(ch) for ch in w]
if any(not c for c in cs): return None
if len(w) == 2: r = cs[0][:2] + cs[1][:2]
elif len(w) == 3: r = cs[0][0] + cs[1][0] + cs[2][:2]
else: r = cs[0][0] + cs[1][0] + cs[2][0] + cs[-1][0]
return r if len(r) == 4 and r.isalpha() else None
def pmi(word, freq, tot_w):
pc = 1.0
for ch in word:
if ch not in char: return None
pc *= char[ch] / tot_c
return math.log2((freq / tot_w) / pc) / max(1, len(word) - 1)
def keep(w):
if set(w) & FUNC: return False # 含虚词字
if len(w) >= 3 and w[-1] in LOC: return False # 方位短语
if len(w) >= 3 and w[0] in DEG: return False # 副词开头
if any(g in w for g in GLUE): return False # 常见口语搭配
return True
4.4 三次实际筛选结果(示例)
| 批次 | 范围 | 主要过滤 | 阈值 | 产出 |
|---|---|---|---|---|
| A 常用词 | 2~3 字,本机缺失,且在现汉词表里 | 虚词字 + PMI≥5 | — | 1,163 |
| B 新词/专名 | 2~3 字,本机缺失,不在词表 | 上述三层 + 搭配黑名单 | 词频≥500 | 2,302 |
| C 成语 | 4 字,本机缺失,在成语库里 | 精确匹配成语库 | 全量 | 6,738 |
经验:2 字档噪音约 15%,3 字档约 25~30%,4 字档原始候选噪音六成(必须靠成语库/词表分档)。
文章来源:https://www.codelast.com/
5. 程序清单与核心逻辑
5.1 mb_table.py — 码表读写库(核心)
对外三个函数:read(path) -> Table、write(path, table)、insert_sorted(table, items)。
-
read:按 2.2 的结构解析,最后断言len(records) == recordCount(防格式判断失误) -
write:头部原样写回 + 记录逐条序列化(码左对齐 NUL 填充、词长含 NUL) -
insert_sorted:按码插入,同码插在组尾,iIndex取全局 max 递增
insert_sorted 的核心(候选顺序 = 文件顺序,所以插入位置决定排第几):
def insert_sorted(table, new_items):
max_idx = max((r[4] for r in table.records), default=0)
add = []
for code, word, typ in new_items:
max_idx += 1
add.append([code, word + b'\x00', typ, 0, max_idx])
add.sort(key=lambda r: (r[0], r[1]))
out, i, ai, recs = [], 0, 0, table.records
while i < len(recs):
if ai < len(add) and add[ai][0] < recs[i][0]:
out.append(add[ai]); ai += 1
else:
cur = recs[i]; out.append(cur); i += 1
if ai < len(add) and add[ai][0] == cur[0]: # 走到该码组末尾
out.append(add[ai]); ai += 1
out.extend(add[ai:])
table.records = out
return len(add)
5.2 merge_wubi.py — 并入五笔词条
读 CSV(至少含 词,五笔码),跳过表里已存在的词,调用 insert_sorted(..., type=0)。
have = {r[1].rstrip(b'\x00').decode('utf-8', 'ignore') for r in t.records}
items = [(row['五笔码'].encode(), row['词'].encode(), 0)
for row in csv.DictReader(open(cand_csv, encoding='utf-8'))
if row['词'] not in have]
n = mb_table.insert_sorted(t, items)
mb_table.write(dst, t)
用法:python3 merge_wubi.py <源.mb> <候选.csv> <输出.mb>
5.3 gen_pinyin.py — 生成拼音码(含多音字)
三个要点:词组级注音(避免多音字按单字乱读)、多音备选、按 pyCodeLen 截断。 关键设计:备选读音必须在"本机表里已出现过的单字读音"白名单内,否则会造出输入法根本打不出的废码。
# 1) 从 dump 里取单字读音白名单
local = collections.defaultdict(set)
for ln in body:
if ln.startswith('@') and ' ' in ln:
c, w = ln[1:].split(' ', 1)
if len(w) == 1:
local[w].add(c)
# 2) 词组级注音 + 按白名单过滤每个字的读音
base = ''.join(lazy_pinyin(w))[:12]
vs = pinyin(w, heteronym=True, style=Style.NORMAL)
vs = [[p for p in lst if p in local.get(ch, {p})] or lst for lst, ch in zip(vs, w)]
# 3) 组合候选,去重,最多保留 cap 个
for combo in itertools.product(*vs):
p = ''.join(combo)[:12]
if p not in cands: cands.append(p)
if len(cands) >= cap: break
用法:python3 gen_pinyin.py <词表.csv> <输出.csv> <表dump.txt> [每词最多几个码]
5.4 merge_pinyin.py — 并入拼音记录
与 merge_wubi.py 同构,区别是 type=1,且支持跳过冲突码:
若某拼音码恰好等于一个"只有唯一候选"的五笔码,插入后会破坏"唯一即上屏"。这类码要放进跳过列表。
skip = set((sys.argv[4] if len(sys.argv) > 4 else '').split(','))
items = [(row['拼音码'].encode(), row['词'].encode(), 1)
for row in csv.DictReader(open(py_csv, encoding='utf-8'))
if row['拼音码'] and row['拼音码'] not in skip
and (row['词'].encode(), row['拼音码'].encode()) not in existing]
用法:python3 merge_pinyin.py <源.mb> <拼音.csv> <输出.mb> [跳过的码,...]
5.5 filter_candidates.py — 候选筛选
输入:本机 dump + 字频表(chars.dict.yaml)+ 一个或多个外部词库文件;输出 CSV:词,五笔码,词频,归一PMI。 判据见第 4.3 节。
用法:python3 filter_candidates.py <dump.txt> <chars.dict.yaml> <词库1> [词库2 ...] > cand.csv
文章来源:https://www.codelast.com/
6. 安装与验证
6.1 为什么必须先停 fcitx
fcitx-table 在退出时会重写整个 .mb(把内存里的表 + 用户造词落盘)。只要 fcitx 还在跑,你替换的文件可能被覆盖,或它下次退出时把内存旧表写回去。 所以顺序必须是:停 → 备份 → 基于最新文件重建 → 替换 → 启动。
6.2 apply.sh:一键替换(已封装上述顺序)
./apply.sh <new_table.mb> [before.txt]
脚本内部的关键几步:
pkill -TERM -x fcitx; sleep 3 # 1) 停(让它把用户词落盘)
cp -p "$MB" "$HOME/wbpy.mb.bak-$TS" # 2) 备份
"$HERE/mb2txt" "$NEW" > after.txt # 3) 回读新表
ADD=$(diff <(sort before.txt) <(sort after.txt) | grep -c '^>')
LOST=$(diff <(sort before.txt) <(sort after.txt) | grep -c '^<')
[ "$LOST" = "0" ] || exit 1 # 4) 有丢失就中止,不动原库
cp "$NEW" "$MB"; chmod 600 "$MB" # 5) 替换
setsid fcitx -d & # 6) 启动
手工等价流程(不用脚本时):
MB="$HOME/.config/fcitx/table/wbpy.mb"
pkill -TERM -x fcitx; sleep 3
cp -p "$MB" "$HOME/wbpy.mb.bak-$(date +%Y%m%d-%H%M%S)"
./mb2txt "$MB" > before.txt
python3 merge_wubi.py "$MB" cand_wubi.csv after.mb
./mb2txt after.mb > after.txt
diff <(sort before.txt) <(sort after.txt) | grep -c '^>' # 新增条数
diff <(sort before.txt) <(sort after.txt) | grep -c '^<' # 必须为 0
cp after.mb "$MB" && chmod 600 "$MB"
setsid fcitx -d &
-
无图形界面的 shell 里启动 fcitx 需带上会话变量: DISPLAY=:0、XAUTHORITY=$XDG_RUNTIME_DIR/gdm/Xauthority、DBUS_SESSION_BUS_ADDRESS=unix:path=$XDG_RUNTIME_DIR/bus。 -
注意 fcitx-remote在没有输入焦点的 shell 里可能返回 0,不能当判据;用xprop -root | grep XIM_SERVERS(应含@server=fcitx)或直接打字判断。
6.3 三层验证
| 层次 | 方法 | 通过标准 |
|---|---|---|
| 结构 |
官方 mb2txt 回读 + 全量 diff |
新增条数准确、丢失 0 |
| 加载 |
fcitx 启动日志、xprop -root \| grep XIM_SERVERS |
无表相关报错、@server=fcitx 在 |
| 功能 | 实际打字:唯一码 + 重码各挑几个 | 唯一码直接上屏;重码在候选里(新词排最后) |
抽验示例:
五笔:iiid 泪流满面 / fiml 云淡风轻 / uspy 盖棺定论 / aflf 攻城略地
拼音:leiliuma → 泪流满面 / yundanfe → 云淡风轻 / zhifubao → 支付宝
6.4 回滚
./rollback.sh ~/wbpy.mb.bak-YYYYmmdd-HHMMSS
建议每批改动前都备份;确认稳定几天后只留最近一份(每个约 10MB)。
文章来源:https://www.codelast.com/
7. 经验与坑(按踩坑顺序)
-
别信"码宽 = 码长 + 1"。真实是 pyCodeLen + 1(w bpy 表里是 13)。按 4/5 字节解析会整体错位,且因为同码相邻记录"看起来也对",很难发现。 -
前 4 字节不是词长。它是码字段的中间部分;词长在码字段之后。第一版解析器能 100% "重建"文件(因为原样拷贝了未知字节),但新记录建不出来 —— round-trip 一致性只能证明"读懂了",不能证明"会写"。 -
记录必须按码首字符分组,否则引擎索引失效。 -
同码先后 = 文件先后,插入位置即候选位置。 -
iIndex用全局最大值 +1,iHit新词写 0,type:五笔 0、拼音 1(写错会被当拼音/提示记录)。 -
拼音码截断到 pyCodeLen(12),但因为是前缀匹配,照样可用。 -
拼音码若与"唯一候选的五笔码"同码,会破坏"唯一即上屏",应跳过。 -
fcitx 退出会重写 .mb,所以顺序不能颠倒;重建要基于"停之后"的最新文件。 -
拼音词库 ≠ 词表。 有多/人是/的时候是整句解码的 n-gram,必须靠 PMI + 词表交叉剔除。 -
4 字词噪音最大,不要只按词频排;用成语库精确匹配效果最好。
8. 一次完整操作清单(checklist)
[ ] git clone https://github.com/codelast/update-freewb-dict-on-linux.git && cd update-freewb-dict-on-linux # 拿到脚本
[ ] ./build_mb2txt.sh # 编译官方工具
[ ] ./mb2txt <词库.mb> > dump.txt # 差集基准 + 单字码/读音来源
[ ] 下载外部词库(白霜 / 墨奇 / 现汉词表 / 成语库)
[ ] python3 filter_candidates.py ... > cand.csv
[ ] 人工抽验候选(TOP + 中段随机)
[ ] python3 merge_wubi.py <源.mb> cand.csv after.mb
[ ] 可选:python3 gen_pinyin.py / merge_pinyin.py
[ ] ./mb2txt after.mb > after.txt;diff 校验:新增对、丢失 0
[ ] ./apply.sh after.mb # 停服→备份→校验→替换→重启
[ ] 日志无报错 + xprop 有 fcitx + 实打若干词
[ ] 记下备份路径与回滚命令
附:常用命令速查
以下命令均需在仓库目录下执行。
# 查看表结构(文本形式)
./mb2txt ~/.config/fcitx/table/wbpy.mb | head -20
# 统计词条数 / 拼音记录数
./mb2txt ~/.config/fcitx/table/wbpy.mb | sed -n '/\[Data\]
/,$p' | tail -n +2 | wc -l
./mb2txt ~/.config/fcitx/table/wbpy.mb | grep -c '^@'
# 按码查词
./mb2txt ~/.config/fcitx/table/wbpy.mb | grep '^rdug '
# 重启输入法
fcitx -r
全部操作只在
~/.config/fcitx/table/(词库与备份)与仓库目录(脚本)内进行,不改动系统文件。
附:授权与合规(数据来源)
| 组件 | 来源 | 许可证 | 用途 |
|---|---|---|---|
| 白霜五笔 |
gaboolic/rime-frost |
GPL-3.0 | 主要词源 + 现代词频 |
| 墨奇五笔 |
gaboolic/rime-wubi-sentence |
MIT | 新词/专名 |
| 雾凇拼音(白霜上游) |
iDvel/rime-ice |
GPL-3.0 | 间接继承 |
| 成语库 |
pwxcoo/chinese-xinhua |
MIT | 4 字成语判定 |
| 现代汉语常用词表 | gist(2014) | 无声明(默认保留) | 仅作"是否成词"判据,未复制内容 |
| fcitx4 源码 |
fcitx/fcitx |
GPL-2.0 | 编译本地校验工具 |
-
本项目代码以 MIT 发布(见仓库 LICENSE)。 -
本机自用(不分发):GPL/MIT 的义务都不触发,无风险。 -
若要分发产出的 .mb:GPL-3.0 部分需提供生成脚本并同许可;MIT 部分保留版权声明;用户自定义词条版权归用户。
文章来源:https://www.codelast.com/
➤➤ 版权声明 ➤➤
转载需注明出处:codelast.com
感谢关注我的微信公众号(微信扫一扫):

以及我的微信视频号:
