Come posso stampare il numero più lungo in una stringa?


11

Sto cercando un metodo per stampare il numero più lungo in una stringa.

Ad esempio: se ho la stringa

212334123434test233

come posso stampare

212334123434

?

Nota: sto cercando la sequenza di numeri continua più lunga, non il valore numericamente più alto.


Modifica: grazie per le risposte, a tutti. La risposta a questa domanda è stata abbastanza travolgente. Ho contrassegnato il post di @ HaukeLaging come risposta accettata perché si adattava molto bene al mio caso specifico, ma vorrei sottolineare che tutte le risposte sono ugualmente valide. È sempre bello avere diverse opzioni per risolvere un problema.


Cosa vuoi che faccia il metodo quando ci sono più sequenze continue ugualmente lunghe? Prendi il primo? L'ultimo? Uno a caso?
Anthon,

@Anthon Huh, non ci avevo pensato. Fortunatamente questo non è un problema nel mio caso specifico. Immagino che una qualsiasi delle opzioni andrebbe bene.
Glutanimate,

3
Nota che la risposta che hai accettato (e tutti gli altri finora tranne uno ) non tratterà con i numeri decimali. Non so se questo sia un problema per te.
terdon

@terdon: Non è un problema nel mio caso specifico perché ho a che fare con ID piuttosto che numeri reali, ma vorrei ringraziarti comunque per la tua risposta! Sono sicuro che qualcun altro lo troverà molto utile in futuro.
Glutanimate,

Vorresti che la soluzione fosse in grado di gestire numeri negativi? E se è così - il segno meno conta per la lunghezza?
Floris,

Risposte:


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

212334123434

13

Credo che si può fare questo con un solo grep, sorte tailpure. Ecco alcune stringhe di esempio.

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

Dov'è la <str>nostra stringa in questione.

Esempio

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

Ora, se eseguo questi tramite il mio grep ...comando a turno.

$ 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

Questo approccio funziona selezionando tutte le sottostringhe che sono sequenze di cifre. Ordiniamo quindi numericamente questo output sort -n, e quindi prendiamo l'ultimo valore nell'elenco, usando tail -1. Questa sarà la sottostringa più lunga.

Puoi vedere come funziona togliendo tail -1e rilanciando uno degli esempi:

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

Stringhe che iniziano con zeri

L'approccio di cui sopra funziona per ogni situazione che potrei concepire tranne una. @terdon ha menzionato nella chat questo scenario che ostacola l'approccio sopra.

  • 0000000000001
  • 2

Quindi, per far fronte a questo, dovrai cambiare leggermente le tattiche. Il kernel dell'approccio sopra può ancora essere sfruttato, tuttavia è necessario iniettare anche il numero di caratteri nei risultati. Ciò consente di ordinare i risultati in base al numero di caratteri nelle stringhe e ai loro valori.

$ 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

risultati:

$ 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

Puoi condensarlo un po 'sfruttando la capacità di Bash di determinare la lunghezza di una variabile 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

Ho optato per l'uso grep -P ...sopra perché io, essendo uno sviluppatore Perl, mi piace la sintassi della classe di dire tutte le cifre in questo modo:, \d+invece di [[:digit:]]\+o [0-9]\+. Ma per questo particolare problema non è davvero necessario. Potresti scambiare facilmente l' grepho usato così:

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

Per esempio:

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

2
Usare ${#i}per ottenere la lunghezza della stringa può farti risparmiare la chiamata wc, se vuoi andare in base alla bash
glenn jackman,

@glennjackman - grazie aggiunto vostro miglioramento al mio A 8-)
SLM

GNU grep 2.16 (almeno) afferma che -P è "altamente sperimentale". Puoi usare grep -o "[0-9]\+"invece digrep -oP "\d+"
David Conrad il

1
@DavidConrad - aggiunto anche questi dettagli alla A, grazie!
slm

8

Una soluzione in perl:

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

Riferimenti


2
Adoro una bella trasformazione Schwartziana!
Glenn Jackman,

7

Utilizzando python con la stringa passata sulla riga di comando e supponendo che si desideri la prima sequenza di lunghezza massima:

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

7

Ecco un altro approccio Perl che può gestire sia i decimali che i numeri interi:

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

