« Module:Unité » : différence entre les versions

De Lagny-sur-Marne Wiki
Aller à la navigation Aller à la recherche
0x010D (discussion | contributions)
Nouvelle page : local p = { } --- Copie de Outils.trim acceptant les nombres. local function trim( texte ) if type( texte ) == 'string' then texte = texte:gsub( '^%s*(.*)%f[%s]%s*$', '%1' )...
 
0x010D (discussion | contributions)
retouche de la modification précédente
 
(41 versions intermédiaires par le même utilisateur non affichées)
Ligne 1 : Ligne 1 :
local p = { }
local p = {}


-- local Delink = require( 'Module:Delink' ) -- chargé uniquement si nécessaire
-- Chargement de la base de données des nom d'unités avec gestion d'erreur.
local moduleData = 'Module:Unité/Data'
local dataSuccess, Data = pcall ( mw.loadData, moduleData )
if dataSuccess and type( Data ) == 'table' then
dataSuccess = type( Data.unit ) == 'table'
and type( Data.prefix ) == 'table'
and type( Data.exposant ) == 'table'
end
local errorCat = '[[Catégorie:Page incorrectement traitée par le Module:Unité]]'
local addErrorCat = false
local supUnicode = { ['0'] = '⁰', ['1'] = '¹', ['2'] = '²', ['3'] = '³', ['4'] = '⁴', ['5'] = '⁵', ['6'] = '⁶', ['7'] = '⁷', ['8'] = '⁸', ['9'] = '⁹',
['+'] = '⁺', ['-'] = '⁻', ['='] = '⁼', ['('] = '⁽', [')'] = '⁾', ['n'] = 'ⁿ' }
local subUnicode = { ['0'] = '₀', ['1'] = '₁', ['2'] = '₂', ['3'] = '₃', ['4'] = '₄', ['5'] = '₅', ['6'] = '₆', ['7'] = '₇', ['8'] = '₈', ['9'] = '₉',
['+'] = '₊', ['-'] = '₋', ['='] = '₌', ['('] = '₍', [')'] = '₎',
['a'] = 'ₐ', ['e'] = 'ₑ', ['o'] = 'ₒ', ['x'] = 'ₓ', ['h'] = 'ₕ', ['k'] = 'ₖ', ['l'] = 'ₗ',
['m'] = 'ₘ', ['n'] = 'ₙ', ['p'] = 'ₚ', ['s'] = 'ₛ', ['t'] = 'ₜ',
}
--- Copie de Outils.trim acceptant les nombres.
--- Copie de Outils.trim acceptant les nombres.
local function trim( texte )
local function trim( texte )
if type( texte ) == 'string' then
if type( texte ) == 'string' then
texte = texte:gsub( '^%s*(.*)%f[%s]%s*$', '%1' )
texte = texte:gsub( '^%s*(%S?.-)%s*$', '%1' )
if texte ~= '' then
if texte ~= '' then
return texte
return texte
Ligne 10 : Ligne 31 :
elseif type( texte ) == 'number' then
elseif type( texte ) == 'number' then
return tostring( texte )
return tostring( texte )
end
end
function p.sanitizeNum( nombre )
if type( nombre ) == 'number' then
return tostring( nombre )
elseif trim( nombre ) then
local result = nombre
-- trim
:gsub( '^%s*(.*)%f[%s]%s*$', '%1' )
-- remplacement des signes moins ou demi-cadratin par un tiret
:gsub( '%−%f[%d]', '-')  -- U+2212
:gsub( '−%f[%d]', '-')  -- html −
:gsub( '\226\128[\146\147]%f[%d]', '-') -- U+2212, U+2213 (tiret numérique et demi-cadratin)
-- remplacement des espaces insécable par des espace simple
:gsub( '\194\160', ' ' )
result = mw.ustring.gsub( result, '[ -  ]', ' ' ) -- U+2002 à U+220A et U+202F
return result
else
return ''
end
end
end
end


