【语法算法】
先抛一个坑:混在文字里的数字,怎么批量捞出来?"战报:击杀12只,伤害3450,耗时96秒"——人眼一眼扫出三个数,机器要逐个字符认。提取数字的写法就是一根渔网:模式匹配逐段过滤,数字段一个个捞出来排好,文字留在原地。 数字浮出成列——一网下去水和鱼各自归位。这篇把整套写法拆开:配置表、模式匹配、逐段捞取、浮出排列、定时器、调试按钮,一段一段照抄能跑。
一、效果演示:模式匹配的逐段捞取
演示场一句战报躺在格子里。点「提取」:句中的数字逐段浮到下方的绿框里排成一列——12、3450、96,文字原地不动。点「换一句」:战报换一句新的,数字的位置、个数全变——网还是那张网,捞上来的鱼不同。演示里试两笔账:三句不同格式的战报用同一个提取器——网不挑鱼,模式匹配挑的只是数字的形状。提取数字教的是捞取:文字是水,数字是鱼,模式是网眼。
flowchart TD
A[逐字符扫描] --> B[遇数字开始收网]
B --> C[连续数字并成一段]
C --> D{下一位还是数字}
D -- 是 --> C
D -- 否 --> E[一段捞出入列]
E --> F{扫到串尾}
F -- 否 --> B
F -- 是 --> G[全部入列 输出]
fx-extractnum
二、底层原理:一次模式过滤加一次逐段捞取
模块的机关是一对搭档。模式过滤:扫描的每一拍判断字符是不是数字——数字开网、非数字收网,连续的数字段并成一段,段的边界就是网的收放。逐段捞取:收网的每一段入列排好——捞出的段保持原文里的先后次序,次序就是战报的叙事顺序。和手写下标的分野在"模式匹配不数下标":手写下标的版本每改一句战报都要重新对格子,模式匹配只认形状不认位置——句子怎么变,网眼不变。
提取为什么用逐字符扫不用查表?查表的版本要预先登记每句话的数字位置——位置是句子的属性不是数字的属性,逐字符扫认的是数字的形状,句子换了网照用,这才是通用件的写法。
三、核心代码:完整模块(上·骨架)
-- @file ExtractNum.lua
-- 提取数字 —— 模式匹配的逐段捞取
local ExtractNum = {}
local CONST = {
LINES = {
"战报:击杀12只,伤害3450,耗时96秒",
"补给:金币500枚,宝石8颗,药水3瓶",
"战况:防线9处,援军120人,伤亡45",
},
AUTOINC_BASE = 1166000,
}
local _lineIdx = 0
local _autoInc = 0
local function GenID(prefix)
_autoInc = _autoInc + 1
return string.format("%s_%d", prefix or "EXN",
CONST.AUTOINC_BASE + _autoInc)
end
local function ShowTip(msg)
if msg and msg ~= "" then SL:ShowSystemTips(msg) end
end
function ExtractNum.Line()
return CONST.LINES[(_lineIdx % #CONST.LINES) + 1]
end
function ExtractNum.NextLine()
_lineIdx = _lineIdx + 1
end
四、核心代码:完整模块(下·逐段捞取与入列)
-- 逐段捞取:模式匹配一把捞全句的数字
function ExtractNum.Pick(line)
local out = {}
for num in line:gmatch("%d+") do
out[#out + 1] = num
end
return out
end
function ExtractNum.Extract()
ExtractNum.NextLine()
local line = ExtractNum.Line()
return ExtractNum.Pick(line)
end
SL:ScheduleOnce(function()
SL:BindDebugButton("提取一句", function()
local nums = ExtractNum.Extract()
ShowTip("捞到" .. #nums .. "个数——逐段浮出")
end)
ShowTip("技能已加载: 提取数字 (调试按钮触发)")
end, 1.0)
function ExtractNum.Unload()
_lineIdx = 0
end
return ExtractNum
五、机制问答
问:负数的负号能捞进来吗?
答:默认捞不进——网眼只认数字字符,负号进不了段,要捞负数就把负号并进网眼再加一层判断。
问:小数点会被当成数字段的一部分吗?
答:默认不会——点不是数字字符,三点一四会被捞成两段"3"和"14",要捞小数得把点加进网眼。
问:提取的段要转成数值吗?
答:看用途——只展示就保持字符串,要参与计算就转成数值,两种形态各有岗位。
问:连续的数字中间夹了空格算几段?
答:算两段——空格不是数字字符,段的边界就是网收放的地方,空格也是合法的边界。
问:一句话里数字很多会漏吗?
答:不会——网从串头扫到串尾,一段不漏,漏不漏和个数无关,和网眼的口径有关。
六、调参与实战怎么用
第四笔是网眼的口径:纯数字的网眼是最基础的口径——负数、小数、千分位逗号各有各的加宽方式,网眼按最常来的句式定,不贪全。第三笔是次序的保持:捞出的段按原文先后入列——次序是战报叙事的骨架,打乱的提取结果没法和文字对账。第二笔是形态的选择:字符串形态和数值形态各有岗位——展示用串、计算用数,转换的时机放在消费端。第一笔是模式的复用:网是通用的不挑句子——战报、补给、战况三句共用一张网,模式的通用性就是提取器的价值。常见坑三个:网眼没含负号漏捞负数;小数点断段导致金额拦腰折断;提取的串直接参与算术报类型错误。
实战里提取数字是"从文本里要数据"的通用件:战报解析、聊天指令、日志抽查,全是"模式捞段"的同一副骨架。组里的约定是:凡是从文本里抽数据一律走模式匹配不手写下标——下标是句子的私产,模式是所有句子公用的网。这篇的模块照抄能跑,改的就是那几句战报。提取数字还有一个隐藏的好处——把数据的展示和数据的值拆成两层,文案随便改数字随便调,网眼只认数字的形状,句式翻新三遍网眼一字不动。这也是为什么解析类的模块写好了能用到天荒地老——它认的是数据的形状不是数据的包装,包装怎么换都行,形状不变它就永远管用。
实战里提取数字还有一个进阶用法——把提取的结果和原文的位置一起返回,不只是返回数字本身。位置信息在很多场景有用:高亮原文里的数字、点击数字跳转详情、数字变动时只刷新数字部分不重排整句。这些进阶用法共享同一个网眼,只是捞上来的东西多带了一个位置坐标。网眼的口径不变,捞上来的账多了位置这一列,下游的用法就宽了一整片。\n\n写完留一句给做解析系的同学:提取数字卖的是"文字是水数字是鱼"——那张逐段收放的模式网和浮出成列的数字,是从文本的河里捞数据的一张渔网。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
【语法算法】 上一版的炮台索敌逻辑有个隐蔽报错:炮台永远打同一个怪——即使那只怪已经死了,索敌的游标也不挪窝。追到底索敌的游…
【语法算法】 先抛一个坑:打不完的火系怪怎么办?火抗怪火打不动,换冰系技能要切装备要换面板——切完黄花菜都凉了。元素转换的答…
【语法算法】 上个月的事故复盘会上有个数字被念了三遍:四成——策划写的是"同伴陪疼四成",代码落下去成了"陪疼四十点",两只…
【语法算法】 单行代码拆解:弹射初速=-420——弹射的全部动力就这一行的负初速。负号朝上、四百二十是弹射的初速大小——踩上…
【语法算法】 上一版的减速类模块全按"乘以零点五"来写,帧率无关的版本照搬了这套写法——结果高帧率机上减速效果好,低帧率机上…
【语法算法】 先抛一个坑:怎么把散在四处的怪聚到一起打?逐个拉是笨办法,一个范围技又只能打一片——引力球的答案是一个会动的吸…