Si noti che nessuna delle risposte finora pubblicate tratterà dei decimali e poiché si specifica che si desidera il numero più lungo e non il numero numericamente maggiore, suppongo che in realtà siano necessari decimali.

Spiegazione

  • perl -lne: -nSignifica "leggi l'input riga per riga ed esegui lo script fornito da -eesso". Il -laggiunge un ritorno a capo ad ogni printchiamata (e altre cose che non interessano qui).
  • while(/([\d.]+)/g): scorre tutti i numeri ( \dsignifica [0-9], quindi [\d.]corrisponderà alle cifre e .. Se vuoi anche trovare numeri negativi, aggiungi -. Le parentesi catturano la stringa corrispondente come quella $1usata nel passaggio successivo.
  • $max=$1 if length($1) > length($max): Se la lunghezza della partita corrente è maggiore della più lunga finora ( $max), salva la partita come $max.
  • print $max: stampa la stringa di numeri più lunga trovata. Questo verrà eseguito al termine del ciclo while, quindi dopo aver trovato tutti i numeri.

1
+1 Il tuo regex è un po 'troppo generico, però. Ad esempio, corrisponderebbe agli indirizzi IP. Propongo \D(\d+(?:\.\d+)?)\Dinvece qualcosa del genere .
Joseph R.

Dovrebbe funzionare anche senza le \Dancore ...
Joseph R.

@JosephR. hmm, vero, non avevo considerato consecutivo .come negli indirizzi IP.
terdon

6

Dato

str="212334123434test233"

poi in bash

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

Una soluzione bash forse più pura che utilizza un array costruito sostituendo i caratteri non numerici nella stringa con spazi bianchi, al posto del grep

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

4

Sulla base della risposta di @mikeserv, ecco un'altra alternativa. Estrae i numeri (secondo il metodo di Mikeserv), quindi li ordina in ordine numerico e prende l'ultimo. Escludendo gli zeri iniziali, questo ti darà il numero più grande (non tenendo conto del segno):

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

Questo funziona davvero - il mio no. Ho avuto il "\ r" dalla parte sbagliata! Ho intenzione di eliminarlo. Puoi anche usare la shell come -set -- $(echo $str | tr ... ) ; b=${#1} ; for d ; do [ ${#d} -gt $b ] && b=${#d} n=$d ; done ; echo $n
mikeserv

1
Ho cancellato il mio post orribile e mi hai trattato abbastanza delicatamente di me. Dal momento che stai già utilizzando lo stesso tr, non avrei alcun rancore se avessi incorporato quanto sopra. Probabilmente sort è più veloce, ma, di nuovo, attende che lo stream finisca come il $(subshell). Non lo so. In ogni caso, la tua è già un'ottima risposta, ma se hai voglia di aggiungere la shell loop sopra, sentiti libero. E comunque - è possibile che tu possa fare a meno sortcon un po 'di gestione creativa wc -Le teein streaming ... Ho finito con questa domanda però - Sono imbarazzato.
Mikeserv,

Un'ultima cosa però: potresti anche estrarre trla subshell e liberartene printf. Fallo e basta '0-9' '\n'.
Mikeserv,

@mikeserv - la cosa buona di questo sito è che impariamo gli uni dagli altri. Grazie per l'aiuto; senza la tua risposta non avrei nemmeno iniziato da solo ...
Floris,

2

bash e ordinamento GNU

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

2

Usa caratteri non numerici per dividere la stringa e trova la sequenza più lunga o il valore numerico più grande (per numeri di uguale lunghezza) con un operatore 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

Puoi anche impostare il separatore di record ( RS) di awk come qualsiasi stringa di caratteri non numerica:

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

2
Perché non semplicemente impostare RS = '[^0-9]+'e utilizzare il ciclo intrinseco di Awk? echo "212334123434test233" | awk -v RS='[^0-9]+' 'length(longest) < length($0) {longest = $0};END{print longest}' 212334123434

@awk_FTW dovresti scrivere anche questa come risposta. :) Grazie per avermi mostrato la RSvariabile, devo ammettere che è la prima volta che la vedo. Hai più suggerimenti da offrire awkrispetto a me ahahah!
hjk
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.