Como posso imprimir o número mais longo em uma string?


11

Estou procurando um método para imprimir o número mais longo em uma string.

Por exemplo: se eu tiver a string

212334123434test233

como posso imprimir

212334123434

?

Nota: estou procurando a sequência contínua mais longa de números, não o valor numericamente mais alto.


Edit: Obrigado pelas respostas, pessoal. A resposta a esta pergunta foi bastante esmagadora. Marquei a postagem de @ HaukeLaging como a resposta aceita, pois se adequava muito bem ao meu caso específico, mas gostaria de ressaltar que todas as respostas são igualmente válidas. É sempre bom ter várias opções diferentes para resolver um problema.


O que você deseja que o método faça quando houver várias seqüências contínuas igualmente longas? Pegue o primeiro? O último? Um aleatório?
Anthon

@ Anton Huh, eu não tinha pensado nisso. Felizmente, isso não é um problema no meu caso específico. Eu acho que qualquer uma das opções estaria bem.
precisa saber é o seguinte

3
Observe que a resposta que você aceitou (e todas as outras até agora, exceto uma ) não lidará com números decimais. Não sei se isso é um problema para você.
terdon

@terdon: Não é um problema no meu caso específico, porque estou lidando com identificações em vez de números reais, mas, no entanto, gostaria de agradecer sua resposta! Estou certo de que alguém o achará muito útil no futuro.
Glutanimate

Deseja que a solução seja capaz de lidar com números negativos? E se sim - o sinal de menos conta para o comprimento?
Floris

Respostas:


7
echo 212334123434test233abc44 | 
awk '{gsub("[^0-9]+","\n"); print;}' | 
awk '{ if (length($0) > max) {max = length($0); maxline = $0} } 
  END { print maxline }'

212334123434

13

Eu acredito que você pode fazer isso com apenas grep, sorte tailtão bem. Aqui estão alguns exemplos de strings.

$ echo <str> | grep -oP "\d+" | sort -n | tail -1

Onde <str>está a nossa string em questão.

Exemplo

$ set -o posix; set | grep "str[0-9]"
str0=212334123434test233
str1=212334123434test233abc44
str2=233test212334123434
str3=a212334123434test233abc44
str4=a91234b212334123434abc

Agora, se eu executar estes através do meu grep ...comando, por sua vez.

$ echo $str0 | grep -oP "\d+" | sort -n | tail -1
212334123434
$ echo $str1 | grep -oP "\d+" | sort -n | tail -1
212334123434
$ echo $str2 | grep -oP "\d+" | sort -n | tail -1
212334123434
$ echo $str3 | grep -oP "\d+" | sort -n | tail -1
212334123434
$ echo $str4 | grep -oP "\d+" | sort -n | tail -1
212334123434

Essa abordagem funciona selecionando todas as substrings que são sequências de dígitos. Em seguida, classificamos essa saída numericamente, sort -ne, em seguida, obtemos o último valor da lista, usando tail -1. Essa será a substring mais longa.

Você pode ver como ele funciona tail -1desativando e executando novamente um dos exemplos:

$ echo $str4 | grep -oP "\d+" | sort -n
91234
212334123434

Sequências que começam com zeros

A abordagem acima funciona para todas as situações que pude conceber, exceto uma. O @terdon mencionou no chat esse cenário que frustra a abordagem acima.

  • 0000000000001
  • 2

Então, para lidar com isso, você precisará mudar um pouco as táticas. O núcleo da abordagem acima ainda pode ser alavancado, no entanto, precisamos injetar o número de caracteres nos resultados também. Isso permite classificar os resultados por número de caracteres nas strings e seus valores.

$ for i in $(echo $str0 | grep -oP "\d+");do a=$(echo "$i" | wc -c); \
    echo "$a $i"; done | sort -n | tail -1 | cut -d" " -f2

Resultados:

$ echo $str0
0000000000001a2test

$ for i in $(echo $str0 | grep -oP "\d+");do a=$(echo "$i" | wc -c); \
    echo "$a $i"; done | sort -n | tail -1 | cut -d" " -f2
0000000000001

