Modul:Formattal
Udseende
![]() | Dette Lua-modul bruges på ca. 8.500 sider, og derfor vil ændringer blive bemærket af mange. Vær venlig at afprøve enhver ændring i modulets /sandkasse eller /test-undersider, eller i din egen module sandkasse. Du bør også overveje at diskutere ændringer på diskussionssiden før du implementerer dem. |
Utilstrækkelig vejledning Dette modul bør have en (bedre) vejledning, helst med eksempler på anvendelse. Hvis andre moduler er nyere og/eller bedre, bør der henvises til dem. |
Brug
{{#invoke:Formattal|function_name}}
local p = {}
p.angivFejlMedRoedStjerneOgFejlbeskrivelse = false
-- Angiver om fejl skal angives med en rød stjerne efter tallet, med
-- en fejlbeskrivelse som viser sig, når man holder musen over stjernen.
-- Lægges i p for at unittests kan tjekke værdien.
local fejlkategorikode = "[[Kategori:Sider med tal hvis format ikke kendes af formattal]]"
--[[
formattal(frame)
Formaterer et tal med højst to decimaler på dansk. Det oprindelige tal kan
enten bruge punktum som decimaladskiller og komma som tusindadskiller
("123,456,789.12"), eller komma som decimaladskiller og punktum som
tusindadskiller ("123.456.789,12").
Uanset hvordan tallet var formateret i input, formateres det i output på dansk
vis med punktum som tusindadskiller og komma som decimaladskiller.
Fortegn ("-" eller "+") håndteres også, og der kan endda være et vilkårligt
antal mellemrum mellem fortegnet og resten af tallet, selv om dette er en
ukorrekt skrivemåde; dette rettes i output ved at disse mellemrum fjernes.
Eksempler (se Modul:Formattal/testcases for langt flere eksempler):
Opfattes som 6-cifret heltal, da tallet ikke må have mere end to decimaler:
formattal("123456") = "123.456"
formattal("123,456") = "123.456"
formattal("123.456") = "123.456"
Opfattes som 5-cifret tal med 2 decimaler:
formattal("123,45) = "123,45"
formattal("123.45") = "123,45"
Giver fejl for ukendt format (tusindadskiller og decimaltegn er ens):
formattal("123.456.7") = "123.456.7"
Giver fejl for ukendt format (bruger forskellige tusindadskillere eller
har for mange decimaler):
formattal("123.456,789") = "123.456,789"
Alle HTML-kommentarer og visse blanktegn i tallet ignores; andre uvedkommende
tegn vil give fejl.
Ved fejl returneres tallet uændret samtidig med at artiklen placeres i
fejlkategorien "Sider med tal hvis format ikke kendes af formattal".
]]
function p.formattal (frame)
local originaltInputEllerTom = (frame.args[1] or "")
return p.formaterIndledendeTalPaaDansk(
originaltInputEllerTom,
p.angivFejlMedRoedStjerneOgFejlbeskrivelse)
end
--[[
p.formaterIndledendeTalPaaDansk()
Funktion som indeholder hele funktionaliteten på "ren" form (uden
frame.args[1]), for at gøre unittests nemmere.
]]
function p.formaterIndledendeTalPaaDansk(originaltInputEllerTom,
angivFejlMedRoedStjerneOgFejlbeskrivelse)
-- Fjern HTML-kommentarer og ydre blanktegn.
local inputUdenEventuelleHtmlKommentarerOgBlanktegn
= p.fjernHtmlkommentarerOgYdreBlanktegn(originaltInputEllerTom)
-- Opdel i inputtal (fx "-1.234.553,43" eller "")
-- og inputrest (fx "<ref>…</ref>" eller "").
local inputtalEllerTom, inputrest
= p.opdelInputITalOgRest(inputUdenEventuelleHtmlKommentarerOgBlanktegn)
-- Fortolk tallet, hvis der er et, og dan det færdige output, inkl. evt.
-- fejlhåndtering.
local faerdigtOutput = p.fortolkTalOgDanFaerdigtOutput(inputtalEllerTom,
inputrest, angivFejlMedRoedStjerneOgFejlbeskrivelse)
return faerdigtOutput
end
--[[
p.fortolkTalOgDanFaerdigtOutput()
Fortolk tallet, hvis der er et, og dan det færdige output, inkl. evt.
fejlhåndtering. Hvis der ikke er et tal, så returner inputrest uændret.
]]
function p.fortolkTalOgDanFaerdigtOutput(inputtalEllerTom, inputrest,
angivFejlMedRoedStjerneOgFejlbeskrivelse)
local faerdigtOutput
if inputtalEllerTom == "" then
-- Taldelen er tom.
-- Dette er en gyldig situation, hvor output så skal være
-- identisk med input. Det antages dog, at det er okay at
-- HTML-kommentarer og ydre blanktegn stadig fjernes.
-- Jf. https://da.wikipedia.org/wiki/Brugerdiskussion:Jhertel#Modul_formattal_og_tomt_input
faerdigtOutput = inputrest
-- inputrest udgør det hele, da inputtalEllerTom er tom.
else
local inputtal = inputtalEllerTom -- Den kan ikke længere være tom.
-- Opdel tallet (inputtal) i enkeltdele.
-- Dette kan fejle, og i så fald vil fejltekstEllerNil være ikke-nil og
-- indeholde en fejlbeskrivelse.
local eventueltFortegnEllerTom, heltalsdelUdenTusindadskillere,
decimalerEllerNil, fejltekstEllerNil = p.opdelTal(inputtal)
-- Returner output (enten et formateret tal eller fejl).
if not fejltekstEllerNil then
-- Succes. Tallet er fuldt fortolket og opdelt i enkeltdele.
-- Dan et færdigt dansk formateret tal.
local outputtal
= p.danDanskOutputtal(
eventueltFortegnEllerTom,
heltalsdelUdenTusindadskillere,
decimalerEllerNil)
faerdigtOutput = outputtal .. inputrest
else
-- Fejlsituation.
-- FejltekstEllerNil kan ikke længere være nil. Angiv dette klart
-- som et værn mod bugs.
local fejltekst = fejltekstEllerNil
faerdigtOutput = p.danSamletFejlreturstreng(
inputtal,
inputrest,
fejltekst,
angivFejlMedRoedStjerneOgFejlbeskrivelse)
end
end
return faerdigtOutput
end
function p.opdelInputITalOgRest(s)
-- Opdel i inputtal (fx "-1.234.553,43") og inputrest (fx "<ref>…</ref>" eller "").
-- Mønsteret er designet, så det ikke kan fejle; hverken inputtalEllerTom
-- eller inputrest kan derfor blive nil.
local inputtalEllerTom, inputrest = s:match("^([+-]? *[%d,.]*)(.*)$")
return inputtalEllerTom, inputrest -- Ingen af disse kan være nil.
end
function p.fjernHtmlkommentarerOgYdreBlanktegn(s)
-- Fjern HTML-kommentarer.
local sUdenEventuelleHtmlKommentarer
= string.gsub(s, "<!%-%-.-%-%->", "")
-- Fjern blanktegn i enderne
local sUdenEventuelleHtmlKommentarerOgYdreBlanktegn
= mw.text.trim(sUdenEventuelleHtmlKommentarer)
return sUdenEventuelleHtmlKommentarerOgYdreBlanktegn
end
function p.danDanskOutputtal(eventueltFortegnEllerTom,
heltalsdelUdenTusindadskillere, decimalerEllerNil)
local heltalsdelMedDanskeTusindadskillere
= p.indsaetTusindadskillere(heltalsdelUdenTusindadskillere)
-- Giver fx "123.456.789" eller "123".
local danskDecimaldel
= p.danDanskDecimaldel(decimalerEllerNil) -- Giver fx ",12", ",6" eller "".
local samletTal
= eventueltFortegnEllerTom
.. heltalsdelMedDanskeTusindadskillere
.. danskDecimaldel
return samletTal
end
function p.danDanskDecimaldel(decimalerEllerNil)
if decimalerEllerNil then
return "," .. decimalerEllerNil
else
return ""
end
end
function p.fejlangivelseskode(fejltekst, angivFejlMedRoedStjerneOgFejlbeskrivelse)
if angivFejlMedRoedStjerneOgFejlbeskrivelse then
return '<sup><span style="color:red" title="Skabelonen Formattal kan ikke konvertere dette tal: ' .. fejltekst .. '">*</span></sup>'
else
return ""
end
end
function p.danSamletFejlreturstreng(inputtal, inputrest, fejltekst,
angivFejlMedRoedStjerneOgFejlbeskrivelse)
return
inputtal
.. p.fejlangivelseskode(fejltekst, angivFejlMedRoedStjerneOgFejlbeskrivelse)
.. inputrest
.. fejlkategorikode
end
--[[
p.opdelTal()
Returnerer
eventueltFortegnEllerTom, heltalsdelUdenTusindadskillere, decimaler, fejltekstEllerNil.
Hvis fejltekstEllerNil ikke er nil, gik det godt. Ellers indeholder fejltekstEllerNil en
beskrivelse af fejlen.
Decimaler er nil, hvis der ingen decimaler er.
]]
function p.opdelTal(inputtal)
local fejltekstEllerNil = nil
local heltalsdelUdenTusindadskillere = nil
-- Fjern et evt. fortegn på første position og husk det.
local eventueltFortegnEllerTom, inputtaldelUdenEventueltFortegn
= p.opsplitIFortegnOgRest(inputtal)
-- Prøv at matche et kommatal med 1 eller 2 decimaler.
local heltalsdelMedEventuelleTusindadskillere, decimaladskiller, decimaler
= inputtaldelUdenEventueltFortegn:match("^([%d,.]-)([,.])(%d%d?)$")
-- Afgør om der var et match.
-- Hvis ikke der er et match, vil både heltalsdel, decimaladskiller
-- og decimaler være nil.
local derVarEtMatch = (heltalsdelMedEventuelleTusindadskillere ~= nil)
if derVarEtMatch then
-- Der var et match: Tallet er et kommatal med 1 eller 2 decimaler.
-- Bestemt tusindadskillertegnet.
-- Bemærk at decimaladskiller logisk kun kan være "." eller ","
-- pga. mønsteret ovenfor.
local tusindadskillertegnIInput = p.modsatAdskillertegn(decimaladskiller)
-- Fjern alle tusindadskillertegn fra heltalsdel.
heltalsdelUdenTusindadskillere
= p.removeAll(heltalsdelMedEventuelleTusindadskillere, "[" .. tusindadskillertegnIInput .. "]")
else
-- Der var ikke et match. Hvis tallet er på korrekt form, er det et heltal.
local heltalsdelMedEventuellePunktumtusindadskillere
= inputtaldelUdenEventueltFortegn:match("^([%d.]+)$")
if heltalsdelMedEventuellePunktumtusindadskillere then
heltalsdelUdenTusindadskillere
= p.removeAll(heltalsdelMedEventuellePunktumtusindadskillere, "%.")
else
local heltalsdelMedEventuelleKommatusindadskillere
= inputtaldelUdenEventueltFortegn:match("^([%d,]+)$")
if heltalsdelMedEventuelleKommatusindadskillere then
heltalsdelUdenTusindadskillere
= p.removeAll(heltalsdelMedEventuelleKommatusindadskillere, ",")
else
-- Angiv fejl.
heltalsdelUdenTusindadskillere = nil
if inputtal == "" then
fejltekstEllerNil = "Tallet er tomt."
else
fejltekstEllerNil = "Kunne ikke fortolke tallet '" .. inputtal .. "'."
end
end
end
end
if not fejltekstEllerNil then
-- Håndter situationer som ".12" = 0,12.
if heltalsdelUdenTusindadskillere == "" then
heltalsdelUdenTusindadskillere = "0"
end
-- Tjek for ugyldige tusindadskillere.
if heltalsdelUdenTusindadskillere:match("%D") then
-- Fejlsituation: Heltalsdelen uden tusindadskillere indeholder
-- mindst ét ikke-ciffer (%D), som kun kan være en forkert
-- tusindadskiller.
fejltekstEllerNil = "Heltalsdelen '" .. heltalsdelUdenTusindadskillere .. "' indeholder ugyldige tusindadskillere; hele tallet er '" .. inputtal .. "'."
eventueltFortegnEllerTom = ""
heltalsdelUdenTusindadskillere = nil
decimaler = nil
end
end
return eventueltFortegnEllerTom, heltalsdelUdenTusindadskillere, decimaler, fejltekstEllerNil
end
function p.removeAll(s, pattern)
return s:gsub(pattern, "")
end
--[[
p.modsatAdskillertegn(adskillertegn)
Returner det modsatte adskillertegn til det givne.
adskillertegn:
Skal være enten "." eller ",".
Returværdi:
"," hvis adskillertegn er ".".
"." hvis adskillertegn er "," (eller hvad som helst andet).
]]
function p.modsatAdskillertegn(adskillertegn)
if adskillertegn == "." then
return ","
else
return "."
end
end
function p.opsplitIFortegnOgRest(s)
local eventueltFortegnEllerTom, reststrengUdenFortegn = s:match("^([+-]?) *(.*)$")
return eventueltFortegnEllerTom, reststrengUdenFortegn
end
--[[
p.indsaetTusindadskillere(heltalsStreng)
Hjælpefunktion.
Indsæt tusindadskillere i en given streng af cifre.
Eksempler:
p.indsaetTusindadskillere("") == ""
p.indsaetTusindadskillere("1") == "1"
p.indsaetTusindadskillere("12") == "12"
p.indsaetTusindadskillere("123") == "123"
p.indsaetTusindadskillere("1234") == "1.234"
p.indsaetTusindadskillere("123456789") == "123.456.789"
]]
function p.indsaetTusindadskillere(heltalsstreng)
local akkumulator = heltalsstreng -- Heltal uden tusindadskillere, fx "123456789".
local antalTusindAdskillereIndsat
repeat
akkumulator, antalTusindAdskillereIndsat
= string.gsub(akkumulator, "^(%d+)(%d%d%d)", '%1.%2')
until antalTusindAdskillereIndsat == 0
return akkumulator -- Heltal med tusindadskillere, fx "123.456.789".
end
return p