¿Cómo puedo imprimir el número más largo en una cadena?

11

Estoy buscando un método para imprimir el número más largo en una cadena.

Por ejemplo: si tengo la cuerda

212334123434test233

como puedo imprimir

212334123434

?

Nota: Estoy buscando la secuencia continua de números más larga, no el valor numéricamente más alto.


Editar: Gracias por las respuestas, todos. La respuesta a esta pregunta ha sido bastante abrumadora. Marqué la publicación de @ HaukeLaging como la respuesta aceptada porque se adaptaba muy bien a mi caso específico, pero me gustaría señalar que todas las respuestas son igualmente válidas. Siempre es bueno tener varias opciones diferentes para resolver un problema.

Glutanimato
fuente
¿Qué quiere que haga el método cuando hay múltiples secuencias continuas igualmente largas? ¿Tomar la primera? ¿El último? Uno al azar?
Anthon
@Anthon Huh, no había pensado en eso. Afortunadamente, eso no es un problema en mi caso específico. Supongo que cualquiera de las opciones estaría bien.
Glutanimate
3
Tenga en cuenta que la respuesta que ha aceptado (y todas las demás hasta ahora excepto una ) no tratará con números decimales. No sé si eso es un problema para ti.
terdon
@terdon: No es un problema en mi caso específico porque estoy tratando con identificaciones en lugar de números reales, ¡pero de todos modos me gustaría agradecerle su respuesta! Estoy seguro de que alguien más lo encontrará muy útil en el futuro.
Glutanimate
¿Te gustaría que la solución sea capaz de lidiar con números negativos? Y si es así, ¿el signo menos cuenta para la longitud?
Floris

Respuestas:

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

212334123434
Hauke ​​Laging
fuente
13

Creo que se puede hacer esto con solo grep, sorty tailasí. Aquí hay algunos ejemplos de cadenas.

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

¿Dónde <str>está nuestra cadena en cuestión?

Ejemplo

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

Ahora si ejecuto estos a través de mi grep ...comando a su 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

Este enfoque funciona seleccionando todas las subcadenas que son secuencias de dígitos. Luego ordenamos esta salida numéricamente, sort -ny luego tomamos el último valor de la lista, usando tail -1. Esta será la subcadena más larga.

Puede ver cómo funciona tail -1despegando y volviendo a ejecutar uno de los ejemplos:

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

Cadenas que comienzan con ceros

El enfoque anterior funciona para todas las situaciones que podría concebir, excepto una. @terdon mencionó en el chat este escenario que frustra el enfoque anterior.

  • 0000000000001
  • 2

Entonces, para lidiar con esto, deberá cambiar ligeramente las tácticas. El núcleo del enfoque anterior todavía se puede aprovechar, sin embargo, también debemos inyectar el número de caracteres en los resultados. Esto le da a la capacidad de ordenar los resultados por número de caracteres en las cadenas y sus 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

Puede condensar esto un poco haciendo uso de la capacidad de Bash para determinar la longitud de una variable 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

He optado por usarlo grep -P ...anteriormente porque, como desarrollador de Perl, me gusta la sintaxis de clase de decir todos los dígitos de esta manera: en \d+lugar de [[:digit:]]\+o [0-9]\+. Pero para este problema en particular no es realmente necesario. Podrías cambiar fácilmente el grepque he usado así:

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

Por ejemplo:

$ for i in $(echo $str0 | grep -o "[0-9]\+");do echo "${#i} $i"; done | \
    sort -n | tail -1 | cut -d" " -f2
0000000000001
slm
fuente
2
Usar ${#i}para obtener la longitud de la cadena puede ahorrarle llamadas wc, si desea ir específico de bash
glenn jackman
@glennjackman - gracias agregó su mejora a mi A 8-)
slm
GNU grep 2.16 (al menos) dice -P es "altamente experimental". Puede usar en grep -o "[0-9]\+"lugar degrep -oP "\d+"
David Conrad
1
@DavidConrad: también agregó estos detalles a la A, ¡gracias!
slm
8

Una solución en perl:

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

Referencias

Cuonglm
fuente
2
Me encanta una bonita transformación Schwartzian!
Glenn Jackman
7

Usando python con la cadena pasada en la línea de comando y suponiendo que desea la primera secuencia de longitud máxima:

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)
Anthon
fuente
2
o python -c "import re,sys; print max(re.split(r'\D+', sys.argv[1]), key=len)"
tersely
7

