For numbers, "and" is used in the middle of these 5 cases:
Between tens and units, from 21 to 99.
Between hundreds and tens or units, from 101 to 999.
Between thousands and tens or units
(Examples: two thousand and thirty , five thousand and eight ).
Between thousands and hundreds, only if there are dozens or units
(Example: eight thousand and four hundred ).
Between millions and tens or units
(Example: two million and thirty-one ).
We can use a regular expression to find the position where there should be an "e". regex101.com
/
# Definição de ordens
(?(DEFINE)(?P<unidades> um|dois|tr[eê]s|quatro|cinco|meia|se(?:is|te)|oito|nove ))
(?(DEFINE)(?P<dezenas_dez> d(?:ez(?:e(?:nove|sse(?:is|te))|oito)?|oze)|onze|treze|catorze|quinze ))
(?(DEFINE)(?P<dezenas_exceto_dez> vinte|trinta|(?:quar|cinqu|se(?:ss|t)|oit|nov)enta ))
(?(DEFINE)(?P<dezenas> (?P>dezenas_dez)|(?P>dezenas_exceto_dez) ))
(?(DEFINE)(?P<dezenas_ou_unidades> (?P>dezenas)|(?P>unidades) ))
(?(DEFINE)(?P<centenas> ce(?:m|ntos?)|(?:qu(?:atroc|inh)|(?:du|tre|se(?:is|te)|oito|nove)[cz])entos? ))
(?(DEFINE)(?P<mil> mil ))
(?(DEFINE)(?P<milhoes> (?:(?:[mb]|tr)i|qu(?:atri|inqua))lh(?:[aã]o|[oõ]es) ))
(?(DEFINE)(?P<s> [ ]+ ))
(?(DEFINE)(?P<e> e(?P>s) ))
#Regras
\b(?:
#1. Entre dezenas>=20 e unidades
(?P>dezenas_exceto_dez)(?P>s)(?=(?P>unidades)\b)
|
#2. Entre centenas e dezenas ou unidades
(?P>centenas)(?P>s)(?=(?P>dezenas_ou_unidades)\b)
|
#3-4. Entre milhares...
(?P>mil)(?P>s)(?=
(?:
#3. ... e dezenas ou unidades
(?P>dezenas_ou_unidades)
|
#4. ... e centenas, somente se não houver dezenas ou unidades
(?P>centenas)(?!(?P>s)(?P>e)?(?P>dezenas_ou_unidades)\b)
)
\b
)
|
#5. Entre milhões e dezenas ou unidades (que não sejam unidades ou dezenas de milhões ou unidades ou dezenas de milhares)
(?P>milhoes)(?P>s)(?=
(?P>dezenas_ou_unidades)(?!(?P>s)(?P>e)?(?:(?P>unidades)(?P>s)(?P>e)?)?(?:(?P>milhoes)|(?P>mil))\b)\b
)
)
\b
#Reset
\K
/iux
Or, in a single line, no comments:
/(?(DEFINE)(?P<unidades>um|dois|tr[eê]s|quatro|cinco|meia|se(?:is|te)|oito|nove))(?(DEFINE)(?P<dezenas_dez>d(?:ez(?:e(?:nove|sse(?:is|te))|oito)?|oze)|onze|treze|catorze|quinze))(?(DEFINE)(?P<dezenas_exceto_dez>vinte|trinta|(?:quar|cinqu|se(?:ss|t)|oit|nov)enta))(?(DEFINE)(?P<dezenas>(?P>dezenas_dez)|(?P>dezenas_exceto_dez)))(?(DEFINE)(?P<dezenas_ou_unidades>(?P>dezenas)|(?P>unidades)))(?(DEFINE)(?P<centenas>ce(?:m|ntos?)|(?:qu(?:atroc|inh)|(?:du|tre|se(?:is|te)|oito|nove)[cz])entos?))(?(DEFINE)(?P<mil>mil))(?(DEFINE)(?P<milhoes>(?:(?:[mb]|tr)i|qu(?:atri|inqua))lh(?:[aã]o|[oõ]es)))(?(DEFINE)(?P<s> +))(?(DEFINE)(?P<e>e(?P>s)))\b(?:(?P>dezenas_exceto_dez)(?P>s)(?=(?P>unidades)\b)|(?P>centenas)(?P>s)(?=(?P>dezenas_ou_unidades)\b)|(?P>mil)(?P>s)(?=(?:(?P>dezenas_ou_unidades)|(?P>centenas)(?!(?P>s)(?P>e)?(?P>dezenas_ou_unidades)\b))\b)|(?P>milhoes)(?P>s)(?=(?P>dezenas_ou_unidades)(?!(?P>s)(?P>e)?(?:(?P>unidades)(?P>s)(?P>e)?)?(?:(?P>milhoes)|(?P>mil))\b)\b))\b\K/iux
Substituting text: 'e '
.
I set the limit for quinquallhão (10 ^ 18). You can either complete it, or use:
(?(DEFINE)(?P<milhoes>\w+lh(?:[aã]o|[oõ]es)))
as a generalized case.
Code:
<?php // https://pt.stackoverflow.com/q/257031/53463
$re = '/
# Definição de ordens
(?(DEFINE)(?P<unidades> um|dois|tr[eê]s|quatro|cinco|meia|se(?:is|te)|oito|nove ))
(?(DEFINE)(?P<dezenas_dez> d(?:ez(?:e(?:nove|sse(?:is|te))|oito)?|oze)|onze|treze|catorze|quinze ))
(?(DEFINE)(?P<dezenas_exceto_dez> vinte|trinta|(?:quar|cinqu|se(?:ss|t)|oit|nov)enta ))
(?(DEFINE)(?P<dezenas> (?P>dezenas_dez)|(?P>dezenas_exceto_dez) ))
(?(DEFINE)(?P<dezenas_ou_unidades> (?P>dezenas)|(?P>unidades) ))
(?(DEFINE)(?P<centenas> ce(?:m|ntos?)|(?:qu(?:atroc|inh)|(?:du|tre|se(?:is|te)|oito|nove)[cz])entos? ))
(?(DEFINE)(?P<mil> mil ))
(?(DEFINE)(?P<milhoes> (?:(?:[mb]|tr)i|qu(?:atri|inqua))lh(?:[aã]o|[oõ]es) ))
(?(DEFINE)(?P<s> [ ]+ ))
(?(DEFINE)(?P<e> e(?P>s) ))
#Regras
\b(?:
#1. Entre dezenas>=20 e unidades
(?P>dezenas_exceto_dez)(?P>s)(?=(?P>unidades)\b)
|
#2. Entre centenas e dezenas ou unidades
(?P>centenas)(?P>s)(?=(?P>dezenas_ou_unidades)\b)
|
#3-4. Entre milhares...
(?P>mil)(?P>s)(?=
(?:
#3. ... e dezenas ou unidades
(?P>dezenas_ou_unidades)
|
#4. ... e centenas, somente se não houver dezenas ou unidades
(?P>centenas)(?!(?P>s)(?P>e)?(?P>dezenas_ou_unidades)\b)
)
\b
)
|
#5. Entre milhões e dezenas ou unidades (que não sejam unidades ou dezenas de milhões ou unidades ou dezenas de milhares)
(?P>milhoes)(?P>s)(?=
(?P>dezenas_ou_unidades)(?!(?P>s)(?P>e)?(?:(?P>unidades)(?P>s)(?P>e)?)?(?:(?P>milhoes)|(?P>mil))\b)\b
)
)
\b
#Reset
\K
/iux';
$subst = 'e ';
//Exemplos
$teste = [
'vinte cinco',
'vinte cinco mil',
'vinte cinco reais',
'vinte cinco reais e vinte cinco centavos',
'aaa bbb ccc',
'dois mil trezentos',
'dois mil trezentos quarenta cinco',
'dois milhões trinta um',
'dois milhões trinta um mil',
'dois milhões trinta mil',
'dois milhões trinta um',
'cento vinte três milhões quatrocentos cinquenta seis mil setecentos oitenta nove'
];
//Substituir
$resultado = preg_replace($re, $subst, $teste);
//Mostrar em uma única matriz
$resultado_final = array_combine( $teste, $resultado);
var_export($resultado_final);
Result:
array (
'vinte cinco' => 'vinte e cinco',
'vinte cinco mil' => 'vinte e cinco mil',
'vinte cinco reais' => 'vinte e cinco reais',
'vinte cinco reais e vinte cinco centavos' => 'vinte e cinco reais e vinte e cinco centavos',
'aaa bbb ccc' => 'aaa bbb ccc',
'dois mil trezentos' => 'dois mil e trezentos',
'dois mil trezentos quarenta cinco' => 'dois mil trezentos e quarenta e cinco',
'dois milhões trinta um' => 'dois milhões e trinta e um',
'dois milhões trinta um mil' => 'dois milhões trinta e um mil',
'dois milhões trinta mil' => 'dois milhões trinta mil',
'cento vinte três milhões quatrocentos cinquenta seis mil setecentos oitenta nove' => 'cento e vinte e três milhões quatrocentos e cinquenta e seis mil setecentos e oitenta e nove',
)
ideone.com