Bagaimana saya bisa mencetak angka terpanjang dalam sebuah string?

11

Saya mencari metode untuk mencetak angka terpanjang dalam sebuah string.

Misalnya: Jika saya memiliki string

212334123434test233

bagaimana saya bisa mencetak

212334123434

?

Catatan: Saya mencari urutan angka terus menerus terpanjang, bukan untuk nilai numerik yang lebih tinggi.


Sunting: Terima kasih atas jawabannya, semuanya. Tanggapan atas pertanyaan ini sangat luar biasa. Saya menandai posting @ HaukeLaging sebagai jawaban yang diterima karena sangat cocok dengan kasus spesifik saya, tetapi saya ingin menunjukkan bahwa semua jawaban sama-sama valid. Selalu luar biasa memiliki beberapa opsi berbeda untuk menyelesaikan masalah.

Glutanimate
sumber
Apa yang Anda ingin metode lakukan ketika ada beberapa urutan kontinu yang sama panjang? Ambil yang pertama? Yang terakhir? Yang acak?
Anthon
@Anthon Huh, saya tidak memikirkan itu. Untungnya itu bukan masalah dalam kasus spesifik saya. Saya kira salah satu opsi akan baik-baik saja.
Glutanimate
3
Perhatikan bahwa jawaban yang Anda terima (dan semua yang lain sejauh ini kecuali satu ) tidak akan berurusan dengan angka desimal. Saya tidak tahu apakah itu masalah bagi Anda.
terdon
@terdon: Ini bukan masalah dalam kasus khusus saya karena saya berurusan dengan ID daripada angka yang sebenarnya tetapi saya ingin berterima kasih atas jawaban Anda! Saya yakin orang lain akan menemukannya sangat berguna di masa depan.
Glutanimate
Apakah Anda ingin solusi untuk dapat menangani angka negatif? Dan jika demikian - apakah tanda minus diperhitungkan terhadap panjang?
Floris

Jawaban:

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

Saya percaya Anda dapat melakukan ini dengan hanya grep, sortdan tailjuga. Berikut adalah beberapa contoh string.

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

Di mana <str>string kami sedang dipertanyakan.

Contoh

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

Sekarang jika saya menjalankan ini melalui grep ...perintah saya pada gilirannya.

$ 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

Pendekatan ini bekerja dengan memilih semua substring yang merupakan urutan digit. Kami kemudian mengurutkan output ini secara numerik,, sort -ndan kemudian mengambil nilai terakhir dalam daftar, menggunakan tail -1. Ini akan menjadi substring terpanjang.

Anda dapat melihat cara kerjanya dengan tail -1melepas dan menjalankan kembali salah satu contoh:

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

String yang dimulai dengan nol

Pendekatan di atas berfungsi untuk setiap situasi yang bisa saya bayangkan kecuali satu. @terdon disebutkan dalam obrolan skenario ini yang menggagalkan pendekatan di atas.

  • 0000000000001
  • 2

Jadi untuk menghadapi ini, Anda perlu sedikit mengubah taktik. Kernel dari pendekatan di atas masih dapat ditingkatkan, namun kita perlu menyuntikkan jumlah karakter ke dalam hasil juga. Ini memberikan kemampuan untuk mengurutkan hasil berdasarkan jumlah karakter dalam string & nilainya.

$ 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

Hasil:

$ 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

