Как я могу напечатать самое длинное число в строке?


11

Я ищу метод для печати самого длинного числа в строке.

Например: если у меня есть строка

212334123434test233

как я могу распечатать

212334123434

?

Примечание: я ищу самую длинную непрерывную последовательность чисел, а не численно более высокое значение.


Изменить: Спасибо за ответы, все. Ответ на этот вопрос был довольно ошеломляющим. Я пометил пост @ HaukeLaging как принятый ответ, потому что он очень хорошо подходил для моего конкретного случая, но я хотел бы отметить, что все ответы одинаково действительны. Всегда здорово иметь несколько разных вариантов решения проблемы.


Что вы хотите, чтобы метод делал, когда есть несколько одинаково длинных непрерывных последовательностей? Взять первый? Последний? Случайный?
Anthon

@Антон Ха, я не думал об этом. К счастью, это не проблема в моем конкретном случае. Я думаю, что любой из вариантов будет в порядке.
Glutanimate

3
Обратите внимание, что ответ, который вы приняли (и все остальные, кроме одного ), не будет иметь дело с десятичными числами. Я не знаю, если это проблема для вас.
Terdon

@terdon: Это не проблема в моем конкретном случае, потому что я имею дело с идентификаторами, а не с фактическими числами, но я все же хотел бы поблагодарить вас за ваш ответ! Я уверен, что кто-то еще найдет это очень полезным в будущем.
Glutanimate,

Хотели бы вы, чтобы решение могло работать с отрицательными числами? И если да, то считается ли знак минус по отношению к длине?
Флорис

Ответы:


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

212334123434

13

Я считаю , что вы можете сделать это только с grep, sortи tailкак хорошо. Вот несколько примеров строк.

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

Где <str>наша строка под вопросом.

пример

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

Теперь, если я проведу их через свою grep ...команду по очереди.

$ 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

Этот подход работает путем выбора всех подстрок, которые являются последовательностями цифр. Затем мы сортируем эти выходные данные численно, sort -nа затем берем последнее значение в списке, используя tail -1. Это будет самая длинная подстрока.

Вы можете увидеть, как это работает, взяв tail -1и повторно запустив один из примеров:

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

Строки, начинающиеся с нулей

Вышеупомянутый подход работает для любой ситуации, которую я мог себе представить, кроме одной. @terdon упомянул в чате этот сценарий, который мешает вышеуказанному подходу.

  • 0000000000001
  • 2

Таким образом, чтобы справиться с этим, вам нужно немного изменить тактику. Ядро вышеупомянутого подхода все еще можно использовать, однако нам нужно также ввести количество символов в результаты. Это дает возможность сортировки результатов по количеству символов в строках и их значениям.

$ 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

Результаты:

$ 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

