Wie kann ich die längste Zahl in einer Zeichenfolge drucken?

11

Ich suche nach einer Methode, um die längste Zahl in einer Zeichenfolge zu drucken.

ZB: Wenn ich die Schnur habe

212334123434test233

Wie kann ich drucken?

212334123434

?

Hinweis: Ich suche nach der längsten fortlaufenden Folge von Zahlen, nicht nach dem numerisch höheren Wert.


Edit: Danke für die Antworten, alle zusammen. Die Antwort auf diese Frage war ziemlich überwältigend. Ich habe den Beitrag von @ HaukeLaging als akzeptierte Antwort markiert, da er sehr gut zu meinem speziellen Fall passt, aber ich möchte darauf hinweisen, dass alle Antworten gleichermaßen gültig sind. Es ist immer toll, verschiedene Optionen zu haben, um ein Problem zu lösen.

Glutanimat
quelle
Was soll die Methode tun, wenn mehrere gleich lange fortlaufende Sequenzen vorhanden sind? Nimm den ersten? Das Letzte? Eine zufällige?
Anthon
@Anthon Huh, daran hatte ich nicht gedacht. Zum Glück ist das in meinem speziellen Fall kein Problem. Ich denke, jede der Optionen wäre in Ordnung.
Glutanimate
3
Beachten Sie, dass die Antwort, die Sie akzeptiert haben (und alle anderen bis auf eine ), keine Dezimalzahlen enthält. Ich weiß nicht, ob das ein Problem für dich ist.
Terdon
@terdon: In meinem speziellen Fall ist dies kein Problem, da es sich eher um IDs als um tatsächliche Nummern handelt, aber ich möchte mich trotzdem für Ihre Antwort bedanken! Ich bin sicher, jemand anderes wird es in Zukunft sehr nützlich finden.
Glutanimate
Möchten Sie, dass die Lösung mit negativen Zahlen umgehen kann? Und wenn ja - zählt das Minuszeichen für die Länge?
Floris

Antworten:

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
quelle
13

Ich glaube , Sie können dies tun , mit nur grep, sortund tailaußerdem. Hier sind einige Beispielzeichenfolgen.

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

Wo <str> ist unsere Schnur in Frage.

Beispiel

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

Nun, wenn ich diese grep ...nacheinander durch meinen Befehl laufen lasse.

$ 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

Bei diesem Ansatz werden alle Teilzeichenfolgen ausgewählt, bei denen es sich um Ziffernfolgen handelt. Wir sortieren diese Ausgabe dann numerisch sort -nund greifen dann mit auf den letzten Wert in der Liste zu tail -1. Dies ist der längste Teilstring.

Sie können sehen, wie es funktioniert, indem tail -1Sie eines der Beispiele abnehmen und erneut ausführen:

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

Zeichenfolgen, die mit Nullen beginnen

Der obige Ansatz funktioniert für jede Situation, die ich mir vorstellen kann, außer für eine. @terdon erwähnte im Chat dieses Szenario, das den obigen Ansatz vereitelt.

  • 0000000000001
  • 2

Um damit fertig zu werden, müssen Sie die Taktik leicht ändern. Der Kernel des obigen Ansatzes kann weiterhin genutzt werden, wir müssen jedoch auch die Anzahl der Zeichen in die Ergebnisse einfügen. Dies gibt sort die Möglichkeit, die Ergebnisse nach Anzahl der Zeichen in den Zeichenfolgen und deren Werten zu sortieren.

$ 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

Ergebnisse:

$ 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

