Làm thế nào tôi có thể in số dài nhất trong một chuỗi?


11

Tôi đang tìm kiếm một phương pháp để in số dài nhất trong một chuỗi.

Vd: Nếu tôi có chuỗi

212334123434test233

làm thế nào tôi có thể in

212334123434

?

Lưu ý: Tôi đang tìm chuỗi số liên tục dài nhất, không phải cho giá trị số cao hơn.


Chỉnh sửa: Cảm ơn câu trả lời, tất cả mọi người. Câu trả lời cho câu hỏi này là khá áp đảo. Tôi đã đánh dấu bài đăng của @ HaukeLaging là câu trả lời được chấp nhận vì nó rất phù hợp với trường hợp cụ thể của tôi nhưng tôi muốn chỉ ra rằng tất cả các câu trả lời đều có giá trị như nhau. Thật tuyệt khi có nhiều lựa chọn khác nhau để giải quyết vấn đề.


Bạn muốn phương thức làm gì khi có nhiều chuỗi liên tục dài bằng nhau? Đi trước? Cuối cùng? Một ngẫu nhiên?
Anthon

@Anthon Huh, tôi đã không nghĩ về điều đó. May mắn thay, đó không phải là một vấn đề trong trường hợp cụ thể của tôi. Tôi đoán bất kỳ tùy chọn sẽ tốt.
Glutimate

3
Lưu ý rằng câu trả lời bạn đã chấp nhận (và tất cả các câu trả lời khác cho đến nay trừ một ) sẽ không xử lý các số thập phân. Tôi không biết nếu đó là một vấn đề cho bạn.
terdon

@terdon: Tuy nhiên, đó không phải là vấn đề trong trường hợp cụ thể của tôi vì tôi đang xử lý ID chứ không phải số thực tế nhưng tôi muốn cảm ơn bạn vì câu trả lời của bạn! Tôi chắc chắn người khác sẽ thấy nó rất hữu ích trong tương lai.
Glutimate

Bạn có muốn giải pháp để có thể xử lý các số âm? Và nếu vậy - dấu trừ có được tính theo chiều dài không?
Floris

Câu trả lời:


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

212334123434

13

Tôi tin rằng bạn có thể làm điều này với chỉ grep, sorttaillà tốt. Dưới đây là một số chuỗi ví dụ.

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

<str>Chuỗi của chúng tôi ở đâu trong câu hỏi.

Thí dụ

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

Bây giờ nếu tôi chạy chúng thông qua grep ...lệnh của tôi lần lượt.

$ 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

Cách tiếp cận này hoạt động bằng cách chọn tất cả các chuỗi con là chuỗi các chữ số. Sau đó, chúng tôi sắp xếp đầu ra này bằng số, sort -nvà sau đó lấy giá trị cuối cùng trong danh sách, sử dụng tail -1. Đây sẽ là chuỗi con dài nhất.

Bạn có thể thấy nó hoạt động như thế nào bằng cách tail -1tắt và chạy lại một trong các ví dụ:

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

Chuỗi bắt đầu bằng số không

Cách tiếp cận trên hoạt động cho mọi tình huống tôi có thể nghĩ ra ngoại trừ một tình huống. @terdon đã đề cập trong trò chuyện kịch bản này cho phép tiếp cận ở trên.

  • 0000000000001
  • 2

Vì vậy, để đối phó với điều này, bạn sẽ cần thay đổi chiến thuật một chút. Hạt nhân của cách tiếp cận trên vẫn có thể được sử dụng, tuy nhiên chúng ta cũng cần đưa số lượng ký tự vào kết quả. Điều này cho phép sắp xếp khả năng sắp xếp kết quả theo số lượng ký tự trong chuỗi & giá trị của chúng.

$ 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

Các kết quả:

$ 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

