【问题标题】:bash regular expression that will match YYMMDD but not longer numbersbash 正则表达式将匹配 YYMMDD 但不再匹配数字
【发布时间】:2017-12-02 10:23:48
【问题描述】:

一般问题

我试图了解在编写正则表达式时如何防止在寻找的模式之前或之后存在某些模式!

一个更具体的例子

我正在寻找一个正则表达式,它将匹配长字符串中格式为 YYMMDD ((([0-9]{2})(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1]))) 的日期,同时忽略较长的数字序列

应该可以匹配:

  • text151124moretext
  • 123text151124moretext
  • 文本151124
  • text151124moretext1944
  • 151124

但应该忽略

  • 文本15112412更多文本 (原因:它有 8 个数字而不是 6 个)
  • 151324 (原因:YYMMDD 不是有效日期 - 没有第 13 个月)

我如何确保如果一个数字的 更多 超过这 6 位数字,它不会在单个正则表达式中作为日期被拾取(意思是,我宁愿避免预处理字符串) 我想到了\D((19|20)([0-9]{2})(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1]))\D,但这是否意味着必须前后有一些角色?

我正在使用 bash 3.2 (ERE)

谢谢!

【问题讨论】:

  • 如果你使用(^|[^0-9])....([^0-9]|$)?
  • 什么没有断言?
  • 没有bash 3.4;你是说 OS X 自带的 3.2,还是你自己安装的 4.3?
  • @chepner,啊,我的错误——OSX 自带的是 3.2!感谢您指出!
  • @sin,这是一个很好的收获!所有这些正则表达式的味道都让我头疼,作为一个初学者,这真的很令人生畏——看来我确实需要的是消极的前瞻。似乎 ERE 不支持负前瞻 - 不是吗?

标签: regex bash macos


【解决方案1】:

试试:

#!/usr/bin/env bash

extract_date() {
    local string="$1"
    local _date=`echo "$string" | sed -E 's/.*[^0-9]([0-9]{6})[^0-9].*/\1/'`
    #date -d $_date &> /dev/null # for Linux
    date -jf '%y%m%d' $_date &> /dev/null # for MacOS
    if [ $? -eq 0 ]; then
        echo $_date
    else
        return 1
    fi
}


extract_date text15111224moretext # ignore n_digits > 6
extract_date text151125moretext # take
extract_date text151132 # # ignore day 32
extract_date text151324moretext1944 # ignore month 13
extract_date text150931moretext1944 # ignore 31 Sept
extract_date 151126 # take

输出:

151125
151126

【讨论】:

  • 感谢@gregoux,这是一个有趣的方法!尽管如此,我一直在寻找一个可以做到这一点的单一正则表达式;原因是我需要概括。现在我想起来了,表达我的问题的更好方法是:我正在尝试了解如何在编写正则表达式时防止在寻找的模式之前或之后存在某些模式!
  • 那么你就可以投票了 ;)。对您来说,问题是如何管理像 9 月 31 日 False 和 2 月 28 日 False 这样的案例。
  • 似乎 sed -r 在 OSX 中不起作用。 sed -E 代表增强(与扩展不同,但特别是它就足够了)(stackoverflow.com/a/12180129/3017323)。 OSX 上的 date 也不同; date -d 不起作用:我不得不将整行替换为 date -jf '%y%m%d' $_date &> /dev/null - 如果您同意这些更改,您可以将它们整合到您的答案中,我会接受它!
  • 作为旁注,150931 通过了date 评估(至少在 OSX 上)并转换为 2015/10/01!就我而言,这无关紧要。
【解决方案2】:

如果您的标记是行分隔的(即每行只有一个标记):

^[\D]*[\d]{6}([\D]*|[\D]+[\d]{1,6})$

基本上,这个正则表达式寻找:

  • 字符串开头的任意数量的非数字;
  • 正好 6 位数字
  • 直到字符串末尾的任意数量的非数字或至少一个非数字和至少一个数字(最多 6 个)到字符串的末尾

这个正则表达式传递所有给定的样本输入。

【讨论】:

  • 谢谢特德 X!但是,我似乎无法将 \D 和 \d 与 bash 的 [[ =~ ]] 运算符一起使用;运算符 =~ 仅限于 ERE - 我弄错了吗?
【解决方案3】:

您可以使用非捕获组在日期正则表达式的任一侧定义非数字。我用这个表达式和你相同的测试数据取得了成功。

(?:\D)([0-9]{2})(0[1-9]|1[0-2])(0[1-9]|[1-2][0-9]|3[0-1])(?:\D)

【讨论】:

  • 不确定是不是我的错误,但似乎 ERE 不支持非捕获组和 \D,\d 等?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-25
相关资源
最近更新 更多