我有这样的字符串:
ACB 01900 X1911D 1910 1955-2011 3424 2135 1934 foobar
我正在尝试获取一年的最后一次出现(从 1900 年到 2050 年),所以我只需要从该字符串中提取1934年。
我正在尝试:
grep -P -o 's(19|20)[0-9]{2}s(?!s(19|20)[0-9]{2}s)'
或
grep -P -o '((19|20)[0-9]{2})(?!s1s)'
但它匹配:1910 年和 1934
年下面是正则表达式 101 示例:
https://regex101.com/r/UetMl0/3
https://regex101.com/r/UetMl0/4
另外:如何在不做额外的 grep 过滤它们的情况下提取没有周围空间的年份?
你有没有听过这句话:
Some people, when confronted with a problem, think
“I know, I'll use regular expressions.” Now they have two problems.
保持简单 - 你有兴趣找到 2 个数字之间的数字,所以只需使用数字比较,而不是正则表达式:
$ awk -v min=1900 -v max=2050 '{yr=""; for (i=1;i<=NF;i++) if ( ($i ~ /^[0-9]{4}$/) && ($i >= min) && ($i <= max) ) yr=$i; print yr}' file
1934
您没有说明如果范围内没有日期该怎么办,因此如果发生这种情况,上述内容会输出一个空行,但很容易调整以执行其他任何操作。
更改上面的脚本以查找第一个而不是最后一个日期是微不足道的(将打印移动到if内),在您的范围内使用不同的开始或结束日期是微不足道的(更改最小值和/或最大值)等等,等等,这强烈表明这是正确的方法。尝试使用基于正则表达式的解决方案更改任何这些要求。
我看不到使用grep
执行此操作的方法,因为它不允许您只输出其中一个捕获组,只输出整个匹配项。
机智的perl我会做类似的事情
perl -lpe 'if (/^.*b(19dd|20(?:0-4d|50))b/) { print $1 }'
想法:使用^.*
(贪婪)在前面消耗尽可能多的字符串,从而找到最后一个可能的匹配。在匹配的数字周围使用b
(单词边界)以防止匹配01900
或X1911D
。仅打印第一个捕获组 ($1
)。
我试图实现你对1900-2050的要求;如果这太复杂,((?:19|20)dd)
可以(但也匹配例如2099)。
使用 grep 完成任务的正则表达式可以如下所示:
b(?:19d{2}|20[0-4]d|2050)b(?!.*b(?:19d{2}|20[0-4]d|2050)b)
详:
b
- 单词边界。(?:
- 非捕获组的开始,需要作为容器 选择。19d{2}|
- 第一种选择(1900 - 1999)。20[0-4]d|
- 第二种选择(2000 - 2049年)。2050
- 第三种选择,只有 2050 年。
)
- 非捕获组的结束。b
- 单词边界。(?!
- 负面展望:.*
- 任何字符的序列,实际上意味着"接下来的内容" 可以发生在更远的地方"。b(?:19d{2}|20[0-4]d|2050)b
- 与以前相同的表达式。
)
- 负面展望的结束。
单词边界锚点提供您将不匹配数字 - 零件较长的单词,例如X1911D
.
负预测提供您将仅匹配所需年份的最后一次出现。
如果可以使用grep以外的其他工具,则支持调用以前的 编号组(?n)
,其中n是另一个捕获的编号 组,正则表达式可以简单一点:
(b(?:19d{2}|20[0-4]d|2050)b)(?!.*(?1))
详:
(b(?:19d{2}|20[0-4]d|2050)b)
- 像以前一样的正则表达式,但是 包含在捕获组中(稍后将"调用"它)。(?!.*(?1))
- 对捕获第 1 组的负面展望, 位于更远的任何地方。
这样,您可以避免再次编写相同的表达式。
有关regex101
中的工作示例,请参阅 https://regex101.com/r/fvVnZl/1
您可以使用不带任何组的 PCRE 正则表达式,仅在以下情况下返回所需的模式的最后一次出现,前提是在模式前面加上^.*K
,或者,在您的情况下,由于您希望有一个空格边界,^(?:.*s)?K
:
grep -Po '^(?:.*s)?K(?:19d{2}|20(?:[0-4]d|50))(?!S)' file
请参阅正则表达式演示。
详
^
- 行首(?:.*s)?
- 匹配 1 次或 0 次出现的可选非捕获组.*
- 除换行符字符以外的任何 0+ 字符,尽可能多s
- 空格字符
K
- 匹配重置运算符丢弃到目前为止匹配的文本(?:19d{2}|20(?:[0-4]d|50))
-19
和任何两位数字或20
后跟从0
到4
的数字,然后是任何数字(00
到49
)或50
。(?!S)
- 空格或字符串结尾。
观看在线演示:
s="ACB 01900 X1911D 1910 1955-2011 3424 2135 1934 foobar"
grep -Po '^(?:.*s)?K(?:19d{2}|20(?:[0-4]d|50))(?!S)' <<< "$s"
# => 1934