Você pode condensar um pouco isso usando a capacidade do Bash para determinar o comprimento de uma variável usando ${#var}.

$ for i in $(echo $str0 | grep -oP "\d+");do echo "${#i} $i"; done | \
    sort -n | tail -1 | cut -d" " -f2
0000000000001

Usando `grep -P

Eu optei por usar grep -P ...acima porque eu, como desenvolvedor Perl, gosto da sintaxe da classe de dizer todos os dígitos assim: em \d+vez de [[:digit:]]\+ou [0-9]\+. Mas, para esse problema em particular, não é realmente necessário. Você poderia facilmente trocar o grepque eu usei assim:

$ .... grep -o "[0-9]\+" ....

Por exemplo:

$ for i in $(echo $str0 | grep -o "[0-9]\+");do echo "${#i} $i"; done | \
    sort -n | tail -1 | cut -d" " -f2
0000000000001

2
Usando ${#i}para obter o comprimento da corda pode poupar chamar wc, se você quiser ir específicas de festa
Glenn Jackman

@glennjackman - obrigado adicionou sua melhoria à minha A 8-)
slm

O GNU grep 2.16 (pelo menos) diz que -P é "altamente experimental". Você pode usar em grep -o "[0-9]\+"vez degrep -oP "\d+"
David Conrad

1
@DavidConrad - adicionou esses detalhes também ao A, obrigado!
Slm

8

Uma solução em perl:

echo 212334123434test233abc44 |
perl -nle 'print ((
    map { $_->[0] }
    sort{ $a->[1] <=> $b->[1] }
    map { [$_,length] }
    split /\D+/, $_)[-1]
    )'
212334123434

Referências


2
Ame uma boa transformação Schwartzian!
Glenn Jackman

7

Usando python com a string passada na linha de comando e supondo que você queira a primeira sequência de tamanho máximo:

import sys

longest = current = ""
for x in sys.argv[1]:
    if current and not x.isdigit():
        if len(current) > len(longest):
            longest = current
        current = ""
    else:
        current += x 
print(longest)

2
ou python -c "import re,sys; print max(re.split(r'\D+', sys.argv[1]), key=len)"
tersely

7

Aqui está outra abordagem Perl que pode lidar com decimais e números inteiros:

echo "0.212334123434test233" | 
 perl -lne 'while(/([\d.]+)/g){$max=$1 if length($1) > length($max)} print $max'

Observe que nenhuma das respostas postadas até agora tratará de decimais e, como você especifica que deseja o número mais longo e não o maior numericamente, presumo que você realmente precise de decimais.

Explicação

  • perl -lne: Os -nmeios "leem a entrada linha por linha e executam o script fornecido por -eela". O -ladiciona uma nova linha a cada printchamada (e outras coisas não relevantes aqui).
  • while(/([\d.]+)/g): itera através de todos os números ( \dsignifica [0-9], portanto [\d.], coincidirá com dígitos e .. Se você também quiser encontrar números negativos, adicione -. Os parênteses capturam a sequência correspondente como $1é usada na próxima etapa.
  • $max=$1 if length($1) > length($max): Se a duração da correspondência atual for maior que a mais longa até agora ( $max), salve a correspondência como $max.
  • print $max: imprime a maior seqüência de números encontrada. Isso será executado depois que o loop while terminar, portanto, depois que todos os números forem encontrados.

1
+1 Entretanto, seu regex é um pouco genérico demais. Ele corresponderia a endereços IP, por exemplo. Proponho algo como isso \D(\d+(?:\.\d+)?)\D.
Joseph R.

Também deve funcionar sem as \Dâncoras ...
Joseph R.

@JosephR. hmm, é verdade, eu não tinha considerado consecutivos .como nos endereços IP.
terdon

6

Dado

str="212334123434test233"

então no bash

max=""
while read num; do 
  (( ${#num} > ${#max} )) && max=$num
done < <(grep -Eo '[0-9]+' <<< "$str")
echo $max
212334123434

Uma solução bash possivelmente mais pura usando uma matriz construída substituindo caracteres não dígitos na cadeia de caracteres por espaço em branco, no lugar do grep

max=""
declare -a nums="${str//[^[:digit:]]/ }"
for num in ${nums[@]}; do 
  (( ${#num} > ${#max} )) && max=$num
done
echo $max

4

Com base na resposta de @mikeserv, aqui está outra alternativa. Extrai os números (pelo método de mikeserv), depois os classifica em ordem numérica e pega o último. Com exceção de zeros à esquerda, você obterá o maior número (sem considerar o sinal):

echo 1111askdlfm2234 |  printf %s\\n $(tr -sc 0-9 \ ) | sort -n | tail -1

Este realmente funciona - o meu não. Eu tinha o '\ r' do lado errado! Eu vou deletá-lo. Você também pode usar o shell comoset -- $(echo $str | tr ... ) ; b=${#1} ; for d ; do [ ${#d} -gt $b ] && b=${#d} n=$d ; done ; echo $n
#

1
Eu apaguei meu próprio post horrível, e você lidou gentilmente comigo. Como você já está usando de trqualquer maneira, eu não guardaria rancor se você incorporasse o item acima. Provavelmente sort é mais rápido, mas, novamente, aguarda o término do fluxo da mesma forma que o $(subshell). Eu não sei. De qualquer forma, a sua já é uma excelente resposta, mas se você quiser adicionar o loop de shell acima, fique à vontade é tudo o que estou dizendo. E, a propósito - é possível que você não tenha sortcomo lidar com um pouco de criatividade wc -Le teefluxo - acabei com essa pergunta - estou envergonhado.
precisa saber é o seguinte

Uma última coisa, porém - é melhor você trsair do subconjunto e se livrar dele printf. Apenas faça '0-9' '\n'.
precisa saber é o seguinte

@mikeserv - o lado bom deste site é que aprendemos um com o outro. Obrigado pela ajuda; sem a sua resposta que eu nem sequer ter começado em meu próprio ...
Floris

2

classificação bash e GNU

IFS=$'\0' read -r l _ < <(tr -cs '[:digit:]' '[\0*]' <<<'11abcde1234556ghijk22'| sort -znr)
echo $l
1234556

2

Use caracteres não numéricos para dividir a sequência e encontre a sequência mais longa ou o maior valor numérico (para números de igual comprimento) com um operador ternário.

$ echo "212334123434test233" | awk -F'[^0-9]+' '{for(i=1;i<=NF;i++){m=length($i)>=length(m)||$i>m?$i:m}};END{print m}'
212334123434

Você também pode definir o separador de registros do awk ( RS) como qualquer sequência de caracteres não numéricos:

$ echo "212334123434test233" \
    | awk -v RS='[^0-9]+' '
        length(longest) < length($0) {longest = $0};
        END{print longest}'
212334123434

2
Por que não apenas definir RS = '[^0-9]+'e usar o loop inerente do Awk? echo "212334123434test233" | awk -v RS='[^0-9]+' 'length(longest) < length($0) {longest = $0};END{print longest}' 212334123434

@awk_FTW você deve colocar isso como resposta também. :) Obrigado por me mostrar a RSvariável, devo admitir que é a primeira vez que a vejo. Você tem mais dicas a oferecer do awkque eu hahaha!
HJK
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.