Anda bisa menyingkat sedikit ini dengan memanfaatkan kemampuan Bash untuk menentukan panjang variabel yang digunakan ${#var}.

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

Menggunakan `grep -P

Saya memilih untuk menggunakan di grep -P ...atas karena saya, sebagai pengembang Perl, menyukai sintaksis kelas untuk mengatakan semua digit seperti:: \d+, bukannya [[:digit:]]\+atau [0-9]\+. Tetapi untuk masalah khusus ini tidak terlalu dibutuhkan. Anda bisa dengan mudah menukar yang grepsaya gunakan seperti ini:

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

Sebagai contoh:

$ for i in $(echo $str0 | grep -o "[0-9]\+");do echo "${#i} $i"; done | \
    sort -n | tail -1 | cut -d" " -f2
0000000000001
slm
sumber
2
Menggunakan ${#i}untuk mendapatkan panjang string dapat menghemat panggilan Anda wc, jika Anda ingin menggunakan bash-spesifik
glenn jackman
@glennjackman - terima kasih menambahkan peningkatan Anda ke A 8-)
slm
GNU grep 2.16 (setidaknya) mengatakan -P "sangat eksperimental". Anda dapat menggunakan grep -o "[0-9]\+"sebagai gantinyagrep -oP "\d+"
David Conrad
1
@ Davidvidon - menambahkan detail ini ke A juga, terima kasih!
slm
8

Solusi di perl:

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

Referensi

cuonglm
sumber
2
Love a Schwartzian Transform yang bagus!
glenn jackman
7

Menggunakan python dengan string yang diteruskan pada commandline dan dengan asumsi Anda menginginkan urutan pertama dari panjang maksimum:

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

Berikut adalah pendekatan Perl lain yang dapat menangani desimal serta bilangan bulat:

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

Perhatikan bahwa tidak ada jawaban yang sejauh ini diposting akan berurusan dengan desimal dan karena Anda menentukan bahwa Anda ingin jumlah terpanjang dan bukan angka terbesar secara numerik, saya menganggap Anda benar - benar membutuhkan desimal.

Penjelasan

  • perl -lne: -nSarana "baca baris input demi baris, dan jalankan skrip yang diberikan -eolehnya". The -lmenambahkan baris baru untuk setiap printpanggilan (dan hal-hal lain tidak relevan di sini).
  • while(/([\d.]+)/g): iterate melalui semua angka ( \dberarti [0-9], sehingga [\d.]akan cocok dengan angka dan .. Jika Anda juga ingin menemukan angka negatif, tambahkan -. Tanda kurung menangkap string yang cocok seperti $1yang digunakan pada langkah berikutnya.
  • $max=$1 if length($1) > length($max): Jika panjang pertandingan saat ini lebih besar dari yang terpanjang sejauh ini ( $max) simpan pertandingan sebagai $max.
  • print $max: cetak string angka terpanjang yang ditemukan. Ini akan dieksekusi setelah loop sementara selesai, jadi setelah semua angka ditemukan.
terdon
sumber
1
+1 Regex Anda agak terlalu umum. Itu akan cocok dengan alamat IP misalnya. Saya mengusulkan sesuatu seperti \D(\d+(?:\.\d+)?)\Dsebagai gantinya.
Joseph R.
Seharusnya juga bekerja tanpa \Djangkar ...
Joseph R.
@ JosephRR. hmm, benar, saya belum dianggap berurutan .seperti pada alamat IP.
terdon
6

Diberikan

str="212334123434test233"

lalu di bash

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

Solusi bash yang mungkin lebih murni menggunakan array yang dibangun dengan mengganti karakter non-digit dalam string dengan spasi putih, menggantikan grep

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

Membangun jawaban dari @mikeserv, berikut adalah alternatif lain. Ini mengekstrak angka (per metode mikeserv), lalu mengurutkannya dalam urutan numerik dan mengambil yang terakhir. Kecuali nol terkemuka, ini akan memberi Anda jumlah terbesar (tidak memperhitungkan tanda):

echo 1111askdlfm2234 |  printf %s\\n $(tr -sc 0-9 \ ) | sort -n | tail -1
Floris
sumber
Yang ini benar-benar berfungsi - milik saya tidak. Saya memiliki 'r' di sisi yang salah! Saya akan menghapusnya. Anda juga bisa menggunakan shell seperti -set -- $(echo $str | tr ... ) ; b=${#1} ; for d ; do [ ${#d} -gt $b ] && b=${#d} n=$d ; done ; echo $n
mikeserv
1
Saya menghapus posting saya yang mengerikan, dan Anda berurusan dengan cukup lembut dengan saya. Karena Anda sudah menggunakan tr, saya tidak akan menyimpan dendam jika Anda memasukkan hal di atas. Mungkin sort lebih cepat, tetapi, sekali lagi, menunggu aliran berakhir sama dengan $(subshell). Saya tidak tahu Bagaimanapun juga, jawaban Anda sudah merupakan jawaban yang sangat baik, tetapi jika Anda ingin menambahkan lingkaran shell di atas, jangan ragu-ragu. Ngomong-ngomong - itu mungkin bisa Anda lakukan tanpa sortsama sekali dengan sedikit penanganan kreatif wc -Ldan teedalam aliran ... Saya sudah selesai dengan pertanyaan ini - Saya malu.
mikeserv
Satu hal terakhir - Anda mungkin juga menarik trkeluar dari subkulit dan dihilangkan printf. Lakukan saja '0-9' '\n'.
mikeserv
@ mikeserv - hal yang baik tentang situs ini adalah kami saling belajar. Terima kasih atas bantuan Anda; tanpa jawaban Anda, saya bahkan tidak akan memulai sendiri ...
Floris
2

bash dan semacam GNU

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

Gunakan karakter non-numerik untuk memisahkan string, dan temukan urutan terpanjang atau nilai numerik terbesar (untuk nomor dengan panjang yang sama) dengan operator ternary.

$ 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

Anda juga dapat mengatur pemisah rekaman awk ( RS) menjadi string karakter non-numerik:

$ echo "212334123434test233" \
    | awk -v RS='[^0-9]+' '
        length(longest) < length($0) {longest = $0};
        END{print longest}'
212334123434
hjk
sumber
2
Mengapa tidak mengatur RS = '[^0-9]+'dan menggunakan loop bawaan Awk? echo "212334123434test233" | awk -v RS='[^0-9]+' 'length(longest) < length($0) {longest = $0};END{print longest}' 212334123434
@awk_FTW Anda harus menuliskannya sebagai jawaban juga. :) Terima kasih telah menunjukkan saya RSvariabel, saya harus mengakui ini adalah pertama kalinya saya melihatnya. Anda memiliki lebih banyak tips untuk ditawarkan awkdaripada yang saya lakukan hahaha!
hjk