---
---
-- parseNum transforme si possible une chaine formater en un chaine interprétable par tonumber()
-- parseNum transforme si possible une chaine formatée en un chaine interprétable par tonumber()
-- retourne une chaine pour éviter les arrondi éventuels de lua.
-- retourne une chaine pour éviter les arrondi éventuels de lua.
-- si nombre n'est pas un nombre ou une chaine retourne une chaine vide.
-- si "nombre" est une chaine non reconnue comme un nombre par la fonction, retourne "nombre".
--
-- si "nombre" n'est pas un number ou une chaine retourne une chaine vide.
function p.parseNombre( nombre )
function p.parseNombre( nombre )
local result
if type( nombre ) == 'number' then
if type( nombre ) == 'number' then
nombre = tostring( nombre )
return tostring( nombre )
elseif type( nombre ) ~= 'string' then
else
return ''
-- remplacement des signes moins ou demi-cadratin par un tiret
elseif nombre == '' or not nombre:match( '^[-\226]?[\128\136]?[\146-\148]?[%d., \194\160]+$' ) then
result = p.sanitizeNum( nombre )
return nombre
if result == '' then
return ''
elseif not result:match( '^%-?[%d., ]*%d$' ) then
return nombre
end
end
end
if nombre:match( '^-?[\146-\148]?[%d., \194\160]+$' ) or
-- suppression espaces
nombre:match( '^\226[\128\136][\146-\148][%d., \194\160]+$' ) or
result = result:gsub( ' ', '' )
nombre:match( '^−[%d., \194\160]+$' )
then
-- gestion des points et des virgules
-- suppression espaces et remplacement minus, ensp et emsp par un tiret (utilisé par lua)
if result:match( '[.,]' ) then
-- Utilise string pour être plus rapide.
if result:match( '%d%.%d%d%d%.%d' ) then
nombre = nombre:gsub( ' ', '' ):gsub( '\194\160', '' )
-- type 12.345.678
:gsub( '^\226\128[\146-\148]', '-' ) -- U+2012 à U+2014 (voir [[Tiret]])
result = result:gsub( '%.', '' ):gsub( ',', '.' )
:gsub( '^−', '-' ) -- U+2212
elseif result:match( '%d,%d%d%d,%d' ) -- type 1,234,567 ou 1.234,567,8
:gsub( '^&minus', '-' ) -- code html du signe moins
or result:match( '%d,%d%d%d%.%d' )  -- format anglo-saxon type 1,234.5   
or result:match( '%d%.%d%d%d,%d' ) -- type 1.123,56 (utilisé en exemple pour sépararer les décimales avec l'ancien modèle unité ou formatnum)
if nombre:match( '[.,]' ) then
then
if nombre:match( '%d+,%d%d%d,%d%d%d%f[%D]' ) -- type 1,234,567  
result = result:gsub( ',', '' )
or nombre:match( '%d+,%d%d%d%.%d+' )  -- type 1,234.5
else
--or nombre:match( '%d+,%d00$' ) -- type 1,200
result = result:gsub( ',', '.' )
then
-- format anglo-saxon
nombre = nombre:gsub( ',', '' )
elseif nombre:match( '%d+%.%d%d%d,%d' ) or nombre:match( '%d+%.%d%d%d.%d%d%d%f[%D]' ) then
-- formant germanique type 1.234,5
nombre = nombre:gsub( '%.', '' ):gsub( ',', '.' )
else
nombre = nombre:gsub( ',', '.' )
end
end
end
end
end
return nombre
return result
end
end


Ligne 60 : Ligne 97 :
-- _formantNum transforme un nombre ou une chaine représentant un nombre en chaine formatée suivant les conventions du français
-- _formantNum transforme un nombre ou une chaine représentant un nombre en chaine formatée suivant les conventions du français
-- si le paramètre ne représente pas un nombre lua il est retourné sans modification
-- si le paramètre ne représente pas un nombre lua il est retourné sans modification
function p._formatNum( num )
function p.formatNum( num )
local params = {}
if type( num ) == 'table' then
params = num
num = params[1]
end
if type( num ) == 'number' then
if type( num ) == 'number' then
num = tostring( num )
num = tostring( num )
elseif type( num ) ~= 'string' then
elseif type( num ) ~= 'string' or num == '' then
return num
return num
end
end
local moins, entier, fraction = num:match( '^(-?)(%d+)%.?(%d*)$' )
-- séparation exposant
local n, exponent = num:match( '^([-%d.]+)e([+-]?%d+)$' )
if exponent then
num = n
if params.noHtml then
exponent = exponent:gsub('+?%f[%d]0', '' )
:gsub( '[%d-]', supUnicode )
else
exponent = '<sup>' .. exponent:gsub('^%+?(%-?)0?', { ['-'] = '−' } ) .. '</sup>'
end
exponent = ' ×10' .. exponent
else
exponent = ''
end
-- arrondi
decimals = tonumber( params.decimals )
round = tonumber( params.round ) or decimals
if round then
local mult = 10 ^ round
num = tostring( math.floor( num * mult + 0.5 ) / mult )
end
local moins, entier, fraction = num:match( '^(%-?)(%d*)%.?(%d*)$' )
if not entier then
if not entier then
return num
return num
Ligne 76 : Ligne 141 :
end
end
if entier:len() > 3 then
if entier == '' then
local ini = math.fmod( entier:len(), 3 )
entier = '0'
elseif entier:len() > 3 then
local ini = math.fmod( entier:len() - 1, 3 ) + 1
entier = ( entier:sub( 1, ini ) or '') .. entier:sub( ini + 1 ):gsub( '(%d%d%d)', '\194\160%1' )
entier = ( entier:sub( 1, ini ) or '') .. entier:sub( ini + 1 ):gsub( '(%d%d%d)', '\194\160%1' )
end
end
if fraction ~= '' then
if fraction ~= '' or ( decimals and decimals > 0 ) then
fraction = ',' .. fraction:gsub( '(%d%d%d)', '%1\194\160' ):gsub( '\194\160$', '' )
if decimals and decimals > #fraction then
fraction = fraction .. string.rep( '0', decimals - #fraction )
end
if #fraction > 4 then
fraction = ',' .. fraction:gsub( '(%d%d%d)', '%1\194\160' ):gsub( '\194\160$', '' )
else
fraction = ',' .. fraction
end
end
end
 
return moins .. entier .. fraction
return moins .. entier .. fraction .. exponent
end
end


---
---
-- formatNum transforme les nombre d'une chaine
-- formatNombre transforme un nombre formaté ou non en chaine formatée suivant les convention du français.
function p.formatNum( num )
-- si la chaine n'est pas reconnu comme un nombre, elle n'est pas modifiée.
if type( num ) == 'number' then
function p.formatNombre( num, round, decimals )
return p._formatNum( num )
return p.formatNum{ p.parseNombre( num ), round, decimals }
elseif type( num ) == 'string' then
end
return num:gsub( '-?%d*%.?%d+', p.formatNombre )
 
--- formatNombres transforme tous les nombres d'une chaine en nombre formaté suivant les conventions du français.
function p.formatNombres( nombres, round, decimals )
if type( nombres ) == 'number' then
return p.formatNum( nombres, round, decimals )
elseif type( nombres ) == 'string' then
-- retire les chiffres des strip marker
local strip, i = {}, 0
nombres = nombres:gsub(
'UNIQ%-%-%a+%-%x%x%x%x%x%x%x%x%-QINU',
function ( marker )
i = i + 1
strip[ tostring( i ) ] = marker
return 'UNIQ^' .. i .. '¤QINU'
end
)
--  formatage proprement dit
nombres = p.sanitizeNum( nombres )
nombres =  nombres:gsub(  
'%-?%f[%d.,^][%d., ]+%f[%D]',  
function ( n )
return p.formatNombre( n, round, decimals )
end
)
-- réintroduction des strip marker
nombres = nombres:gsub( 'UNIQ^(%d+)¤QINU', strip )
return nombres
else
return ''
end
end
end
end


function p.formatNombre( nombre )
function p.parseUnit( texte )
return p._formatNum( p.parseNombre( nombre ) )
local toParse = p.sanitizeNum( texte )
if toParse ~= '' then
local result
local specificArgs = {
['à'] = 'à',
et = 'et',
ou = 'ou',
['–'] = '–', -- demi cadratin
['±'] = '±', ['+-'] = '±', ['+/-'] = '±',
['+'] = '+',
['−'] = '−', ['-'] = '−', -- signe moins et tiret
['×'] = '×', x = '×', ['*'] = '×',
['××'] = '××', xx = '××', ['**'] = '××',
}
-- valeur numérique
local match, capture = toParse:match( '^((%-?%f[%d.,][%d., ]*%d%f[%D])%s*)' )
result = { capture or false }
if match then
toParse = toParse:sub( match:len() + 1 )
-- fraction
match, capture = toParse:match( '^(([+-]?%d* ?/ ?%d+)%s*)' )
if not match then
match, capture = mw.ustring.match( toParse, '^(([¼-¾⅐-⅞])%s*)' )
end
if match then
if capture:match( '^/' ) then
local n = result[1]:match( ' (%d+)$' ) or ''
result[1] = result[1]:sub( 1, -1 - #n )
result.fraction = n .. capture
else
result.fraction = capture
end
toParse = toParse:sub( match:len() + 1 )
end
-- lien avec un deuxième nombre
local match2, conj, num = mw.ustring.match( toParse, '^(([àetouM+/−x*×±–-]+) ?(%-?%f[%d.,][%d., ]*%d%f[%D])%s*)' )
if match2 and specificArgs[ conj ]
and not ( specificArgs[ conj ] == '×' and mw.ustring.match( toParse, '^[×x] ?10 ?e') ) then
result[ specificArgs[ conj ] ] = num
toParse = toParse:sub( match2:len() + 1 )
end
if result['+'] or result['×'] then
match2, conj, num = mw.ustring.match( toParse, '^(([x*×−-]) ?(%-?%f[%d.,][%d., ]*%d%f[%D])%s*)' )
if match2 then
if specificArgs[ conj ] == '×' then
result['××'] = num
else
result['−'] = num
end
toParse = toParse:sub( match2:len() + 1 )
end
end
end
-- 10 exposant  ( \195\151 = ×, signe multiplié)
match, capture = toParse:match( '^(%s*e(%-?%d+)%s*)' )
if not match then
match, capture = toParse:match( '^(%s*[x\195]\151?10e(%-?%d+)%s*)' )
end
if match then
result.e = capture
toParse = toParse:sub( match:len() + 1 )
end
-- unités
if Data.unit[ toParse ] or mw.ustring.match( toParse, '^%a+$' ) or toParse:match( '%b<>' ) then
table.insert( result, toParse )
toParse = ''
elseif toParse ~= '' then
local unit, exp
toParse = toParse:gsub( '²', '2' ):gsub( '³', '3' )
repeat
-- unité contenant un lien
match, unit, exp = mw.ustring.match( toParse, '^((/?[^%s%d/%[%]]*%b[][^%s%d/]*) ?(%-?%d*)%s*)' )
if not match then
-- unité ne contenant pas de lien
match, unit, exp = mw.ustring.match( toParse, '^((/?[^%s%d/]+) ?(%-?%d*)%s*)' )
end
if match then
if unit:match( '%-$' ) and exp ~= '' then
unit = unit:gsub( '%-$', '' )
exp = '-' .. exp
elseif exp == '-' then
unit = match
exp = ''
end
table.insert( result, unit )
table.insert( result, exp )
toParse = toParse:sub( match:len() + 1 )
end
until toParse == '' or not match
end
if toParse == '' then
return result
else
-- une partie de la chaine n'a pas pu être décodée, on retourne la chaine originale
addErrorCat = true
return { texte }
end
else
return { }
end
end
end


---
-- nomUtnit retourne le nom français du code d'une unité et de son exposant.
-- si le code de l'unité n'est pas reconnu retourne 1 et false, de façon à ajouter false en première position d'une table.
function p.nomUnit( unit, exposant )
if not dataSuccess or type( unit ) ~= 'string' then
return 1, false
end
-- nettoyage des liens et balise HTML
unit = unit:gsub( '^/' , '' )
if unit:match( '%[' ) then
local Delink = require( 'Module:Delink' )
unit = Delink._delink{ unit }
end
if unit:match( '<' ) then
unit = unit:gsub( '%b<>', '' )
end
-- récupère le nom de l'unité
local unitTab = Data.unit[ unit ]
local unitPrefix = { nom = '' }
if not unitTab then
unitTab = Data.unit[ unit:sub( 2 ) ]
unitPrefix = Data.prefix[ unit:sub( 1, 1 ) ]
if not ( unitTab and unitPrefix ) then
-- pour µ, Ki, Mi, Gi... qui sont codé sur deux octets
unitTab = Data.unit[ unit:sub( 3 ) ]
unitPrefix = Data.prefix[ unit:sub( 1, 2 ) ]
if not ( unitTab and unitPrefix ) then
unitTab = false
end
end
end
-- récupère le nom de l'exposant
if trim( exposant ) then
local exp = tonumber( exposant )
exp = exp and Data.exposant[ math.abs( exp ) ]
exposant = exp or ' puissance ' .. exposant
else
exposant = ''
end
-- assemble les deux partie
if type( unitTab ) == 'table' and type( unitTab.nom ) == 'string' then
return unitPrefix.nom .. unitTab.nom .. exposant
elseif unit:match( '[/%d]' ) then
-- ce n'est pas du texte simple, on anule l'infobule
return 1, false
else
return unit .. exposant
end
end


function p._unite( args )
function p._unite( args )
local wiki = { }
-- remplacement de certains caractères, pour simplifier les pattern
local sep = ''
local nombre = p.sanitizeNum( args[1] )
local nombre = trim( args[1] )
if nombre == '' then
if nombre then
nombre = nil
nombre = nombre:gsub( '[-−–—]?%f[%d][%d., \194\160]+%f[%D]', p.formatNombre )
else
table.insert( wiki, nombre )
-- formatage du nombre
sep = '\194\160'
nombre = p.formatNombres( nombre, args.arrondi, args['décimales'] )
end
end
local i = 1
local wiki = { nombre }
local unit = trim( args[2] )
if unit == 10 then
-- fraction
args.e = args[3]
if args.fraction then
i = 2
local nom, den = args.fraction:match( '^(.-)/(.+)$' )
unit = trim( args[4] )
if nom then
if nom:match( '^[ %dn()=+-]+$' ) and den:match( '^[ %daeoxhklmnpst()=+-]$' ) then
nom = nom:gsub( '[%dn()=+-]', supUnicode )
den = den:gsub( '[%daeoxhklmnpst()=+-]', subUnicode )
else
nom = '<sup style="font-size: 70%; vertical-align: 0.4em;">' .. nom .. '</sup>'
den = '<sub style="font-size: 70%; vertical-align: 0em;">' .. den .. '</sub>'
end
args.fraction = nom .. '⁄' .. den
end
table.insert( wiki, ' ' .. args.fraction )
end
end
local exp = trim( args.e )
if exp then
-- à, et, ou, ×, – (tiret cadratin)
if #wiki > 0 then
local specificArgs = { '–', 'à', 'et', 'ou', '×', '××', '±' }
table.insert( wiki, '×10<sup>' .. exp .. '</sup>' )
for _, name in ipairs( specificArgs ) do
else
local v = trim( args[ name ] )
table.insert( wiki, '10<sup>' .. exp .. '</sup>' )
if v then
v = p.formatNombres( v )
if name == '–' and nombre and nombre:match( '^[^−]' ) and v:match( '^[^−]' ) then
-- pas d'espace pour le tiret cadratin entre deux nombres positifs
table.insert( wiki, '–' )
elseif name == '××' then
table.insert( wiki, ' × ' )
else
table.insert( wiki, ' ' .. name .. ' ' )
end
table.insert( wiki, v )
end
end
sep = '\194\160'
end
end
-- unités
local i = 1
local unit = trim( args[ 2 * i ] )
local units = ''
local nomUnits, par = {}, false
while unit do
while unit do
table.insert( wiki, sep )
local exp = p.parseNombre( args[ 2 * i + 1 ] )
table.insert( wiki, unit )
local sep = ''
exp = trim( args[ 2 * i + 1 ] )
-- gestion des exposants
if exp then
local expUnit = ''
table.insert( wiki, '<sup>' .. exp:gsub( '-', '' ) .. '</sup>' )
if exp == '' then
if unit:sub( -2 ) == '²' then
exp = '2'
unit = unit:sub( 1, -3 )
elseif unit:sub( -2 ) == '³' then
exp = '3'
unit = unit:sub( 1, -3 )
end
end
end
if #exp > 0 then
expUnit = '<sup>' .. exp:gsub( '^-', '−') .. '</sup>'  -- remplace le tiret par un vrai signe moins
end
-- gestion de la séparation des unités et des unités en dénominateur
if units ~= '' then
if unit:sub( 1, 1 ) == '/' then
sep = '/'
unit = unit:sub( 2 )
if not par then
par = true
table.insert( nomUnits, 'par' )
end
else
sep = '\194\160'  -- point médian désactivé : '⋅\194\160' 
if exp:match( '^-' ) and not par then
par = true
table.insert( nomUnits, 'par' )
end
end
end
-- remplacement de l'unité par son symbole
if Data.unit[ unit ] then
-- unit = Data.unit[ unit ].symbole
-- désactivé car ne gère pas les multiple tel mL
end
units = units .. sep .. unit .. expUnit
table.insert( nomUnits, p.nomUnit( unit, exp ) )
i = i + 1
i = i + 1
unit = trim( args[ 2 * i ] )
unit = trim( args[ 2 * i ] )
sep = '⋅'
end
end
-- conversion
local unitNameString = nomUnits[1] and table.concat( nomUnits, ' ' ) or ''
unitNameString = mw.ustring.gsub( unitNameString, '(%a)s%f[%A]', '%1' )
local multiple = 1
local convertTable = Data.convert[ unitNameString ]
if not convertTable and #unitNameString > 5 then
-- gesion des multiples (Kilo, méga, mili...)
local prefix = Data.prefix[ unitNameString:sub( 1, 4 ) ] or Data.prefix[ unitNameString:sub( 1, 5 ) ]
local _, par = unitNameString:find( ' par ' )
local prefix2
if par then
prefix2 = Data.prefix[ unitNameString:sub( par + 1, 4 ) ] or Data.prefix[ unitNameString:sub( par + 1, 5 ) ]
end
if prefix and Data.convert[ unitNameString:gsub( '^' .. prefix.nom, '' ) ] then
convertTable = Data.convert[ unitNameString:gsub( '^' .. prefix.nom, '' ) ]
multiple = 10 ^ prefix.puissance
elseif prefix2 and  Data.convert[ unitNameString:gsub( ' par ' .. prefix2.nom, '' ) ] then
convertTable = Data.convert[ unitNameString:gsub( ' par ' .. prefix2.nom, '' ) ]
multiple = 1 / 10 ^ prefix2.puissance
elseif prefix and prefix2 and Data.convert[ unitNameString:gsub( '^' .. prefix.nom, '' ):gsub( ' par ' .. prefix2.nom, '' ) ] then
convertTable = Data.convert[ unitNameString:gsub( '^' .. prefix.nom, '' ):gsub( ' par ' .. prefix2.nom, '' ) ]
multiple = 10 ^ prefix.puissance / 10 ^ prefix2.puissance
end
end
if convertTable then
if type( convertTable[1] ) ~= 'table' then
convertTable = { convertTable }
end
for i, v in ipairs( wiki ) do
local n = tonumber( p.parseNombre( v ) )
if n then
n = n * 10 ^ ( tonumber( p.parseNombre( args.e ) ) or 0 )
local converted = {}
for _, c in ipairs( convertTable ) do
local nConverted = n
if c.inverse then
nConverted = 1 / n
end
if c.M then
-- M = masse molaire
local M = tonumber( args.M )
if not M then
break
end
if c.M == '*' then
nConverted = nConverted * M
elseif c.M == '/' then
nConverted = nConverted / M
end
end
nConverted = nConverted * multiple * c[2] + ( c[3] or 0 )
-- format
nConverted = p.formatNum{ nConverted, round = c.round or 6, noHtml = true }
table.insert( converted, nConverted .. ' '.. c[1] )
end
wiki[ i ] = '<span title="' .. table.concat( converted, ' ou ' ) ..'">' .. v ..'</span>'
end
end
end
-- incertitude avec + et − séparés
if trim( args['+'] ) then
local approximation = '+' .. p.formatNombre( args['+'] ) .. ''
if trim( args['−'] ) then
approximation = approximation .. '<br> −' .. p.formatNombre( args['−'] )
end
table.insert( wiki, '<span style="display:inline-block; vertical-align:top; line-height:1em; font-size:80%; text-align:left;">' )
table.insert( wiki, approximation .. '</span>' )
end
-- puissance de 10
local exposant = trim( args.e )
if exposant then
exposant = p.formatNombre( exposant )
if nombre then
if trim( args['±'] ) and not nombre:match( '^%(' ) then
table.insert( wiki, 1, '(' )
table.insert( wiki, ')' )
end
table.insert( wiki, ' × 10<sup>' .. exposant .. '</sup>' )
else
table.insert( wiki, '10<sup>' .. exposant .. '</sup>' )
end
end
-- ajoute une abbréviation si le nom de l'unité est différent de l'unité (en retirant les espace qui peuvent être devenus insécables)
if units and nomUnits[1] and mw.ustring.gsub( table.concat( nomUnits ), '[ \194\160]', '' ) ~= mw.ustring.gsub( units, '[ \194\160]', '' ) then
units = string.format( ' <abbr class=abbr title="%s">%s</abbr>', table.concat( nomUnits, ' ' ), units )
elseif units ~= '' and units ~= '°' then
units = ' ' .. units
end
table.insert( wiki, units )
if #wiki > 0 then
if #wiki > 0 then
return '<span class="nowrap">' .. table.concat( wiki ) .. '</span>'
local result = table.concat( wiki )
if result:match( ' ' ) then
return '<span class="nowrap">' .. result .. '</span>'
else
return result
end
end
end
end
end
Ligne 148 : Ligne 563 :
function p.unite( frame )
function p.unite( frame )
local args
local args
if type( frame ) == 'table' and type( frame.getParent ) == 'function' then
if type( frame ) == 'table' then
if type( frame.getParent ) == 'function' then
args = frame:getParent().args;
args = frame:getParent().args;
else
args = frame
end
end
end
if args then
if args then
return p._unite( args )
if trim( args[1] ) then
if args[1]:match('[^%d,. -]') then
local tempArgs = p.parseUnit( trim( args[1] ) )
if not ( args[2] and tempArgs[2] ) then
for k, v in pairs( tempArgs ) do
args[k] = v
end
end
end
if args[2] and not Data.unit[ args[2] ] and not args[3] and mw.text.trim( args[2] ):match('[%d/ -]') then
local tempArgs = p.parseUnit( trim( args[2] ) )
args[2] = false
if tempArgs[1] ~= false then
table.insert( tempArgs, 1, false )
end
for k, v in pairs( tempArgs ) do
if args[k] and v then
addErrorCat = true
end
args[k] = args[k] or v
end
end
end
-- args alias
args['x'] = args['×'] -- lettre x → signe multiplié
if args['+'] then
args['−'] = args['−'] or args['-'] -- tiret → signe moins
else
args['–'] = args['–'] or args['-'] -- tiret → demi-cadratin
end
local cat = ''
if addErrorCat then
cat = errorCat
end
return p._unite( args ) .. cat
end
end
end
end


return p
return p

Dernière version du 29 mai 2017 à 22:44

local p = {}

-- local Delink = require( 'Module:Delink' ) -- chargé uniquement si nécessaire

-- Chargement de la base de données des nom d'unités avec gestion d'erreur. local moduleData = 'Module:Unité/Data' local dataSuccess, Data = pcall ( mw.loadData, moduleData ) if dataSuccess and type( Data ) == 'table' then dataSuccess = type( Data.unit ) == 'table' and type( Data.prefix ) == 'table' and type( Data.exposant ) == 'table' end

local errorCat = local addErrorCat = false

local supUnicode = { ['0'] = '⁰', ['1'] = '¹', ['2'] = '²', ['3'] = '³', ['4'] = '⁴', ['5'] = '⁵', ['6'] = '⁶', ['7'] = '⁷', ['8'] = '⁸', ['9'] = '⁹', ['+'] = '⁺', ['-'] = '⁻', ['='] = '⁼', ['('] = '⁽', [')'] = '⁾', ['n'] = 'ⁿ' } local subUnicode = { ['0'] = '₀', ['1'] = '₁', ['2'] = '₂', ['3'] = '₃', ['4'] = '₄', ['5'] = '₅', ['6'] = '₆', ['7'] = '₇', ['8'] = '₈', ['9'] = '₉', ['+'] = '₊', ['-'] = '₋', ['='] = '₌', ['('] = '₍', [')'] = '₎', ['a'] = 'ₐ', ['e'] = 'ₑ', ['o'] = 'ₒ', ['x'] = 'ₓ', ['h'] = 'ₕ', ['k'] = 'ₖ', ['l'] = 'ₗ', ['m'] = 'ₘ', ['n'] = 'ₙ', ['p'] = 'ₚ', ['s'] = 'ₛ', ['t'] = 'ₜ', } --- Copie de Outils.trim acceptant les nombres. local function trim( texte ) if type( texte ) == 'string' then texte = texte:gsub( '^%s*(%S?.-)%s*$', '%1' ) if texte ~= then return texte end elseif type( texte ) == 'number' then return tostring( texte ) end end

function p.sanitizeNum( nombre ) if type( nombre ) == 'number' then return tostring( nombre ) elseif trim( nombre ) then local result = nombre -- trim :gsub( '^%s*(.*)%f[%s]%s*$', '%1' ) -- remplacement des signes moins ou demi-cadratin par un tiret :gsub( '%−%f[%d]', '-') -- U+2212 :gsub( '−%f[%d]', '-') -- html − :gsub( '\226\128[\146\147]%f[%d]', '-') -- U+2212, U+2213 (tiret numérique et demi-cadratin) -- remplacement des espaces insécable par des espace simple :gsub( '\194\160', ' ' ) result = mw.ustring.gsub( result, '[ -  ]', ' ' ) -- U+2002 à U+220A et U+202F return result else return end end

--- -- parseNum transforme si possible une chaine formatée en un chaine interprétable par tonumber() -- retourne une chaine pour éviter les arrondi éventuels de lua. -- si "nombre" est une chaine non reconnue comme un nombre par la fonction, retourne "nombre". -- si "nombre" n'est pas un number ou une chaine retourne une chaine vide. function p.parseNombre( nombre ) local result if type( nombre ) == 'number' then return tostring( nombre ) else -- remplacement des signes moins ou demi-cadratin par un tiret result = p.sanitizeNum( nombre ) if result == then return elseif not result:match( '^%-?[%d., ]*%d$' ) then return nombre end end

-- suppression espaces result = result:gsub( ' ', )

-- gestion des points et des virgules if result:match( '[.,]' ) then if result:match( '%d%.%d%d%d%.%d' ) then -- type 12.345.678 result = result:gsub( '%.', ):gsub( ',', '.' ) elseif result:match( '%d,%d%d%d,%d' ) -- type 1,234,567 ou 1.234,567,8 or result:match( '%d,%d%d%d%.%d' ) -- format anglo-saxon type 1,234.5 or result:match( '%d%.%d%d%d,%d' ) -- type 1.123,56 (utilisé en exemple pour sépararer les décimales avec l'ancien modèle unité ou formatnum) then result = result:gsub( ',', ) else result = result:gsub( ',', '.' ) end end

return result end

--- -- _formantNum transforme un nombre ou une chaine représentant un nombre en chaine formatée suivant les conventions du français -- si le paramètre ne représente pas un nombre lua il est retourné sans modification function p.formatNum( num ) local params = {} if type( num ) == 'table' then params = num num = params[1] end if type( num ) == 'number' then num = tostring( num ) elseif type( num ) ~= 'string' or num == then return num end

-- séparation exposant local n, exponent = num:match( '^([-%d.]+)e([+-]?%d+)$' ) if exponent then num = n if params.noHtml then exponent = exponent:gsub('+?%f[%d]0', ) :gsub( '[%d-]', supUnicode ) else exponent = '' .. exponent:gsub('^%+?(%-?)0?', { ['-'] = '−' } ) .. '' end exponent = ' ×10' .. exponent else exponent = end

-- arrondi decimals = tonumber( params.decimals ) round = tonumber( params.round ) or decimals if round then local mult = 10 ^ round num = tostring( math.floor( num * mult + 0.5 ) / mult ) end

local moins, entier, fraction = num:match( '^(%-?)(%d*)%.?(%d*)$' ) if not entier then return num end

if moins == '-' then moins = '−' -- signe moins (U+2212) end

if entier == then entier = '0' elseif entier:len() > 3 then local ini = math.fmod( entier:len() - 1, 3 ) + 1 entier = ( entier:sub( 1, ini ) or ) .. entier:sub( ini + 1 ):gsub( '(%d%d%d)', '\194\160%1' ) end if fraction ~= or ( decimals and decimals > 0 ) then if decimals and decimals > #fraction then fraction = fraction .. string.rep( '0', decimals - #fraction ) end if #fraction > 4 then fraction = ',' .. fraction:gsub( '(%d%d%d)', '%1\194\160' ):gsub( '\194\160$', ) else fraction = ',' .. fraction end end

return moins .. entier .. fraction .. exponent end

--- -- formatNombre transforme un nombre formaté ou non en chaine formatée suivant les convention du français. -- si la chaine n'est pas reconnu comme un nombre, elle n'est pas modifiée. function p.formatNombre( num, round, decimals ) return p.formatNum{ p.parseNombre( num ), round, decimals } end

--- formatNombres transforme tous les nombres d'une chaine en nombre formaté suivant les conventions du français. function p.formatNombres( nombres, round, decimals ) if type( nombres ) == 'number' then return p.formatNum( nombres, round, decimals ) elseif type( nombres ) == 'string' then -- retire les chiffres des strip marker local strip, i = {}, 0 nombres = nombres:gsub( 'UNIQ%-%-%a+%-%x%x%x%x%x%x%x%x%-QINU', function ( marker ) i = i + 1 strip[ tostring( i ) ] = marker return 'UNIQ^' .. i .. '¤QINU' end ) -- formatage proprement dit nombres = p.sanitizeNum( nombres ) nombres = nombres:gsub( '%-?%f[%d.,^][%d., ]+%f[%D]', function ( n ) return p.formatNombre( n, round, decimals ) end ) -- réintroduction des strip marker nombres = nombres:gsub( 'UNIQ^(%d+)¤QINU', strip ) return nombres else return end end

function p.parseUnit( texte ) local toParse = p.sanitizeNum( texte ) if toParse ~= then local result local specificArgs = { ['à'] = 'à', et = 'et', ou = 'ou', ['–'] = '–', -- demi cadratin ['±'] = '±', ['+-'] = '±', ['+/-'] = '±', ['+'] = '+', ['−'] = '−', ['-'] = '−', -- signe moins et tiret ['×'] = '×', x = '×', ['*'] = '×', ['××'] = '××', xx = '××', ['**'] = '××', }

-- valeur numérique local match, capture = toParse:match( '^((%-?%f[%d.,][%d., ]*%d%f[%D])%s*)' ) result = { capture or false } if match then toParse = toParse:sub( match:len() + 1 )

-- fraction match, capture = toParse:match( '^(([+-]?%d* ?/ ?%d+)%s*)' ) if not match then match, capture = mw.ustring.match( toParse, '^(([¼-¾⅐-⅞])%s*)' ) end if match then if capture:match( '^/' ) then local n = result[1]:match( ' (%d+)$' ) or result[1] = result[1]:sub( 1, -1 - #n ) result.fraction = n .. capture else result.fraction = capture end toParse = toParse:sub( match:len() + 1 ) end

-- lien avec un deuxième nombre local match2, conj, num = mw.ustring.match( toParse, '^(([àetouM+/−x*×±–-]+) ?(%-?%f[%d.,][%d., ]*%d%f[%D])%s*)' ) if match2 and specificArgs[ conj ] and not ( specificArgs[ conj ] == '×' and mw.ustring.match( toParse, '^[×x] ?10 ?e') ) then result[ specificArgs[ conj ] ] = num toParse = toParse:sub( match2:len() + 1 ) end if result['+'] or result['×'] then match2, conj, num = mw.ustring.match( toParse, '^(([x*×−-]) ?(%-?%f[%d.,][%d., ]*%d%f[%D])%s*)' ) if match2 then if specificArgs[ conj ] == '×' then result['××'] = num else result['−'] = num end toParse = toParse:sub( match2:len() + 1 ) end end end

-- 10 exposant ( \195\151 = ×, signe multiplié) match, capture = toParse:match( '^(%s*e(%-?%d+)%s*)' ) if not match then match, capture = toParse:match( '^(%s*[x\195]\151?10e(%-?%d+)%s*)' ) end if match then result.e = capture toParse = toParse:sub( match:len() + 1 ) end

-- unités if Data.unit[ toParse ] or mw.ustring.match( toParse, '^%a+$' ) or toParse:match( '%b<>' ) then table.insert( result, toParse ) toParse = elseif toParse ~= then local unit, exp toParse = toParse:gsub( '²', '2' ):gsub( '³', '3' ) repeat -- unité contenant un lien match, unit, exp = mw.ustring.match( toParse, '^((/?[^%s%d/%[%]]*%b[][^%s%d/]*) ?(%-?%d*)%s*)' ) if not match then -- unité ne contenant pas de lien match, unit, exp = mw.ustring.match( toParse, '^((/?[^%s%d/]+) ?(%-?%d*)%s*)' ) end if match then if unit:match( '%-$' ) and exp ~= then unit = unit:gsub( '%-$', ) exp = '-' .. exp elseif exp == '-' then unit = match exp = end table.insert( result, unit ) table.insert( result, exp ) toParse = toParse:sub( match:len() + 1 ) end until toParse == or not match end

if toParse == then return result else -- une partie de la chaine n'a pas pu être décodée, on retourne la chaine originale addErrorCat = true return { texte } end else return { } end end

--- -- nomUtnit retourne le nom français du code d'une unité et de son exposant. -- si le code de l'unité n'est pas reconnu retourne 1 et false, de façon à ajouter false en première position d'une table. function p.nomUnit( unit, exposant ) if not dataSuccess or type( unit ) ~= 'string' then return 1, false end

-- nettoyage des liens et balise HTML unit = unit:gsub( '^/' , ) if unit:match( '%[' ) then local Delink = require( 'Module:Delink' ) unit = Delink._delink{ unit } end if unit:match( '<' ) then unit = unit:gsub( '%b<>', ) end

-- récupère le nom de l'unité local unitTab = Data.unit[ unit ] local unitPrefix = { nom = } if not unitTab then unitTab = Data.unit[ unit:sub( 2 ) ] unitPrefix = Data.prefix[ unit:sub( 1, 1 ) ] if not ( unitTab and unitPrefix ) then -- pour µ, Ki, Mi, Gi... qui sont codé sur deux octets unitTab = Data.unit[ unit:sub( 3 ) ] unitPrefix = Data.prefix[ unit:sub( 1, 2 ) ] if not ( unitTab and unitPrefix ) then unitTab = false end end end

-- récupère le nom de l'exposant if trim( exposant ) then local exp = tonumber( exposant ) exp = exp and Data.exposant[ math.abs( exp ) ] exposant = exp or ' puissance ' .. exposant else exposant = end

-- assemble les deux partie if type( unitTab ) == 'table' and type( unitTab.nom ) == 'string' then return unitPrefix.nom .. unitTab.nom .. exposant elseif unit:match( '[/%d]' ) then -- ce n'est pas du texte simple, on anule l'infobule return 1, false else return unit .. exposant end end

function p._unite( args ) -- remplacement de certains caractères, pour simplifier les pattern local nombre = p.sanitizeNum( args[1] ) if nombre == then nombre = nil else -- formatage du nombre nombre = p.formatNombres( nombre, args.arrondi, args['décimales'] ) end

local wiki = { nombre }

-- fraction if args.fraction then local nom, den = args.fraction:match( '^(.-)/(.+)$' ) if nom then if nom:match( '^[ %dn()=+-]+$' ) and den:match( '^[ %daeoxhklmnpst()=+-]$' ) then nom = nom:gsub( '[%dn()=+-]', supUnicode ) den = den:gsub( '[%daeoxhklmnpst()=+-]', subUnicode ) else nom = '' .. nom .. '' den = '' .. den .. '' end args.fraction = nom .. '⁄' .. den end table.insert( wiki, ' ' .. args.fraction ) end

-- à, et, ou, ×, – (tiret cadratin) local specificArgs = { '–', 'à', 'et', 'ou', '×', '××', '±' } for _, name in ipairs( specificArgs ) do local v = trim( args[ name ] ) if v then v = p.formatNombres( v ) if name == '–' and nombre and nombre:match( '^[^−]' ) and v:match( '^[^−]' ) then -- pas d'espace pour le tiret cadratin entre deux nombres positifs table.insert( wiki, '–' ) elseif name == '××' then table.insert( wiki, ' × ' ) else table.insert( wiki, ' ' .. name .. ' ' ) end table.insert( wiki, v ) end end

-- unités local i = 1 local unit = trim( args[ 2 * i ] ) local units = local nomUnits, par = {}, false while unit do local exp = p.parseNombre( args[ 2 * i + 1 ] ) local sep = -- gestion des exposants local expUnit = if exp == then if unit:sub( -2 ) == '²' then exp = '2' unit = unit:sub( 1, -3 ) elseif unit:sub( -2 ) == '³' then exp = '3' unit = unit:sub( 1, -3 ) end end if #exp > 0 then expUnit = '' .. exp:gsub( '^-', '−') .. '' -- remplace le tiret par un vrai signe moins end -- gestion de la séparation des unités et des unités en dénominateur if units ~= then if unit:sub( 1, 1 ) == '/' then sep = '/' unit = unit:sub( 2 ) if not par then par = true table.insert( nomUnits, 'par' ) end else sep = '\194\160' -- point médian désactivé : '⋅\194\160' if exp:match( '^-' ) and not par then par = true table.insert( nomUnits, 'par' ) end end end -- remplacement de l'unité par son symbole if Data.unit[ unit ] then -- unit = Data.unit[ unit ].symbole -- désactivé car ne gère pas les multiple tel mL end units = units .. sep .. unit .. expUnit table.insert( nomUnits, p.nomUnit( unit, exp ) ) i = i + 1 unit = trim( args[ 2 * i ] ) end

-- conversion local unitNameString = nomUnits[1] and table.concat( nomUnits, ' ' ) or unitNameString = mw.ustring.gsub( unitNameString, '(%a)s%f[%A]', '%1' ) local multiple = 1 local convertTable = Data.convert[ unitNameString ] if not convertTable and #unitNameString > 5 then -- gesion des multiples (Kilo, méga, mili...) local prefix = Data.prefix[ unitNameString:sub( 1, 4 ) ] or Data.prefix[ unitNameString:sub( 1, 5 ) ] local _, par = unitNameString:find( ' par ' ) local prefix2 if par then prefix2 = Data.prefix[ unitNameString:sub( par + 1, 4 ) ] or Data.prefix[ unitNameString:sub( par + 1, 5 ) ] end if prefix and Data.convert[ unitNameString:gsub( '^' .. prefix.nom, ) ] then convertTable = Data.convert[ unitNameString:gsub( '^' .. prefix.nom, ) ] multiple = 10 ^ prefix.puissance elseif prefix2 and Data.convert[ unitNameString:gsub( ' par ' .. prefix2.nom, ) ] then convertTable = Data.convert[ unitNameString:gsub( ' par ' .. prefix2.nom, ) ] multiple = 1 / 10 ^ prefix2.puissance elseif prefix and prefix2 and Data.convert[ unitNameString:gsub( '^' .. prefix.nom, ):gsub( ' par ' .. prefix2.nom, ) ] then convertTable = Data.convert[ unitNameString:gsub( '^' .. prefix.nom, ):gsub( ' par ' .. prefix2.nom, ) ] multiple = 10 ^ prefix.puissance / 10 ^ prefix2.puissance end end if convertTable then if type( convertTable[1] ) ~= 'table' then convertTable = { convertTable } end for i, v in ipairs( wiki ) do local n = tonumber( p.parseNombre( v ) ) if n then n = n * 10 ^ ( tonumber( p.parseNombre( args.e ) ) or 0 ) local converted = {} for _, c in ipairs( convertTable ) do local nConverted = n if c.inverse then nConverted = 1 / n end if c.M then -- M = masse molaire local M = tonumber( args.M ) if not M then break end if c.M == '*' then nConverted = nConverted * M elseif c.M == '/' then nConverted = nConverted / M end end nConverted = nConverted * multiple * c[2] + ( c[3] or 0 ) -- format nConverted = p.formatNum{ nConverted, round = c.round or 6, noHtml = true } table.insert( converted, nConverted .. ' '.. c[1] ) end wiki[ i ] = '' .. v ..'' end end end

-- incertitude avec + et − séparés if trim( args['+'] ) then local approximation = '+' .. p.formatNombre( args['+'] ) .. if trim( args['−'] ) then approximation = approximation .. '
−' .. p.formatNombre( args['−'] ) end table.insert( wiki, '' ) table.insert( wiki, approximation .. '' ) end

-- puissance de 10 local exposant = trim( args.e ) if exposant then exposant = p.formatNombre( exposant ) if nombre then if trim( args['±'] ) and not nombre:match( '^%(' ) then table.insert( wiki, 1, '(' ) table.insert( wiki, ')' ) end table.insert( wiki, ' × 10' .. exposant .. '' ) else table.insert( wiki, '10' .. exposant .. '' ) end end

-- ajoute une abbréviation si le nom de l'unité est différent de l'unité (en retirant les espace qui peuvent être devenus insécables) if units and nomUnits[1] and mw.ustring.gsub( table.concat( nomUnits ), '[ \194\160]', ) ~= mw.ustring.gsub( units, '[ \194\160]', ) then units = string.format( ' %s', table.concat( nomUnits, ' ' ), units ) elseif units ~= and units ~= '°' then units = ' ' .. units end table.insert( wiki, units )

if #wiki > 0 then local result = table.concat( wiki ) if result:match( ' ' ) then return '' .. result .. '' else return result end end end

function p.unite( frame ) local args if type( frame ) == 'table' then if type( frame.getParent ) == 'function' then args = frame:getParent().args; else args = frame end end if args then if trim( args[1] ) then if args[1]:match('[^%d,. -]') then local tempArgs = p.parseUnit( trim( args[1] ) ) if not ( args[2] and tempArgs[2] ) then for k, v in pairs( tempArgs ) do args[k] = v end end end if args[2] and not Data.unit[ args[2] ] and not args[3] and mw.text.trim( args[2] ):match('[%d/ -]') then local tempArgs = p.parseUnit( trim( args[2] ) ) args[2] = false if tempArgs[1] ~= false then table.insert( tempArgs, 1, false ) end for k, v in pairs( tempArgs ) do if args[k] and v then addErrorCat = true end args[k] = args[k] or v end end end -- args alias args['x'] = args['×'] -- lettre x → signe multiplié if args['+'] then args['−'] = args['−'] or args['-'] -- tiret → signe moins else args['–'] = args['–'] or args['-'] -- tiret → demi-cadratin end local cat = if addErrorCat then cat = errorCat end return p._unite( args ) .. cat end end

return p