Sie können dies ein wenig komprimieren, indem Sie die Fähigkeit von Bash nutzen, die Länge einer Variablen mithilfe von zu bestimmen ${#var}.

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

Mit `grep -P

Ich habe mich für die grep -P ...obige Verwendung entschieden, weil ich als Perl-Entwickler die Klassensyntax mag, alle Ziffern so zu sagen : \d+, anstelle von [[:digit:]]\+oder [0-9]\+. Aber für dieses spezielle Problem wird es nicht wirklich benötigt. Sie können das, was grepich verwendet habe, genauso gut austauschen :

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

Beispielsweise:

$ for i in $(echo $str0 | grep -o "[0-9]\+");do echo "${#i} $i"; done | \
    sort -n | tail -1 | cut -d" " -f2
0000000000001
slm
quelle
2
Die Verwendung ${#i}zum Abrufen der Zeichenfolgenlänge kann Ihnen das Anrufen ersparen wc, wenn Sie bash-spezifisch werden möchten
Glenn Jackman
@glennjackman - danke fügte Ihre Verbesserung zu meinem A 8-)
slm hinzu
GNU grep 2.16 sagt (zumindest), dass -P "sehr experimentell" ist. Sie können grep -o "[0-9]\+"anstelle vongrep -oP "\d+"
David Conrad
1
@DavidConrad - hat diese Details auch dem A hinzugefügt, danke!
slm
8

Eine Lösung in perl:

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

Verweise

cuonglm
quelle
2
Ich liebe eine schöne Schwartzian Transformation!
Glenn Jackman
7

Verwenden von Python mit der in der Befehlszeile übergebenen Zeichenfolge und vorausgesetzt, Sie möchten die erste Sequenz mit maximaler Länge:

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

Hier ist ein weiterer Perl-Ansatz, der sowohl mit Dezimalstellen als auch mit ganzen Zahlen umgehen kann:

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

Beachten Sie, dass sich keine der bisher veröffentlichten Antworten mit Dezimalstellen befasst. Da Sie angeben, dass Sie die längste und nicht die numerisch größte Zahl möchten, gehe ich davon aus, dass Sie diese tatsächlich benötigen gehe Dezimalstellen .

Erläuterung

  • perl -lne: Das -nbedeutet "Lesen Sie die Eingabe Zeile für Zeile und führen Sie das darauf angegebene Skript -eaus". Das -lfügt jedem printAnruf eine neue Zeile hinzu (und andere Dinge, die hier nicht relevant sind).
  • while(/([\d.]+)/g): Durchlaufen Sie alle Zahlen ( \dbedeutet [0-9], dass [\d.]die Ziffern übereinstimmen und .. Wenn Sie auch negative Zahlen suchen möchten, fügen Sie hinzu -. Die Klammern erfassen die übereinstimmende Zeichenfolge, wie $1sie im nächsten Schritt verwendet wird.
  • $max=$1 if length($1) > length($max): Wenn die Länge der aktuellen Übereinstimmung größer als die bisher längste ist ( $max), speichern Sie die Übereinstimmung als $max.
  • print $max: Gibt die längste gefundene Zahlenfolge aus. Dies wird ausgeführt, nachdem die while-Schleife beendet ist, also nachdem alle Zahlen gefunden wurden.
terdon
quelle
1
+1 Ihre Regex ist allerdings etwas zu allgemein. Es würde zum Beispiel IP-Adressen entsprechen. Ich schlage \D(\d+(?:\.\d+)?)\Dstattdessen so etwas vor.
Joseph R.
Sollte auch ohne die \DAnker funktionieren ...
Joseph R.
@ JosephR. hmm, stimmt, ich hatte nicht .wie in IP-Adressen als konsekutiv angesehen .
Terdon
6

Gegeben

str="212334123434test233"

dann in bash

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

Eine möglicherweise reinere Bash-Lösung, bei der ein Array verwendet wird, bei dem nicht-stellige Zeichen in der Zeichenfolge anstelle von grep durch Leerzeichen ersetzt werden

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

Aufbauend auf der Antwort von @mikeserv gibt es hier noch eine weitere Alternative. Es extrahiert die Zahlen (nach der Methode von mikeserv), sortiert sie dann in numerischer Reihenfolge und nimmt die letzte. Abgesehen von führenden Nullen erhalten Sie die größte Zahl (ohne Berücksichtigung des Vorzeichens):

echo 1111askdlfm2234 |  printf %s\\n $(tr -sc 0-9 \ ) | sort -n | tail -1
Floris
quelle
Dieser funktioniert tatsächlich - meiner nicht. Ich hatte das '\ r' auf der falschen Seite! Ich werde es löschen. Sie können auch einfach die Shell wie -set -- $(echo $str | tr ... ) ; b=${#1} ; for d ; do [ ${#d} -gt $b ] && b=${#d} n=$d ; done ; echo $n
mikeserv
1
Ich habe meinen eigenen schrecklichen Beitrag gelöscht, und Sie haben sanft genug mit mir umgegangen. Da Sie trsowieso schon verwenden, würde ich keinen Groll ertragen, wenn Sie das oben genannte einbeziehen. Wahrscheinlich sort ist es schneller, aber andererseits wartet es auf das Ende des Streams genauso wie das $(subshell). Ich weiß es nicht. In jedem Fall ist Ihre Antwort bereits eine ausgezeichnete, aber wenn Sie Lust haben, die obige Shell-Schleife hinzuzufügen, fühlen Sie sich frei, das ist alles, was ich sage. Übrigens - es ist möglich, dass Sie sortmit ein wenig kreativem Umgang mit wc -Lund teeim Stream ganz darauf verzichten können ... Ich bin jedoch mit dieser Frage fertig - es ist mir peinlich.
Mikeserv
Eine letzte Sache - Sie könnten genauso gut traus der Unterschale ziehen und loswerden printf. Tu es einfach '0-9' '\n'.
Mikeserv
@mikeserv - das Gute an dieser Seite ist, dass wir voneinander lernen. Danke für Ihre Hilfe; Ohne deine Antwort hätte ich nicht einmal alleine angefangen ...
Floris
2

Bash und GNU sortieren

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

Verwenden Sie nicht numerische Zeichen, um die Zeichenfolge zu teilen und die längste Sequenz oder den größten numerischen Wert (für Zahlen gleicher Länge) mit einem ternären Operator zu ermitteln.

$ 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

Sie können das Datensatztrennzeichen ( RS) von awk auch auf eine beliebige nicht numerische Zeichenfolge setzen:

$ echo "212334123434test233" \
    | awk -v RS='[^0-9]+' '
        length(longest) < length($0) {longest = $0};
        END{print longest}'
212334123434
hjk
quelle
2
Warum nicht einfach RS = '[^0-9]+'Awks inhärente Schleife setzen und verwenden? echo "212334123434test233" | awk -v RS='[^0-9]+' 'length(longest) < length($0) {longest = $0};END{print longest}' 212334123434
@awk_FTW das solltest du auch als Antwort aufschreiben. :) Danke, dass du mir die RSVariable gezeigt hast . Ich muss zugeben, dass ich sie zum ersten Mal sehe. Sie haben mehr Tipps zu bieten awkals ich, hahaha!
hjk