Meu amigo DGmike publicou: Retornando o último número (script PHP)
Como eu acho interessante comparar soluções em linguagens diferentes, resolvi escrever o mesmo script em Python. Veja como ficou:
import re
def ultimoNumero(string):
return re.findall(r"\d+",string)[-1]
Gostou?
Publicado por
Elcio
Elcio é sócio fundador da Visie Padrões Web. Pioneiro no uso e divulgação dos padrões do W3C no Brasil, Elcio já treinou equipes de dezenas de empresas como Globo.com, Terra, Petrobras, iG e Locaweb. Além disso, tem dirigido as equipes da Visie no desenvolvimento de projetos web para marcas como Brastemp, Itaú Unibanco, Johnson & Johnson e Rede Globo.
Ver todos os posts de Elcio
No meu blog, o junior deu uma solução interessante e eu fiz alguns testes de velocidade (os resultados estão no meu blog). Parece que a solução dele foi melhor, além de ser um pouco mais enxuta. Então eu a adaptei a uma função:
function ultimo_numero ($string = '')
{
if ('string' === gettype ($string))
{
return;
}
$string = preg_split ( '/[^\d]+/', $string );
return end ( $string );
}
Se retirar esse if, a função sempre retornará uma string (verifiquei isso usando o var_dump() )
ops, esqueci de escapar:
>>> def ultimo(string):
… a = re.search(r'(?P<ultimo>\d+)[^\d]*$’, string)
… return a and a.group(‘ultimo’)
…
>>> ultimo(‘dsadasdas22222dddd233’)
‘233’
>>> ultimo(‘dsadasdas22222dddd’)
‘22222’
>>> ultimo(‘1a’)
‘1’
>>> ultimo(‘1a2’)
‘2’
gostei mais da sua, mas aqui vai a minha:
>>> def ultimo(string):
… a = re.search(r'(?P\d+)[^\d]*$’, string)
… return a and a.group(‘ultimo’)
…
>>> ultimo(‘dsadasdas22222dddd233’)
‘233’
>>> ultimo(‘dsadasdas22222dddd’)
‘22222’
>>> ultimo(‘1a’)
‘1’
>>> ultimo(‘1a2’)
‘2’
DGmike, o que eu quis dizer sobre o tratamento do tipo de dados era mais sobre a entrada, que você verificava se o parâmetro passado era string… no retorno não sei se dar um parâmetro opcional vale a pena (em PHP não bastaria um (int)ultimoNum($x)?). Então até por questões de comparação, acho mais justo não colocar isso apenas no PHP, fica injustamente maior quando no fim a linguagem permitiria converter o resultado pra int com a mesma facilidade que as outras 😉
E aqui uma versão em JavaScript:
function ultimoNum(string) {
var match = string.match(/(\d+)\D*$/);
if (match) {
return match[1];
}
}
Ela também teve um pequeno toque específico à linguagem: só dei return caso um número seja encontrado, para que caso contrário a função retorne undefined em vez de null ou string em branco, o que me pareceu mais apropriado 🙂 Mas se a briga for por número de linhas de código, dá pra reduzir o return a um condicional como os outros, retornando até mesmo undefined, estando disposto a deixar o código um pouco menos legível pra quem não está habituado à linguagem.
PHP ::
function ultimoNumero($string){
preg_match_all(‘/\d+/’, $string, $p);
return $p[0][count($p[0])-1];
}
E a versão resumida-filha-da-mae-num-consigo-entender-nada em php.
function ultimoNum ($s='',$rs=true){
$s=preg_match_all('\d+',(string) $s,$m);
$r=end($m[0])?end($m[0]):'';
if($rs)return (string) $r;
else return (int) $r;
}
Hmmm… como ficaria em javaScript?
Então… a versão em PHP melhorada, com base no que o Daniel disse:
function ultimo_numero ($string = '', $retornarString = true)
{
$string = (string) $string;
$string = preg_match_all ('(\d+)', $string, $match);
$return = end($match[0]) ? end($match[0]) : '';
if ($retornarString)
{
return (string) $return;
} else {
return (int) $return;
}
}
Meio complexo, mas funciona sempre retornando uma string ou um inteiro, para você poder utilizar como você quiser. Ficou um pouco mais complicada e maior, mas é bem mais completa. 😀
Aliás… uma daquelas soluções que você bate na cabeça e diz “por que não pensei nisso antes?”
def ultimoNumero(string):
match = re.search(r'(\d+)\D*$', string)
return match.group(1) if match else None
Ou em Ruby:
def ultimo_numero(string)
string =~ /(\d+)\D*\Z/ ? $1 : nil
end
Mais simples e computacionalmente mais leve 🙂
Infinitas vezes melhor!
🙂
Uma maneira simples de se resolver a questão é envolver o findall num valor alternativo:
return (re.findall(…) or [None])[-1]
(usei None, mas poderia ser trocando por uma string em branco se desejado)
Ainda falta a questão de verificar o tipo de dados, mas aqui acho interessante comentar: PHP faz conversão implícita de tipos de dados, resultando numa prática bastante comum de passar dados sem convertê-los para o que se realmente deseja (o mais comum, na minha experiência, é tratar strings como se fossem números). Python, por sua vez, nunca faz conversão imlpícita (apenas coerção de tipos numéricos: você pode fazer contas misturando ints e floats, etc), e assim você na prática dificilmente tem em mãos um valor que não é do tipo que se espera. Por isso, acho que numa versão Python o teste do tipo do parâmetro passado é desnecessário. Outro detalhe também é que Python favorece exceções em vez de ignorar erros silenciosamente, então seria até mais “correto” deixar a função findall gerar uma exceção quando o dado passado não for string. 😉
E pra fins comparativos (também adoro esse tipo de comparação), uma versão equivalente em Ruby:
def ultimo_numero(string)
string.scan(/\d+/).pop
end
Também retornando nil em caso vazio 🙂
A possibilidade de fazer funcao_ou_metodo()[chave] é uma das coisas que fazem (bastante, até) falta em PHP.
Hmmm…
e se não tiver qualquer número na string? Ele retorna uma string vazia? Já fez o teste com unitTest?