×
创建新页面
在此填写您的页面标题:
我们当前在Undertale社区维基上拥有199个页面。请在上方输入您的页面名称或点击以下任意标题来开始编写页面!



Undertale社区维基
欢迎来到Undertale社区维基(*`∀´*)ノ亻,如果想要参与条目创建或编辑,请先登录

 来自ZH Wikipedia

本页面之全部或部分原来自ZH Wikipedia的Module:NumberToChinese,依 CC BY-SA 4.0 授权引入;原贡献者可以在这里看到。经过双方编者的修改,内容可能已与来源有很大差异。


local p = {}
local calc_lib = {} --用于处理科学记号
local yesno = require('Module:Yesno')
local function stringToTable(s) --字串转阵列
	local t = {}
	for i = 1, #s do --将每一个字元存入阵列
		t[i] = s:sub(i, i)
	end
	return t
end
local ClearTenOne, NoClearOne, ClearAllOne = 0, 1, 2 --清除首位1的状况,清十前1、不清1、清所有首位1
local Normal, Financial = 0, 1 --小写和大写
local Over, Ten, Hundred, Thousand, LargeStart = 10, 11, 12, 13, 14 --Over是万进的节点ID,LargeStart是万进位数中文的开始ID
local Data = { --中文数字资料
	standard = {{'〇', '一', '二', '三', '四', '五', '六', '七', '八', '九'}, {'零', '壹', '贰', '参', '肆', '伍', '陆', '柒', '捌', '玖'}},
	decimal = {{'', '十', '百', '千'}, {'', '拾', '佰', '仟'}},
	large = {'', '万', '亿', '兆', '京', '垓', '秭', '穰', '沟', '涧', '正', '载', '极', '恆河沙', '阿僧祇', '那由他', '不可思议', '无量', '大数'},
	NotANumber = "这不是一个数字", Infinity = "无穷大", NumberingZero = '〇',
	Positive = '正', Negative = '负', Point = '点', Dash = '之'
}
local largeSize = #(Data.large)

local function argsToVariable(frame) --输入参数阵列转变数
	local args = require('Module:Arguments').getArgs(frame)
	local number = args.num or args.number or args[1] or 0
	local numberType = args.b or args.daiji or args.numberType or Normal
	numberType = tonumber(numberType) or (yesno(numberType) and Financial or Normal)
	if (numberType > Financial) then
		numberType = Financial
	end
	local clearOne = args.ten or args.clearOne or ClearTenOne
	clearOne = tonumber(clearOne) or ClearTenOne
	if (clearOne > ClearAllOne) then
		clearOne = ClearAllOne
	end
	return number, numberType, clearOne
end

local function IDToChinese(id, numberType) --由上述定义可以知道ID代表意义
	if id < Over then --小于Over的id即数字本身
		return Data.standard[numberType + 1][id + 1]
	elseif id < LargeStart then --Over至LargeStart的id即为万进前的单位 (如:十、百、千)
		return Data.decimal[numberType + 1][id - Over + 1]
	elseif id < LargeStart + largeSize then --万进后的单位
		return Data.large[id - LargeStart + 1]
	else --用完后单位再进位
		return p.LargeName(id - LargeStart)
	end
end

local function LessThan10000ToID(number) --低于10000的转换,用途为中文数字是万进
	local id = {}
	table.insert(id, 0) --先丢0让千位判断较容易
	local numberArr = stringToTable(number .. '')
	for i = 1, 4 do
		repeat
			if id[#id] == 0 and numberArr[i] == '0' then --当连续0的时候直接跳出
				break
			elseif id[#id] ~= 0 and numberArr[i] == '0' then --当上一位不是0,目前是0时单纯加入0不加入千百十的ID
				table.insert(id, 0)
				break
			end
			table.insert(id, tonumber(numberArr[i])) --其他状况就照一般说法位数大小和位数名称
			table.insert(id, Thousand - i + 1)
		until true
	end
	if numberArr[1] ~= '0' then --如果千位不为0把首位0去除
		table.remove(id, 1)
	end
	if #id > 1 then --只有在0000的时候ID个数才会只有1
		id[#id] = Over
	else
		table.insert(id, Over)
	end
	return id
end
local function FrontNumberToChinese(number, numberType, clearOne) --这边的前数指的是大数,但实际英文并非如此称呼,只是要让名称浅显易懂
	number = '0000' .. number --把位数补到4的倍数,先补4个0在清除多馀的,下列程式本身有去除首位0的功能,若首位0会自动消除
	number = number:sub(#number % 4 + 1)
	local numberLargeLength = #number / 4
	local id = {}
	table.insert(id, 0) --先补0让最高的万进位数容易判断
	for i = 1, numberLargeLength do
		repeat
			local data = LessThan10000ToID(number:sub(i * 4 - 3, i * 4 - 3 + 4))
			if id[#id] == 0 and #data == 2 then --如果上一个万进位数为0,目前也为0(0000的ID个数最后有加上Over,所以为2),则直接跳出
				break
			elseif id[#id] ~= 0 and #data == 2 then --如果上一个万进位数不为0,目前为0,补0跳出
				table.insert(id, 0)
				break
			elseif id[#id] == 0 and data[1] == 0 then --如果上一个万进位数为0,目前的万进位数首位为0,清除掉首位0
				table.remove(data, 1)
			end
			for j = 1, #data do --将目前的万进位数加入到ID阵列裡
				table.insert(id, data[j])
			end
			id[#id] = LargeStart + numberLargeLength - i --最后补上万进位数的ID
		until true
	end
	if #id == 1 then --ID长度为1代表答案是0
		return IDToChinese(0, numberType)
	end
	table.remove(id, 1) --清除首位0
	table.remove(id, #id) --清除最后一个元素,有可能是万进位数的第1个空格ID,也有可能是末位0
	if clearOne == ClearTenOne and id[1] == 1 and id[2] == Ten then --如果是选择清十前1,则必须首位要是一十才清1
		table.remove(id, 1)
	elseif clearOne == ClearAllOne and id[1] == 1 and #id > 1 then --不管如何首位1都清,但是单独1不清1
		table.remove(id, 1)
	end
	local chinese = '' --转成中文回传
	for i = 1, #id do
		chinese = chinese .. IDToChinese(id[i], numberType)
	end
	return chinese
end
local function BackNumberToChinese(number, numberType) --这边的后数指的是小数,但实际英文并非如此称呼,只是要让名称浅显易懂
	local chinese = ''
	local numberLength = #number
	local numberArr = stringToTable(number .. '')
	while numberLength > 0 do --寻找末位0的个数,并扣除个数
		if numberArr[numberLength] ~= '0' then
			break
		else
			numberLength = numberLength - 1
		end
	end
	for i = 1, numberLength do --直接转成中文回传
		chinese = chinese .. IDToChinese(tonumber(numberArr[i]), numberType)
	end
	return chinese
end

local function NumberToChinese(number, numberType, clearOne) --转中文
	number = tostring(number) --转成字串进行作业
	Data.standard[Normal + 1][0 + 1] = Data.standard[Financial + 1][0 + 1] --一般数量时大小写的0皆使用“零”,然后Lua的阵列从1开始
	local chinese = ''
	number = mw.ustring.gsub(mw.text.trim(number), "%s+", '') --移除空白字元
	local check_char = mw.ustring.sub(number, 1, 1) --读取第一个字以便判断正负号
	if check_char == '+' then --有正号才显示正
		chinese = chinese .. Data.Positive
		number = mw.ustring.sub(number, 2, #number)
	elseif check_char == '-' or check_char == '−' then
		chinese = chinese .. Data.Negative
		number = mw.ustring.sub(number, 2, #number) --在此阶段仍有可能有非ASCII字元,故使用mw.ustring
	end
	if number == '∞' or number:lower()=='inf' then
		chinese = chinese .. Data.Infinity
		return chinese
	end
	if number:lower()=='nan' then --nan会让tonumber通过,故提前跳出
		chinese = ((chinese == '') and Data.Positive or chinese) .. Data.NotANumber
		return chinese
	end
	if number:find("[%d%.][Ee][%d%+%-]") then --如果有科学记号
		if type(calc_lib.scientific2number) ~= type(function()end) then calc_lib = require("Module:Complex_Number/Calculate")end
		number = calc_lib.scientific2number(number) --将科学记号转换成一般数字
	end
	if number:find("[Ee]") then return Data.NotANumber end --如果转换完还有e字元代表非法的科学记号
	
	local frontNumber, backNumber = '', ''
	local point = number:find('%.')
	if point == nil then --如果小数点不存在代表只有大数
		frontNumber = number
	else --撷取大数部分及小数部分
		frontNumber = number:sub(1, point - 1)
		backNumber = number:sub(point + 1, #number)
	end
	if tonumber('0' .. frontNumber) == nil or --lua空字串判断为非数值,所以一定要至少补1个0
		tonumber('0.' .. backNumber .. '0') == nil then --如果这裡有小数点(变为超过1个小数点)则会误判为数字,所以一定要补1个'0.'
		return Data.NotANumber
	end
	chinese = chinese .. FrontNumberToChinese(frontNumber, numberType, clearOne)
	local success, backChinese = pcall(BackNumberToChinese, backNumber, numberType)
	if not success then return Data.NotANumber end --遗漏的事项会让转换的过程出错;若出错,则假定其并非一个数字
	if backChinese ~= '' then --如果小数为空字串则连小数点都不加入中文字串
		chinese = chinese .. Data.Point .. backChinese
	end
	return chinese
end
local function NumberToChineseNumbering(number, numberType) --编号用途,编号只叙述数字不叙述进位系统,且有多个“点”和“之”
	number = tostring(number) --转成字串进行作业
	Data.standard[Normal + 1][0 + 1] = Data.NumberingZero --一般编号时小写的0使用“〇”,然后Lua的阵列从1开始
	local chinese = ''
	local numberLength = #number
	local numberArr = stringToTable(number .. '')
	for i = 1, numberLength do
		if numberArr[i] == '.' then --小数点在编号时中文通常念作“点”
			chinese = chinese .. Data.Point
		elseif numberArr[i] == '-' then --减号在编号时中文通常念作“之”
			chinese = chinese .. Data.Dash
		elseif tonumber(numberArr[i]) ~= nil then
			chinese = chinese .. IDToChinese(tonumber(numberArr[i]), numberType)
		end
	end
	return chinese
end
local function loadInvChineseData()
	Data.number={['叄'] = 3, ['叁'] = 3, ['䦉'] = 4, ['两'] = 2, ['两'] = 2}
	Data.standard[3] = {'零', '壹', '贰', '参', '肆', '伍', '陆', '柒', '捌', '玖'}
	Data.standard[4] = {'洞', '么', '两', '叄', '肆', '伍', '六', '拐', '八', '勾'}
	Data.otherNumber = {
		['廿'] = {addAt = 2, value = 2}, ['念'] = {addAt = 2, value = 2},
		['卅'] = {addAt = 2, value = 3},
		['卌'] = {addAt = 2, value = 4},
		['皕'] = {addAt = 3, value = 2},
	}
	local all_large = {Data.large, {'', '万', '亿', '兆', '京', '垓', '秭', '穰', '沟', '涧', '正', '载', '极', '恒河沙', '阿僧祇', '那由他', '不可思议', '无量', '大数'}}
	Data.less1000={}
	Data.largeTable = {['$'] = 1, [Data.Point] = 1, ['点'] = 1}
	for i=1,#(Data.standard) do --建表以利数字查表
		for j=1,#(Data.standard[i]) do Data.number[Data.standard[i][j]] = j - 1 end
	end
	for i=0,9 do Data.number[tostring(i)] = i end
	for i=1,#(Data.decimal) do --建表以利单位查表
		for j=1,#(Data.decimal[i]) do Data.less1000[Data.decimal[i][j]] = j end
	end
	Data.maxLarge = 0
	for i=1,#all_large do --建表以利大数量词查表
		for j=1,#(all_large[i]) do 
			if mw.ustring.len(all_large[i][j]) > Data.maxLarge then Data.maxLarge = mw.ustring.len(all_large[i][j]) end
			Data.largeTable[all_large[i][j]] = j 
		end
	end
end
local function FindChineseToken(str, i)
	local it = mw.ustring.sub(str, i, i)
	for j=1,Data.maxLarge do --对照表中所有可能长度的子字串
		local lit = mw.ustring.sub(str, i, i + j)
		if Data.largeTable[lit] then --如果查表命中
			it = lit
			i = i + j
			break --表示已经找到数字词彙,跳出
		end
	end
	return ((it=='')and'null'or it), i --查不到返回'null'以利判断
end
local function LoadChineseToken(input_str)
	local str = input_str..'$' --加入结尾符号
	local token_list = {}
	local i, strlen, it, uit = 1, mw.ustring.len(str), '', ''
	local buffer, buffer_flag = {0,0,0,0}, false --每四位数一个缓衝区
	while i <= strlen do --逐一数字词彙查找
		it, i = FindChineseToken(str, i)
		uit = it
		if not(Data.number[it] or Data.otherNumber[it] or Data.less1000[it] or Data.largeTable[it])then
			if it~='null'then error(mw.ustring.format("未知的字元 '%s'", it), 2)end
		end
		if Data.less1000[it] then --单独的数字量词(十、百、千),当作省略了 "一" 的表示方式
			buffer[Data.less1000[it]] = 1
			local check_error, err_i = FindChineseToken(str, i + 1)
			if Data.less1000[check_error] then
				error(mw.ustring.format("错误的用法 '%s'", it..check_error), 2)
			end
			buffer_flag = true
		elseif Data.otherNumber[it] then
			
			buffer[Data.otherNumber[it].addAt] = Data.otherNumber[it].value
			local check_error, err_i = FindChineseToken(str, i + 1)
			if Data.less1000[check_error] then
				error(mw.ustring.format("错误的用法 '%s'", it..check_error), 2)
			end
			buffer_flag = true
		elseif Data.number[it] then --读到数字
			if Data.number[it] ~= 0 then
				i = i + 1
				uit, i = FindChineseToken(str, i) --检查下一个数字词彙是否是数字量词
				if not(Data.number[uit] or Data.less1000[uit] or Data.largeTable[uit])then
					if uit~='null'then error(mw.ustring.format("未知的字元 '%s'", uit), 2)end
				end
				local check_error, err_i = FindChineseToken(str, i + 1)
				if Data.less1000[check_error] then
					error(mw.ustring.format("错误的用法 '%s'", uit..check_error), 2)
				end
				if Data.less1000[uit] then --数字 + 数字量词 在该位数做纪录
					buffer[Data.less1000[uit]] = Data.number[it]
					buffer_flag = true
				elseif Data.number[uit] then
					error(mw.ustring.format("错误的用法 '%s'", it..uit), 2)
				else --数字 + 大数量词 表示本缓衝区(小于1000的子字串)已读完,储存token,更新缓衝区
					buffer[1] = Data.number[it]
					table.insert(token_list, buffer)
					buffer = {0,0,0,0}
					buffer_flag = false
				end
			else
				local check_next, next_i = FindChineseToken(str, i + 1)
				if (Data.largeTable[(check_next == '')and'null'or check_next]) and (not buffer_flag) then
					buffer[1] = 1 --省略1的写法
					buffer_flag = true
				end
			end
		end
		if Data.largeTable[(uit == '')and'null'or uit] then --读到的内容是大数量词
			if buffer_flag then --检查是否有未储存的缓衝区
				table.insert(token_list, buffer) --如有未储存的缓衝区,储存token,更新缓衝区
				buffer = {0,0,0,0}
				buffer_flag = false
			end
			table.insert(token_list, Data.largeTable[uit]) --储存大数量词id到token表
		end
		i = i + 1
	end
	if buffer_flag then --还有未储存的缓衝区表示有万未以下的数
		table.insert(token_list, buffer) --储存token
	end
	if type(token_list[1]) ~= type({}) then --最高位如果本身是大数量词id,当作省略了 "一" 的表示方式
		table.insert(token_list, 1, {1,0,0,0})
	end
	
	local loop_count, maxId = #token_list, 0 --准备整理token
	local result = {}
	i, buffer_flag = 1, false
	local last_it = {}

	while i <= loop_count do
		it = token_list[i]
		if type(it) == type(0) then --如果是大数量词id
			it = it - 1
			uit = token_list[i + 1] --如果是连续的大数量词id,将之值相加 (lua阵列以1起始所以减1)
			while type(uit) == type(0) and i <= loop_count do
				it = it + uit - 1
				i = i + 1
				uit = token_list[i + 1]
			end
			it = it + 1 --加回1变成id
			if it > maxId then maxId = it end --纪录出现过的最大id
			result[it] = last_it --在这id位置下储存解析完的一组4个数位
			buffer_flag = false
		else
			last_it = it
			buffer_flag = true
		end
		i = i + 1
	end
	if buffer_flag then --剩馀万位以下的一组4个数位
		result[1] = last_it 
		if 1 > maxId then maxId = 1 end
	end
	return result, maxId
end
--{{#invoke:NumberToChinese|Chinese_To_Number}}
function p.Chinese_To_Number(frame)
	local input_str = ((type(frame)==type({})) and (frame.args or frame) or {tostring(frame) or ""})[1]
	loadInvChineseData() --建立中文数字查表
	local str = mw.ustring.gsub(mw.text.trim(input_str), "%s", '')
	if str == '' then return '' end
	local number_pattern = Data.Point..'点%.'
	local large_pattern = ''
	local number, sign = '', ''

	local check_char = mw.ustring.sub(str, 1, 1) --读取第一个字以便判断正负号
	if (check_char == Data.Positive or check_char == '+') and not (str == Data.Positive) then --有正号才显示正
		sign = '+'
		str = mw.ustring.sub(str, 2, -1)
	elseif check_char == Data.Negative or check_char == '负' or check_char == '-' or check_char == '−' then
		sign = '-'
		str = mw.ustring.sub(str, 2, -1) 
	elseif check_char == "零" then --处理如 "零下三" 的表示方法
		local check_char = mw.ustring.sub(str..'$$', 2, 2)
		if check_char == "上" then
			sign = '+'
			str = mw.ustring.sub(str, 3, -1) 
		elseif check_char == "下" then
			sign = '-'
			str = mw.ustring.sub(str, 3, -1) 
		end
		if str == '' then return "0" end
	end
	if str == '' then return '' end --清完正负号为空表示是空字串
	if str == p.NotANumber() then return sign .. 'nan' end
	if mw.ustring.match(str,'[无无][穷穷限]') then return sign .. 'inf' end

	for name,index in pairs(Data.number) do if type(name) == type("string") then number_pattern = number_pattern .. name end end
	for name,index in pairs(Data.less1000) do if type(name) == type("string") then large_pattern = large_pattern .. name end end
	for name,index in pairs(Data.largeTable) do if type(name) == type("string") then large_pattern = large_pattern .. name end end
	for name,index in pairs(Data.otherNumber) do if type(name) == type("string") then large_pattern = large_pattern .. name end end
	if mw.ustring.match(str, "^["..number_pattern.."]+$") then --全部都是数字直接输出
		for i=1,mw.ustring.len(str) do
			local it = mw.ustring.sub(str,i,i)
			if it == '点' or it == Data.Point then
				number = number .. '.'
			else
				number = number .. (Data.number[it] or 0)
			end
		end
		return sign .. number
	end
	local find_invalid, find_invalid_end = mw.ustring.find(str, "[^"..number_pattern..large_pattern.."]+")
	if find_invalid then
		error(mw.ustring.format("输入的内容含有无效的词彙 '%s'", mw.ustring.sub(str, find_invalid, find_invalid_end)), 2)
	end
	local point = mw.ustring.find(str, "[点"..Data.Point.."%.]")
	local frontStr, backStr = '', ''
	if point == nil then --如果小数点不存在代表只有大数
		frontStr = str
	else --撷取大数部分及小数部分
		frontStr = mw.ustring.sub(str, 1, point - 1)
		backStr = mw.ustring.sub(str, point + 1, -1)
	end
	
	local tokens, token_len = LoadChineseToken(frontStr)
	local zero_flag = true

	for i=token_len,1,-1 do
		for j = 4,1,-1 do
			local it = (tokens[i]or{})[j] or 0
			if zero_flag then
				if it ~= 0 then 
					zero_flag = false
					number = number .. it
				end
			else
				number = number .. it
			end
		end
	end
	if backStr ~= '' then
		number = number .. '.'
		for i=1,mw.ustring.len(backStr) do
			local it = mw.ustring.sub(backStr,i,i)
			if Data.number[it] then
				number = number .. Data.number[it]
			end
		end
	end
	return sign .. number
end
function p.NotANumber() return Data.NotANumber end --用于模板内字串判断
function p.Infinity() return Data.Infinity end
function p.LargeName(frame) 
	local index = ((type(frame)==type({})) and (frame.args or frame) or {tonumber(tostring(frame)) or 1})[1]
	local overlying, quotient = '', index
	local remainder = quotient % (largeSize - 1)
	local largecount = (quotient - remainder) / (largeSize - 1)
	overlying = Data.large[remainder + 1]
	for i = 1, largecount do
		overlying = overlying .. Data.large[largeSize]
	end
	return overlying
end
--{{#invoke:NumberToChinese|Number_to_Chinese}}
function p.Number_to_Chinese(frame) --进位系统的中文数字
	return NumberToChinese(argsToVariable(frame))
end
function p.Number_to____(frame) --编号的中文数字
	return NumberToChineseNumbering(argsToVariable(frame))
end
--{{#invoke:NumberToChinese|Number_to_Numbering}}
function p.Number_to_Numbering(frame) --编号的中文数字
	return NumberToChineseNumbering(argsToVariable(frame))
end
--转给其他模组使用,但为了避免影响其他运作中模板,改为函数呼叫
--因此此函数命名遵照[[Wikipedia:Lua代码风格#命名常规]]
function p._numberToChinese(number, numberType, clearOne)
	return NumberToChinese(tostring(tostring(number)), numberType or 0, clearOne or 0) --转中文
end
function p._numberToNumbering(number, numberType)
	return NumberToChinese(tostring(tostring(number)), numberType or 0) --转中文
end

return p