【语法算法】
先抛一个坑:数字能转中文大写吗?一百零二转"一百零二"看着简单,中间那个零才是难题——一千零一、一万零二百、一百二十,零什么时候补、补几个,进位读法的账比看上去密。数字转中文的写法就是一本进位读法的账:逐位查表、进位拼接、零位补读,三步走完,阿拉伯数字就念出了中文的调。这篇把整套写法拆开:配置表、数位查表、零位补读、进位拼接、定时器、调试按钮,一段一段照抄能跑。
一、效果演示:进位读法的查表拼接
演示场上下两格:上格是阿拉伯数字,下格是它的中文读法。点「随机数」:一个五位数以内的数落下——上格显示数值,下格当场拼出中文读法,一零二四念"一千零二十四",中间的零一个不少。点「加一」:数字逐个往上加——每加到进位的边界,中文读法当场换调。点「整数」:跳到两万零一——万位和个位之间的三个零只补读一个零,这是进位读法最讲究的一处。演示里试两笔账:零位补读的数目和数字里零的个数——零再多也只补读一个,中文的规矩就是这么省。数字转中文教的是查表:数位查表拼接,零位补读兜底——两张小表加一条补读的规矩,撑起中文数数的一片天。
flowchart TD
A[数字逐位拆开] --> B[每位查数字表]
B --> C{这一位是零}
C -- 是 --> D[已拼了零就跳过 未拼补读零]
C -- 否 --> E[查数位表拼接进位单位]
D --> F[数字右移一位]
E --> F
F --> G{还有位吗}
G -- 是 --> B
G -- 否 --> H[输出中文读法]
fx-numtocn
二、底层原理:一张数位表加一次零位补读
模块的机关是一对搭档。数位查表:数字逐位拆开逐位查表——数字表十个字、进位表五个字,两张小表撑起五位数以内的全部读法。零位补读:连续的零只在恰当的位置补读一个——零是读法的润滑剂,多了啰嗦少了歧义,补读的位置就是中文数数的规矩。和逐字硬翻的分野在"零位补读懂省略":逐字硬翻会把一千零二十四翻成一千零二零四,账面没错读法全错;零位补读是懂中文数数的人在翻译,零补在哪、补不补,全按念的规矩来——规矩是人念出来的,写代码之前先自己念三遍。
零位补读为什么只在未拼时补?连着的零补一个就够——一千零零一的读法是"一千零一"不是"一千零零一",已拼了零就跳过,这条是中文读法最省的规矩,也是查表时最容易写错的一处。
三、核心代码:完整模块(上·骨架)
-- @file NumToCN.lua
-- 数字转中文 —— 进位读法的查表拼接
local NumToCN = {}
local CONST = {
DIGITS = { "零", "一", "二", "三", "四",
"五", "六", "七", "八", "九" },
UNITS = { "", "十", "百", "千", "万" },
MAX_DIGITS = 5,
AUTOINC_BASE = 1163000,
}
local _num = 0
local _autoInc = 0
local function GenID(prefix)
_autoInc = _autoInc + 1
return string.format("%s_%d", prefix or "NTC",
CONST.AUTOINC_BASE + _autoInc)
end
local function ShowTip(msg)
if msg and msg ~= "" then SL:ShowSystemTips(msg) end
end
function NumToCN.SetNum(n)
_num = math.floor(math.max(0, n))
end
function NumToCN.Num()
return _num
end
四、核心代码:完整模块(下·零位补读与进位拼接)
-- 逐位查表拼接:零位补读的完整账
function NumToCN.Convert()
local n = _num
if n == 0 then return CONST.DIGITS[1] end
local out = ""
local unit = 0
local zeroPending = false
while n > 0 do
local d = n % 10
if d == 0 then
if not zeroPending and out ~= "" then
zeroPending = true
end
else
if zeroPending then
out = CONST.DIGITS[1] .. out
zeroPending = false
end
out = CONST.DIGITS[d] .. CONST.UNITS[unit + 1] .. out
end
n = math.floor(n / 10)
unit = unit + 1
end
return out
end
SL:ScheduleOnce(function()
SL:BindDebugButton("随机数", function()
NumToCN.SetNum(math.random(0, 99999))
ShowTip("随机数:" .. NumToCN.Num()
.. " 读作 " .. NumToCN.Convert())
end)
ShowTip("技能已加载: 数字转中文 (调试按钮触发)")
end, 1.0)
function NumToCN.Unload()
_num = 0
end
return NumToCN
五、机制问答
问:一万零一中间的三个零怎么只补读一个?
答:补读一条——零位记号挂着就不再重复补,三个零共享一个补读位,念出来才是一千零一不是一千零零零一。
问:超过五位数还能转吗?
答:账本只到万——十万以上要再添"十万""百万"的进位表,表不够长就先不接单,账本的诚实是查表法的本分。
问:负数能转吗?
答:能——前置一个"负"字再走正数的账,读法的主体不变,负号是门口的一块牌子——进门前先看牌子,牌子后面的账照旧走。
问:零位补读和英文读法一样吗?
答:不一样——英文的零不补读,一千零一和一千一英文读着一样,中文的零必须补,两套规矩别互相套。
问:进位单位为什么从空串开始?
答:个位没有单位——单位表第一格是空串,个位的数字光着拼就行,从十位才开始挂单位。
六、调参与实战怎么用
第四笔是零位记号的规矩:未拼才补、拼过即跳是读法的灵魂——每零必补的版本啰嗦,全不补的版本歧义,这一处是数字转中文全部的难度所在。第三笔是进位表的长度:五格进位表管五位数——表的长短就是账本的容量,容量写死在配置里,超额直接拒单别硬翻——账本的容量写在那儿,翻不动的账硬翻只会错得更体面。第二笔是取整的时机:逐位取余前先取整降位——忘了取整的版本同一位反复读,账越读越长。第一笔是零的特判:零数字单独走一格出口——整本账只有零最难念,零的读法是唯一的特例也是唯一的门面。常见坑三个:连续零重复补读;进位单位错位一格;超大数静默截断没有拒单提示。
实战里数字转中文是"读法类"玩法的通用件:翻牌报数、任务口播、拍卖唱价,全是"逐位查表补零拼接"的同一副骨架。组里的约定是:凡是念给玩家听的数字一律转中文读法——阿拉伯数字是看的,中文数字是念的,两套各有各的岗位。这篇的模块照抄能跑,改的就是两张表。
写完留一句给做读法系的同学:数字转中文卖的是"零补在哪"——那一个见缝插针的零字和逐位查表的两张小表,是把阿拉伯数字念出中文腔的一本进位账。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【语法算法】 上一版的炮台索敌逻辑有个隐蔽报错:炮台永远打同一个怪——即使那只怪已经死了,索敌的游标也不挪窝。追到底索敌的游…
【语法算法】 先抛一个坑:打不完的火系怪怎么办?火抗怪火打不动,换冰系技能要切装备要换面板——切完黄花菜都凉了。元素转换的答…
【语法算法】 上个月的事故复盘会上有个数字被念了三遍:四成——策划写的是"同伴陪疼四成",代码落下去成了"陪疼四十点",两只…
【语法算法】 单行代码拆解:弹射初速=-420——弹射的全部动力就这一行的负初速。负号朝上、四百二十是弹射的初速大小——踩上…
【语法算法】 上一版的减速类模块全按"乘以零点五"来写,帧率无关的版本照搬了这套写法——结果高帧率机上减速效果好,低帧率机上…
【语法算法】 先抛一个坑:怎么把散在四处的怪聚到一起打?逐个拉是笨办法,一个范围技又只能打一片——引力球的答案是一个会动的吸…