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.