如何打印字符串中最长的数字?


11

我正在寻找一种打印字符串中最长数字的方法。

例如:如果我有字符串

212334123434test233

我该如何打印

212334123434

注意:我正在寻找最长的连续数字序列,而不是数字更高的值。


编辑:谢谢大家的回答。对这个问题的反应非常热烈。我将@HaukeLaging的帖子标记为可接受的答案,因为它非常适合我的具体情况,但我想指出,所有答案都同样有效。有几种不同的选择解决一个问题总是很高兴。


当存在多个等长的连续序列时,您希望该方法做什么?先拿吗 最后?一个随机的?
Anthon 2014年

@Anthon Huh,我没想到。幸运的是,在我的特定情况下这不是问题。我想任何选择都可以。
Glutanimate

3
请注意,您接受的答案(以及到目前为止所有其他答案,除了一个之外)不会处理十进制数字。我不知道这是否对您有问题。
terdon

@terdon:在我的特定情况下这不是问题,因为我正在处理ID而不是实际数字,但是我仍然要感谢您的回答!我相信其他人将来会发现它非常有用。
Glutanimate

您是否希望解决方案能够处理负数?如果是这样-减号是否计入长度?
弗洛里斯2014年

Answers:


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

212334123434

13

我相信你可以做到这一点只是grepsorttail也。这是一些示例字符串。

$ 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

因此,要解决此问题,您需要稍微改变策略。仍然可以利用上述方法的内核,但是我们也需要在结果中注入字符数。这使sort能够按字符串中的字符数及其值对结果进行排序。

$ 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:]]\+or [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
如果您想进行特定于bash的操作,则使用${#i}获取字符串长度可以节省调用费用wc
glenn jackman 2014年

@glennjackman -感谢您的加入改善我的8-)
SLM

GNU grep 2.16(至少)说-P是“高度实验性的”。您可以使用grep -o "[0-9]\+",而不是grep -oP "\d+"
大卫·康拉德

1
@DavidConrad-也将这些详细信息添加到A中,谢谢!
slm

8

解决方案perl

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

参考文献


2
爱一个不错的Schwartzian变换!
glenn jackman 2014年

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
Joseph R.

也应该在没有\D锚的情况下工作...
Joseph R.

@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)。我不知道。无论如何,您的答案已经是一个不错的选择,但是如果您想在上述shell循环中添加代码,那就很随意了。顺便说一句-这是可能的,你可以不这样做sort完全用一点创新的处理wc -Ltee在流......我这个问题,虽然做了-我很尴尬。
mikeserv

最后一件事-您最好tr退出子外壳并摆脱它printf。做吧'0-9' '\n'
mikeserv

@mikeserv-这个网站的好处是我们可以互相学习。谢谢你的帮助; 没有您的回答,我什至不会独自开始……
弗洛里斯2014年

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 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.