Вы можете немного сократить это, используя способность Bash определять длину переменной, используя ${#var}.

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

Использование `grep -P

Я решил использовать grep -P ...выше, потому что мне, будучи разработчиком Perl, нравится синтаксис класса, состоящий в том, чтобы произносить все цифры следующим образом:, \d+вместо [[:digit:]]\+или [0-9]\+. Но для этой конкретной проблемы это действительно не нужно. Вы могли бы так же легко поменять местами, как grepя использовал, вот так:

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

Например:

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

2
Использование, ${#i}чтобы получить длину строки, может спасти ваши звонки wc, если вы хотите использовать bash
glenn jackman

@glennjackman - спасибо, что вы улучшили мою A 8-)
slm

GNU grep 2.16 (по крайней мере) говорит, что -P "очень экспериментальный". Вы можете использовать grep -o "[0-9]\+"вместоgrep -oP "\d+"
Дэвид Конрад

1
@DavidConrad - также добавил эти детали в A, спасибо!
SLM


7

Используя python со строкой, переданной в командной строке, и предположим, что вы хотите первую последовательность максимальной длины:

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
или краткоpython -c "import re,sys; print max(re.split(r'\D+', sys.argv[1]), key=len)"
iruvar

7

Вот еще один подход Perl, который может работать как с десятичными числами, так и с целыми числами:

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

Обратите внимание, что ни один из опубликованных ответов не будет иметь дело с десятичными знаками, и, поскольку вы указываете, что хотите получить самое длинное, а не числовое наибольшее число, я предполагаю, что вам действительно нужны десятичные дроби.

объяснение

  • perl -lne: -nОзначает «читать входные данные построчно и запускать сценарий, заданный для -eнего». -lДобавляет новую строку каждого printвызова (и другие вещи , не имеющие отношения здесь).
  • while(/([\d.]+)/g): выполнить итерацию по всем числам ( \dзначит [0-9], так [\d.]будут совпадать цифры и .. Если вы также хотите найти отрицательные числа, добавьте -. В скобках указана совпадающая строка, $1которая используется на следующем шаге.
  • $max=$1 if length($1) > length($max): Если длина текущего совпадения больше, чем самая длинная ( $max), сохраните совпадение как $max.
  • print $max: вывести самую длинную найденную строку чисел. Это будет выполнено после завершения цикла while, то есть после того, как все числа будут найдены.

1
+1 Ваше регулярное выражение слишком общее. Это будет соответствовать IP-адресам, например. Я предлагаю что-то вроде \D(\d+(?:\.\d+)?)\Dэтого.
Джозеф Р.

Также должен работать без \Dякорей ...
Джозеф Р.

@JosephR. хм, правда, я не считал подряд, .как в IP-адресах.
Terdon

6

Данный

str="212334123434test233"

тогда в баш

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

Возможно, более чистое решение bash с использованием массива, созданного путем замены нецифровых символов в строке пробелом вместо grep

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

4

Основываясь на ответе @mikeserv, вот еще одна альтернатива. Он извлекает числа (по методу mikeserv), затем сортирует их по порядку номеров и берет последнее. Если исключить начальные нули, это даст вам наибольшее число (без учета знака):

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

Этот на самом деле работает - мой нет. У меня был '\ r' на неправильной стороне! Я собираюсь удалить это. Вы также можете просто использовать оболочку, как -set -- $(echo $str | tr ... ) ; b=${#1} ; for d ; do [ ${#d} -gt $b ] && b=${#d} n=$d ; done ; echo $n
mikeserv

1
Я удалил свой собственный ужасный пост, а ты относился ко мне достаточно осторожно. Так как вы уже используете в trлюбом случае, я не буду обижаться, если вы включите выше. Возможно, sort это быстрее, но опять же, он ожидает окончания потока так же, как и $(subshell). Я не знаю. В любом случае, ваш ответ уже отличный, но если вы хотите добавить в вышеупомянутый цикл оболочки, не стесняйтесь, это все, что я говорю. И, между прочим - возможно, что вы могли бы обойтись sortвообще без небольшой творческой обработки wc -Lи teeв потоке ... Я закончил с этим вопросом, хотя - я смущен.
mikeserv

Последнее, что можно сделать - вы также можете выйти trиз подоболочки и избавиться от нее printf. Просто делай '0-9' '\n'.
mikeserv

@mikeserv - хорошая вещь об этом сайте в том, что мы учимся друг у друга. Спасибо за вашу помощь; без вашего ответа я бы даже не начал сам по себе ...
Флорис

2

Bash и GNU сортировать

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

2

Используйте нечисловые символы для разделения строки и найдите самую длинную последовательность или наибольшее числовое значение (для чисел одинаковой длины) с помощью троичного оператора.

$ 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

Вы также можете установить разделитель записей awk ( RS) как любую нечисловую строку символов:

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

2
Почему бы просто не установить RS = '[^0-9]+'и использовать собственный цикл Awk? echo "212334123434test233" | awk -v RS='[^0-9]+' 'length(longest) < length($0) {longest = $0};END{print longest}' 212334123434

@awk_FTW ты тоже должен это записать как ответ. :) Спасибо за показ мне RSпеременной, я должен признать, что это первый раз, когда я вижу это. У вас есть больше советов, awkчем я, хахаха!
HJK
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.