Bạn có thể ngưng tụ điều này một chút bằng cách sử dụng khả năng của Bash để xác định độ dài của biến bằng cách sử dụng ${#var}.

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

Sử dụng `grep -P

Tôi đã chọn sử dụng grep -P ...ở trên vì tôi, là nhà phát triển Perl, thích cú pháp lớp nói tất cả các chữ số như vậy : \d+, thay vì [[:digit:]]\+hoặc [0-9]\+. Nhưng đối với vấn đề đặc biệt này thì nó không thực sự cần thiết. Bạn có thể dễ dàng trao đổi như greptôi đã sử dụng như vậy:

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

Ví dụ:

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

2
Sử dụng ${#i}để có được độ dài chuỗi có thể giúp bạn tiết kiệm cuộc gọi wc, nếu bạn muốn truy cập bash-cụ thể
glenn jackman

@glennjackman - cảm ơn đã thêm sự cải thiện của bạn vào A 8-)
slm

GNU grep 2.16 (ít nhất) nói rằng -P là "thử nghiệm cao". Bạn có thể sử dụng grep -o "[0-9]\+"thay vìgrep -oP "\d+"
David Conrad

1
@DavidConrad - cũng đã thêm các chi tiết này vào A, cảm ơn!
slm

8

Một giải pháp trong perl:

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

Người giới thiệu


2
Yêu một Schwartzian chuyển đổi tốt đẹp!
glenn jackman

7

Sử dụng python với chuỗi được truyền trên dòng lệnh và giả sử bạn muốn chuỗi đầu tiên có độ dài tối đa:

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

7

Đây là một cách tiếp cận Perl khác có thể xử lý số thập phân cũng như số nguyên:

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

Lưu ý rằng không có câu trả lời nào được đăng cho đến nay sẽ xử lý số thập phân và vì bạn xác định rằng bạn muốn số dài nhất và không phải là số lớn nhất, tôi cho rằng bạn thực sự cần số thập phân.

Giải trình

  • perl -lne: Có -nnghĩa là "đọc dòng đầu vào theo dòng và chạy tập lệnh được cung cấp bởi -enó". Việc -lthêm một dòng mới cho mỗi printcuộc gọi (và những thứ khác không liên quan ở đây).
  • while(/([\d.]+)/g): Lặp qua tất cả các số ( \dphương tiện [0-9], vì vậy [\d.]sẽ phù hợp với chữ số và .Nếu bạn cũng muốn tìm số âm, thêm. -Các ngoặc chụp chuỗi phù hợp như. $1Được sử dụng trong các bước tiếp theo.
  • $max=$1 if length($1) > length($max): Nếu độ dài của trận đấu hiện tại lớn hơn thời gian dài nhất cho đến nay ( $max) hãy lưu trận đấu dưới dạng $max.
  • print $max: in chuỗi số dài nhất được tìm thấy. Điều này sẽ được thực hiện sau khi vòng lặp while kết thúc, vì vậy sau khi tất cả các số đã được tìm thấy.

1
+1 Regex của bạn là một chút quá chung chung, mặc dù. Nó sẽ phù hợp với địa chỉ IP chẳng hạn. Tôi đề xuất một cái gì đó như \D(\d+(?:\.\d+)?)\Dthay thế.
Joseph R.

Cũng nên làm việc mà không có \Dmỏ neo ...
Joseph R.

@JosephR. hmm, đúng, tôi đã không xem xét liên tiếp .như trong địa chỉ IP.
terdon

6

Được

str="212334123434test233"

sau đó trong bash

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

Một giải pháp bash có thể tinh khiết hơn bằng cách sử dụng một mảng được xây dựng bằng cách thay thế các ký tự không có chữ số trong chuỗi bằng khoảng trắng, thay cho grep

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

4

Dựa trên câu trả lời từ @mikeerv, đây là một cách khác. Nó trích xuất các số (theo phương pháp của mikeerv), sau đó sắp xếp chúng theo thứ tự số và lấy số cuối cùng. Chặn các số 0 đứng đầu, điều này sẽ cung cấp cho bạn số lượng lớn nhất (không tính đến dấu hiệu):

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

Cái này thực sự hoạt động - của tôi thì không. Tôi đã có '\ r' ở phía sai! Tôi sẽ xóa nó. Bạn cũng có thể chỉ cần sử dụng shell như -set -- $(echo $str | tr ... ) ; b=${#1} ; for d ; do [ ${#d} -gt $b ] && b=${#d} n=$d ; done ; echo $n
mikeerv

1
Tôi đã xóa bài viết khủng khiếp của riêng tôi, và bạn xử lý đủ nhẹ nhàng với tôi. Vì trdù sao bạn cũng đã sử dụng , tôi sẽ không ác cảm nếu bạn kết hợp những điều trên. Có lẽ sort là nhanh hơn, nhưng, sau đó một lần nữa, nó chờ cho luồng kết thúc giống như $(subshell). Tôi không biết. Trong mọi trường hợp, câu trả lời của bạn đã là một câu trả lời tuyệt vời, nhưng nếu bạn cảm thấy muốn thêm vào vòng lặp shell ở trên thì cảm thấy miễn phí là tất cả những gì tôi đang nói. Và nhân tiện - có thể bạn có thể làm mà không cần sorthoàn toàn với một chút xử lý sáng tạo wc -Lteetrong luồng ... Tôi đã hoàn thành với câu hỏi này - tôi cảm thấy xấu hổ.
mikeerv

Một điều cuối cùng - bạn cũng có thể rút trra khỏi lớp con và loại bỏ printf. Cứ làm đi '0-9' '\n'.
mikeerv

@mikeerv - điều tốt về trang web này là chúng tôi học hỏi lẫn nhau. Cảm ơn bạn đã giúp đỡ; không có câu trả lời của bạn, tôi thậm chí sẽ không tự mình bắt đầu ...
Floris

2

bash và sắp xếp GNU

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

2

Sử dụng các ký tự không phải là số để phân tách chuỗi và tìm chuỗi dài nhất hoặc giá trị số lớn nhất (đối với các số có độ dài bằng nhau) với toán tử 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

Bạn cũng có thể đặt dấu tách bản ghi của awk ( RS) thành bất kỳ chuỗi ký tự không phải là số nào:

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

2
Tại sao không chỉ thiết lập RS = '[^0-9]+'và sử dụng vòng lặp vốn có của Awk? echo "212334123434test233" | awk -v RS='[^0-9]+' 'length(longest) < length($0) {longest = $0};END{print longest}' 212334123434

@awk_FTW bạn cũng nên đặt nó xuống như một câu trả lời. :) Cảm ơn đã chỉ cho tôi RSbiến, tôi phải thừa nhận đây là lần đầu tiên tôi nhìn thấy nó. Bạn có nhiều lời khuyên để cung cấp awkhơn tôi làm hahaha!
hjk
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.