Jak wydrukować najdłuższy numer w ciągu?

11

Szukam metody wydrukowania najdłuższej liczby w ciągu.

Np .: jeśli mam ciąg

212334123434test233

jak mogę wydrukować

212334123434

?

Uwaga: szukam najdłuższej ciągłej sekwencji liczb, a nie liczbowo wyższej wartości.


Edycja: Dziękujemy za odpowiedzi, wszyscy. Odpowiedź na to pytanie była przytłaczająca. Oznacziłem post @ HaukeLaging jako zaakceptowaną odpowiedź, ponieważ bardzo dobrze pasował do mojego konkretnego przypadku, ale chciałbym zauważyć, że wszystkie odpowiedzi są jednakowo prawidłowe. Zawsze świetnie jest mieć kilka różnych opcji rozwiązania problemu.

Glutanimate
źródło
Co chcesz zrobić, gdy metoda ma wiele równie długich ciągłych sekwencji? Weź pierwszy? Ostatni? Przypadkowy?
Anthon
@Anthon Huh, nie myślałem o tym. Na szczęście to nie jest problem w moim konkretnym przypadku. Myślę, że każda z opcji byłaby w porządku.
Glutanimate,
3
Zauważ, że odpowiedź, którą zaakceptowałeś (i wszystkie inne jak dotąd oprócz jednej ) nie dotyczy liczb dziesiętnych. Nie wiem, czy to dla ciebie problem.
terdon
@terdon: W moim konkretnym przypadku nie stanowi to problemu, ponieważ mam do czynienia z identyfikatorami, a nie z faktycznymi liczbami, ale mimo to chciałbym podziękować za odpowiedź! Jestem pewien, że ktoś inny uzna to za bardzo przydatne w przyszłości.
Glutanimate,
Czy chciałbyś, aby rozwiązanie poradziło sobie z liczbami ujemnymi? A jeśli tak - czy znak minus jest liczony do długości?
Floris

Odpowiedzi:

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
źródło
13

Wierzę, że można to zrobić tylko grep, sorti tailjak dobrze. Oto kilka przykładowych ciągów.

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

Gdzie <str>jest kwestionowany nasz ciąg?

Przykład

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

Teraz, jeśli przejrzę je grep ...kolejno po kolei.

$ 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

Takie podejście polega na wybraniu wszystkich podciągów, które są ciągami cyfr. Następnie sortujemy dane wyjściowe numerycznie, sort -na następnie pobieramy ostatnią wartość z listy za pomocą tail -1. To będzie najdłuższy podciąg.

Możesz zobaczyć, jak to działa, tail -1zdejmując i ponownie uruchamiając jeden z przykładów:

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

Ciągi zaczynające się od zer

Powyższe podejście działa w każdej sytuacji, z wyjątkiem jednej. @terdon wspomniał na czacie o tym scenariuszu, który udaremnia powyższe podejście.

  • 0000000000001
  • 2)

Aby sobie z tym poradzić, trzeba nieco zmienić taktykę. Jądro powyższego podejścia może być nadal wykorzystywane, jednak musimy również wprowadzić liczbę znaków do wyników. Daje to sortowi możliwość sortowania wyników według liczby znaków w łańcuchach i ich wartości.

$ 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

Wyniki:

$ 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