Aquí hay otro enfoque de Perl que puede tratar con decimales y enteros:

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

Tenga en cuenta que ninguna de las respuestas publicadas hasta ahora tratará con decimales y dado que usted especifica que desea el número más largo y no el número numéricamente mayor, supongo que realmente necesita decimales.

Explicación

  • perl -lne: Los -nmedios "leen la entrada línea por línea y ejecutan el script dado por -eella". El -lañade una nueva línea para cada printllamada (y otras cosas que no vienen al caso).
  • while(/([\d.]+)/g): Iterar a través de todos los números ( \dmedios [0-9], por lo que [\d.]coincidirá con dígitos y .Si también quiere encontrar los números negativos, agregar. -Los paréntesis capturan la cadena coincidente como. $1Que se utiliza en el siguiente paso.
  • $max=$1 if length($1) > length($max): Si la duración de la coincidencia actual es mayor que la más larga hasta ahora ( $max) guarde la coincidencia como $max.
  • print $max: imprime la cadena más larga de números encontrados. Esto se ejecutará después de que finalice el ciclo while, así que después de que se hayan encontrado todos los números.
terdon
fuente
1
+1 Sin embargo, tu expresión regular es demasiado genérica. Coincidiría con las direcciones IP, por ejemplo. Propongo algo así en su \D(\d+(?:\.\d+)?)\Dlugar.
Joseph R.
También debería funcionar sin los \Danclajes ...
Joseph R.
@JosephR. hmm, cierto, no lo había considerado consecutivo .como en las direcciones IP.
terdon
6

Dado

str="212334123434test233"

luego en bash

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

Una solución bash posiblemente más pura que utiliza una matriz construida reemplazando caracteres que no son dígitos en la cadena con espacios en blanco, en lugar del grep

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

Sobre la base de la respuesta de @mikeserv, aquí hay otra alternativa. Extrae los números (según el método de mikeserv), luego los ordena en orden numérico y toma el último. Salvo los ceros iniciales, esto le dará el número más grande (sin tener en cuenta el signo):

echo 1111askdlfm2234 |  printf %s\\n $(tr -sc 0-9 \ ) | sort -n | tail -1
Floris
fuente
Este realmente funciona, el mío no. ¡Tenía el '\ r' en el lado equivocado! Lo voy a borrar. También puede usar el shell como -set -- $(echo $str | tr ... ) ; b=${#1} ; for d ; do [ ${#d} -gt $b ] && b=${#d} n=$d ; done ; echo $n
mikeserv
1
Eliminé mi propia publicación horrible, y me trataste lo suficientemente gentilmente. Como ya lo estás usando de trtodos modos, no guardaría rencor si incorporaste lo anterior. Probablemente sort sea ​​más rápido, pero, de nuevo, espera a que la transmisión termine igual que el $(subshell). No lo sé. En cualquier caso, la suya ya es una excelente respuesta, pero si tiene ganas de agregar el bucle de shell anterior, no dude en decirlo. Y, por cierto, es posible que pueda prescindir por sortcompleto con un poco de manejo creativo wc -Ly teeen la transmisión ... Sin embargo, terminé con esta pregunta, estoy avergonzado.
mikeserv
Sin embargo, una última cosa: también podría trsalir de la subshell y deshacerse de él printf. Solo hazlo '0-9' '\n'.
mikeserv
@mikeserv: lo bueno de este sitio es que aprendemos unos de otros. Gracias por tu ayuda; sin su respuesta, ni siquiera habría comenzado por mi cuenta ...
Floris
2

bash y GNU sort

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

Use caracteres no numéricos para dividir la cadena y encuentre la secuencia más larga o el valor numérico más grande (para números de igual longitud) con un operador ternario.

$ 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

También puede configurar el separador de registros de awk ( RS) para que sea cualquier cadena de caracteres no numérica:

$ echo "212334123434test233" \
    | awk -v RS='[^0-9]+' '
        length(longest) < length($0) {longest = $0};
        END{print longest}'
212334123434
hjk
fuente
2
¿Por qué no simplemente configurar RS = '[^0-9]+'y usar el bucle inherente de Awk? echo "212334123434test233" | awk -v RS='[^0-9]+' 'length(longest) < length($0) {longest = $0};END{print longest}' 212334123434
@awk_FTW también deberías dejar eso como respuesta. :) Gracias por mostrarme la RSvariable, debo admitir que es la primera vez que la veo. Tienes más consejos que ofrecer awkque yo jajaja!
hjk