模組:Num2Chinese
外觀
可在模組:Num2Chinese/doc建立此模組的說明文件
local err = require("Module:Error")
local mapping = {
["0"] = "〇",
["1"] = "一",
["2"] = "二",
["3"] = "三",
["4"] = "四",
["5"] = "五",
["6"] = "六",
["7"] = "七",
["8"] = "八",
["9"] = "九",
["."] = "點",
["+"] = "正",
["-"] = "負",
["%"] = "厘",
[" "] = "",
[","] = "",
}
local mapping_digit = {
{[0] = "零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "百", "千"},
{[0] = "零", "壹", "貳", "叄", "肆", "伍", "陸", "柒", "捌", "玖", "拾", "佰", "仟"},
}
local units = {
"萬", "億", -- 大陆通行
"兆", "京", "垓", "秭", "穰", "溝", "澗", "正", "載", "極", -- 传统记数
-- "恆河沙", "阿僧祇", "那由他", "不可思議", "無量", "大數",
}
local units_minor = {
"分", "厘", "毫", "絲", "忽", "微", "纖", "沙", "塵", "埃", "渺", "漠", -- 传统小数
-- "模糊", "逡巡", "須臾", "瞬息", "彈指", "刹那", "六德", "虛空", "清净", "阿賴耶", "阿摩羅", "涅槃寂靜",
}
-- 将数字逐位转换,不添加单位,原样保留其他字符
local function _digits(s)
local ans = {}
for c in string.gmatch(s, ".") do
local mapped = mapping[c]
if mapped == nil then
mapped = c
end
table.insert(ans, mapped)
end
return table.concat(ans)
end
-- 将数字逐位转换,不添加单位,去除所有逗号、空格,改为小数点前后每隔len位空格分隔
local function _digits_segmented(s, len)
if type(len) ~= "number" or len <= 0 then
return _digits(s)
end
local ans = {}
if string.sub(s, 1, 1) == '-' then
table.insert(ans, '負')
s = string.sub(s, 2)
end
s = string.gsub(s, "[,%s]", "") -- filter comma, space
local pos = string.find(s, '.', 1, true)
if pos == nil then pos = #s + 1 end
local countdown = (pos - 2) % len + 2
for c in string.gmatch(s, ".") do
if c == '.' then
countdown = len + 1
else
countdown = countdown - 1
if countdown == 0 then
table.insert(ans, " ")
countdown = len
end
end
table.insert(ans, mapping[c])
end
return table.concat(ans)
end
-- 一万以内的整数,使用map对应的中文数字,ling代替大小位分隔“零”
local function _number_small(s, map, ling)
local n = tonumber(s, 10)
if n == 0 then
if ling == '〇' then
return '〇'
else
return "零"
end
end
assert(n > 0 and n < 10000, "超出范围:" .. s)
local ans = {}
if n >= 1000 then
table.insert(ans, map[math.floor(n/1000)] .. map[12])
n = n%1000
if 0 < n and n < 100 then
table.insert(ans, ling)
end
end
if n >= 100 then
table.insert(ans, map[math.floor(n/100)] .. map[11])
n = n%100
if 0 < n and n < 10 then
table.insert(ans, ling)
end
end
local tens = 0
if n >= 10 then
tens = math.floor(n/10)
if #ans ~= 0 or tens ~= 1 or string.sub(s, 1, 1) == '0' then
table.insert(ans, map[tens] .. map[10])
else
table.insert(ans, map[10])
end
n = n%10
end
if n > 0 then
table.insert(ans, map[n])
end
return table.concat(ans)
end
-- 所有实数,长度不限,可以空格、逗号分隔,len=-1采用传统小数记法,否则小数部分按位分隔;
-- daxie=1采用大写,ling代替大小位分隔“零”
local function _number(s, len, daxie, ling)
assert(type(len) == "number")
if ling == nil then ling = "零" end
local ans = {}
local map = mapping_digit[1]
if daxie == "1" then map = mapping_digit[2] end
if string.sub(s, 1, 1) == '-' then
table.insert(ans, '負')
s = string.sub(s, 2)
end
s = string.gsub(s, "[,%s]", "") -- filter comma, space
if not string.match(s,'^%d+%.?%d*$') then -- TODO 科学计数法
return err.error{ message = "非數字:" .. s }
end
local pos = string.find(s, '.', 1, true)
if pos == nil then pos = #s + 1 end
-- integer part
-- print("pos", pos)
local i_unit = math.floor((pos - 2) / 4)
for i = (pos - 2) % 4 - 2, pos - 4, 4 do -- 将小数点前数字每四位分隔
i_unit = i_unit - 1
if i <= 0 then -- first segment
-- print(i, i_unit, string.sub(s, 1, i+3))
table.insert(ans, _number_small(string.sub(s, 1, i+3), map, ling))
else
local small = string.sub(s, i, i+3)
-- print(i, i_unit, small)
if string.sub(small, 1, 1) == '0' and ans[#ans] ~= ling then
table.insert(ans, ling)
end
if small ~= "0000" then
table.insert(ans, _number_small(small, map, ling))
end
end
-- add units
if i_unit ~= -1 then
if ans[#ans] == ling then -- max unit loop
if i_unit % #units + 1 == #units then
table.insert(ans, #ans, units[#units])
end
else
table.insert(ans, units[i_unit % #units + 1])
end
end
end
if ans[#ans] == ling and pos > 2 then
table.remove(ans, #ans)
end
-- fraction
if len < 0 then -- 传统小数
local frac = string.sub(s, pos+1)
if ans[#ans] == "零" and #frac > 0 then -- 不以“零又”开头
table.remove(ans, #ans)
else
table.insert(ans, '又')
end
for i = 1, #units_minor do
if i > #frac then
break
end
local digit = string.sub(frac, i, i)
if digit ~= '0' then
table.insert(ans, map[string.byte(digit) - 48] .. units_minor[i])
elseif i == #units_minor then -- 最后一个可用单位
table.insert(ans, "零" .. units_minor[i])
end
end
if #units_minor < #frac then
frac = _digits(string.sub(frac, #units_minor))
if ling ~= '〇' then
frac = frac:gsub('〇', '零')
end
table.insert(ans, frac)
end
if ans[#ans] == '又' then -- 若无小数,移除
table.remove(ans, #ans)
end
else
local frac = _digits_segmented(string.sub(s, pos), len)
if ling ~= '〇' then
frac = frac:gsub('〇', '零')
end
table.insert(ans, frac)
end
return table.concat(ans)
end
local p = {}
function p.digits(frame)
local s = frame.args[1]
local len = tonumber(frame.args.len)
if type(len) == "number" then
return _digits_segmented(s, len)
else
return _digits(s)
end
end
function p.number(frame)
local s = frame.args[1]
local len = tonumber(frame.args.len)
if len == nil then len = 0 end
return _number(s, len, frame.args.daxie, frame.args.ling)
end
return p