Możesz to trochę skondensować, wykorzystując zdolność Basha do określenia długości zmiennej za pomocą ${#var}.

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

Używanie `grep -P

Zdecydowałem się użyć grep -P ...powyższego, ponieważ jako programista Perla podoba mi się składnia klasowa wypowiadania wszystkich cyfr w ten sposób: \d+zamiast [[:digit:]]\+lub [0-9]\+. Ale w przypadku tego konkretnego problemu nie jest tak naprawdę potrzebny. Równie łatwo możesz wymienić grepużywane przeze mnie:

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

Na przykład:

$ for i in $(echo $str0 | grep -o "[0-9]\+");do echo "${#i} $i"; done | \
    sort -n | tail -1 | cut -d" " -f2
0000000000001
slm
źródło
2
Używanie ${#i}do uzyskania długości łańcucha może zaoszczędzić ci dzwonienia wc, jeśli chcesz przejść do konkretnej bash
glenn jackman
@glennjackman - dzięki dodałeś ulepszenie do mojego A 8-)
slm
GNU grep 2.16 (przynajmniej) mówi, że -P jest „wysoce eksperymentalny”. Możesz użyć grep -o "[0-9]\+"zamiastgrep -oP "\d+"
David Conrad
1
@DavidConrad - dodał te szczegóły również do A, dzięki!
slm
8

Rozwiązanie w perl:

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

Bibliografia

Cuonglm
źródło
2
Uwielbiam niezłą transformację Schwartzian!
glenn jackman
7

Za pomocą python z ciągiem przekazanym w wierszu polecenia i zakładając, że chcesz pierwszą sekwencję o maksymalnej długości:

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
źródło
2
lub zwięźlepython -c "import re,sys; print max(re.split(r'\D+', sys.argv[1]), key=len)"
iruvar
7

Oto inne podejście Perla, które może radzić sobie zarówno z liczbami dziesiętnymi, jak i liczbami całkowitymi:

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

Zauważ, że żadna z dotychczas opublikowanych odpowiedzi nie dotyczy miejsc po przecinku, a ponieważ określasz, że chcesz mieć najdłuższą, a nie największą liczbowo liczbę, zakładam, że faktycznie potrzebujesz miejsc po przecinku.

Wyjaśnienie

  • perl -lne: -nOznacza „czytaj wiersz po wierszu i uruchom podany przez -eniego skrypt ”. -lDodaje nowej linii do każdego printpołączenia (i innych rzeczy nie istotnych tutaj).
  • while(/([\d.]+)/g): iteruj po wszystkich liczbach ( \doznacza to [0-9], [\d.]że dopasują cyfry i .. Jeśli chcesz również znaleźć liczby ujemne, dodaj -. Nawiasy przechwytują dopasowany ciąg znaków, $1który jest używany w następnym kroku.
  • $max=$1 if length($1) > length($max): Jeśli długość bieżącego dopasowania jest większa niż najdłuższa jak dotąd ( $max), zapisz dopasowanie jako $max.
  • print $max: wydrukuj najdłuższy ciąg znalezionych liczb. Zostanie to wykonane po zakończeniu pętli while, a więc po znalezieniu wszystkich liczb.
terdon
źródło
1
+1 Twoje wyrażenie regularne jest jednak zbyt ogólne. Pasowałby na przykład do adresów IP. \D(\d+(?:\.\d+)?)\DZamiast tego proponuję coś takiego .
Joseph R.
Powinien także działać bez \Dkotwic ...
Joseph R.
@JosephR. hmm, prawda, nie zastanawiałem się nad kolejnością .jak w adresach IP.
terdon
6

Dany

str="212334123434test233"

następnie w bash

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

Prawdopodobnie czystsze rozwiązanie bash wykorzystujące tablicę skonstruowaną przez zastąpienie znaków innych niż ciąg znaków spacją zamiast grep

max=""
declare -a nums="${str//[^[:digit:]]/ }"
for num in ${nums[@]}; do 
  (( ${#num} > ${#max} )) && max=$num
done
echo $max
steeldriver
źródło
4

Opierając się na odpowiedzi z @mikeserv, oto kolejna alternatywa. Wyodrębnia liczby (według metody Mikeserva), a następnie sortuje je w kolejności numerycznej i przyjmuje ostatnią. Pomijając zera wiodące, otrzymasz największą liczbę (nie uwzględniając znaku):

echo 1111askdlfm2234 |  printf %s\\n $(tr -sc 0-9 \ ) | sort -n | tail -1
Floris
źródło
Ten faktycznie działa - mój nie. Miałem „r” po niewłaściwej stronie! Usunę to. Możesz także użyć powłoki jak -set -- $(echo $str | tr ... ) ; b=${#1} ; for d ; do [ ${#d} -gt $b ] && b=${#d} n=$d ; done ; echo $n
mikeserv
1
Usunąłem własny okropny post, a ty poradziłeś sobie ze mną wystarczająco delikatnie. Ponieważ i tak już korzystasz tr, nie zrażę się, jeśli zastosujesz powyższe. Prawdopodobnie sort jest szybszy, ale z drugiej strony czeka na zakończenie strumienia tak samo jak $(subshell). Nie wiem W każdym razie twoja jest już doskonałą odpowiedzią, ale jeśli masz ochotę dodać powyższą pętlę powłoki, nie krępuj się, to wszystko co mówię. A tak przy okazji - możliwe jest, że sortobejdzie się to bez odrobiny kreatywnej obsługi wc -Li teetransmisji… Skończyłem z tym pytaniem - jestem zawstydzony.
mikeserv
Jeszcze jedna rzecz - równie dobrze możesz wyciągnąć trz podpowłoki i się go pozbyć printf. Po prostu zrób '0-9' '\n'.
mikeserv
@mikeserv - dobrą stroną tego serwisu jest to, że uczymy się od siebie nawzajem. Dzięki za pomoc; bez twojej odpowiedzi nie zacząłbym nawet sam ...
Floris
2

sortowanie bash i GNU

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

Użyj znaków nienumerycznych, aby podzielić ciąg i znajdź najdłuższą sekwencję lub największą wartość liczbową (dla liczb o równej długości) za pomocą operatora trójskładnikowego.

$ 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

Możesz także ustawić separator rekordów awk ( RS) na dowolny ciąg znaków nienumeryczny:

$ echo "212334123434test233" \
    | awk -v RS='[^0-9]+' '
        length(longest) < length($0) {longest = $0};
        END{print longest}'
212334123434
hjk
źródło
2
Dlaczego po prostu nie ustawić RS = '[^0-9]+'i użyć wewnętrznej pętli Awk? echo "212334123434test233" | awk -v RS='[^0-9]+' 'length(longest) < length($0) {longest = $0};END{print longest}' 212334123434
@awk_FTW również powinieneś odłożyć to jako odpowiedź. :) Dzięki za pokazanie RSzmiennej, muszę przyznać, że widzę ją po raz pierwszy. Masz więcej wskazówek do zaoferowania awkniż ja hahaha!
hjk