首页 > 解决方案 > Preg_match 正在“忽略”捕获组分隔符

问题描述

我们的数据库中存储了数千个结构化文件名,不幸的是,数百个已手动更改为不符合我们命名约定的名称。使用正则表达式,我试图匹配正确的文件名以识别所有错误命名的文件名。这些文件都与会议议程相关,并在名称中使用日期、会议类型、议程项目# 和描述。

我们的命名约定是yyyymmdd_aa[_bbb]_ccccc.pdf

示例文件名:

   20200225_RM_agenda.pdf
   20200225_RM_2_memo.pdf
   20200225_SS1_3c_presenTATION.pdf
   20200225_CA_4d_SiGnEd.pdf
   20200225_RM_5_Order1234.pdf
   2021_02_25_EV_Notice.pdf

我用来匹配这些文件的正则表达式如下(正则表达式演示):

/^(\d{4}[_]?\d{2}[_]?\d{2})_(\w{2,3})_([a-z0-9]{1,3})_?(.+)?.pdf/i

问题: 一般来说,它工作正常,但如果议程编号(“bbb”)不在文件名中,正则表达式会捕获并返回描述的前 3 个字符。在我看来,第 3 个捕获组在下划线之间_([a-z0-9]{1,3})_1-3 个字母数字字符,但我不知道如何“强制使用下划线分隔符”,或者告诉它该组可能不存在,并且它现在看着描述性文字。这可以在演示代码中看到,其中第一个和最后一个文件名不使用议程编号。

任何帮助表示赞赏。

标签: phpregexpreg-match

解决方案


可选标识符?用于最后一件事,可以是字符或组。所以表达式([a-z0-9]{1,3})_?使下划线成为可选的,而不是前面的组。解决方案是将下划线移到括号中。

^(\d{4}[_]?\d{2}[_]?\d{2})_(\w{2,3})_([a-z0-9]{1,3}_)?(.+)?.pdf

此外,[_]?可以简化为_?,文件名句点应该被转义(否则它们是通配符),我个人喜欢使用(?<name>)语法命名我的组。把所有这些放在一起,你会得到:

^(?<date>\d{4}_?\d{2}_?\d{2})_(?<meeting_type>\w{2,3})_(?<agenda>[a-z0-9]{1,3}_)?(?<description>.+)?\.pdf$

此处演示:https ://regex101.com/r/BUKCih/1

更新:

我根据评论做了一些更新。正如@Chris Maurer 所说,我$在末尾添加了强制“文件名结尾”。这阻止file.pdf.txt了通过。我还创建了一个子组并将名称移动到该组中,这样可以不将尾随下划线包含在命名组中。尽管我同意这一点,但我将保留 Chris 关于单独收紧最后一个匹配组的其他评论,如果他们使用[a-z0-9]+或类似的,OP 可能会发现一些不合格的文件。我不记得 PHP 是否支持 POSIX,但如果支持[:alnum:]也可以使用。

^(?<date>\d{4}_?\d{2}_?\d{2})_(?<meeting_type>\w{2,3})_((?<agenda>[a-z0-9]{1,3})_)?(?<description>.+)?\.pdf$

此处更新演示:https ://regex101.com/r/ebmxkF/1


推荐阅读