用于捕获下一行的正则表达式 日期/时间 年/月/日时 hh:mm 或 年/月/天 h:m



我想捕获日期格式 -yyyy/mm/dd hh:mm

[^nr]*[rn]+([12]d{3}/(0[1-9]|1[0-2])/(0[1-9]|[12]d|3[0‌​1]))** 

上面的表达式捕获了当天的下一行,但我也想包括时间部分,并且还想捕获月份、日期和时间的个位数,而不必输入两位数。

例如。用户可以输入2017/5/2 9:52017/05/02 09:05

需要帮助捕获个位数的月份和日期以及时间部分。

这是...

d{4}/([1-9]{1}|0[1-9]|1[0-2])/([1-9]{1}|[0-2]{1}[1-9]{1}|3[0-1])s+([0-9]{1}|[0-1]{1}[0-9]{1}|2[0-4]):([0-9]{1}|[0-5]{1}[0-9]{1})s+

这可能看起来势不可挡,所以这里是表达式的演练。 此表达式不仅需要查找日期和时间,还会查找不切实际的日期时间,例如 2001/44/44 或2344/44444/999它仅检查有效的日期时间。无效的日期时间将被忽略。此外,它不仅会检查行首的日期时间,还会检查字符串中单行或多行的任何地方。

解释

第一个 4 位数字将是年份。

d{4}

后跟"/"...

d{4}/

现在,月份可以是个位数,例如 1-9

d{4}/( [1-9]{1} )

或两位数 01、02、03、09(请记住,如果月份以 0 开头,则其第 2 位不能大于 9。

d{4}/( [1-9]{1} | 0[1-9]{1} )

或 10、11、12,但不能大于 12。

d{4}/( [1-9]{1} | 0[1-9]{1} | 1[0-2]{1} )

后跟"/">

d{4}/( [1-9]{1} | 0[1-9]{1} | 1[0-2]{1} ) /

现在到了几天,它可以是个位数 1-9

d{4}/( [1-9]{1} | 0[1-9]{1} | 1[0-2]{1} ) /( [1-9]{1} )

或两位数 01、02、03、09、19、29。

d{4}/( [1-9]{1} | 0[1-9]{1} | 1[0-2]{1} ) /( [1-9]{1} | [0-2]{1}[1-9]{1} )

或者它可以是 30 或 31,但不能大于此值。

d{4}/( [1-9]{1} | 0[1-9]{1} | 1[0-2]{1} ) /( [1-9]{1} | [0-2]{1}[1-9]{1} | 3[0-1] )

现在日期部分已完成。日期和时间之间的一些空格。

d{4}/( [1-9]{1} | 0[1-9]{1} | 1[0-2]{1} ) /( [1-9]{1} | [0-2]{1}[1-9]{1} | 3[0-1] ) s+

现在让我们关注时间部分。 假设时间基于 24 小时格式。 小时可以是个位数,如 0、1、2、9

( [0-9]{1} )

或两位数,如 01、02、09、11、19

( [0-9]{1} | [0-1]{1}[0-9]{1} )

或 20、21、22、23、24 但不大于 24。

( [0-9]{1} | [0-1]{1}[0-9]{1} | 2[0-4]{1} )

后跟":">

( [0-9]{1} | [0-1]{1}[0-9]{1} | 2[0-4]{1} ) : 

分钟可以是个位数,如 0、1、2、9...

( [0-9]{1} | [0-1]{1}[0-9]{1} | 2[0-4]{1} ) : ( [0-9]{1} )

或两位数,如 01、02、03、23、44、59(不是 60)。

( [0-9]{1} | [0-1]{1}[0-9]{1} | 2[0-4]{1} ) : ( [0-9]{1} | [0-5]{1}[0-9]{1} )

后跟一些空间

( [0-9]{1} | [0-1]{1}[0-9]{1} | 2[0-4]{1} ) : ( [0-9]{1} | [0-5]{1}[0-9]{1} )
s+

现在结合您的日期正则表达式和时间正则表达式,您将获得

d{4}/([1-9]{1}|0[1-9]|1[0-2])/([1-9]{1}|[0-2]{1}[1-9]{1}|3[0-1])s+([0-9]{1}|[0-1]{1}[0-9]{1}|2[0-4]):([0-9]{1}|[0-5]{1}[0-9]{1})s+

注意:在解释过程中,我在正则表达式中添加了额外的空间,以提高可读性。

要使数字成为可选数字,只需使用?量词。

假设你使用的是Pattern类,并从包java.util.regexMatcher,你的代码将是这样的(另请注意,在Java中你必须转义反斜杠,所以模式d必须写成\d):

String input = "2017/5/2 9:5";
Pattern pattern = Pattern.compile("(\d{4})/(0?[1-9]|1[0-2])/(0?[1-9]|[12]\d|3[0‌​1]) ([01]?\d|2[0-3]):([0-5]?\d)");
Matcher matcher = pattern.matcher(input);
while (matcher.find()) {
String year = matcher.group(1);
String month = matcher.group(2);
String day = matcher.group(3);
String hour = matcher.group(4);
String minute = matcher.group(5);
}

对于月份和日期,我只是在零后添加了一个?,使其成为可选的。

在这一个小时里,我做到了:

  • [01]?:可选零或一,后跟任意数字(d),
  • 2[0-3]:数字 2,后跟 0、1、2 或 3(因此从 20 到 23 得到小时数)

对于分钟:

  • [0-5]?:从 0 到 5 的可选数字
  • 后跟任何数字 (d)

当输入为零时,这也适用于"2017/05/02 09:05".您可以选择使用Integer.parseInt(matcher.group(1))String值转换为int


为什么不使用日期/时间 API?

上面的代码不会检查有效日期的所有情况,例如一个月中的天数(包括闰年的二月)。尽管可以使用正则表达式来做到这一点,但它将非常复杂且难以维护,因此IMO最好为此使用适当的API(只是闰年验证本身是一个非常复杂的表达式)。

如果您只是出于学习目的而编写此代码,那么这很好。但对于真正的业务应用程序,最好使用日期/时间 API(正则表达式很棒,但并不总是所有事情的最佳工具)。

如果您使用的是Java 8,请考虑使用新的java.timeAPI。它比旧的 API 更容易、更少错误且更不容易出错。

如果您使用的是Java6或7,则可以使用ThreeTen Backport,这是Java 8的新日期/时间类的绝佳反向移植。对于Android,您还需要ThreeTenABP(更多有关如何使用它的信息)。

下面的代码适用于这两种情况。 唯一的区别是包名称(在Java 8中是java.time,在ThreeTen Backport(或Android的ThreeTenABP)中是org.threeten.bp),但类和方法名称是相同的。

首先,您可以使用DateTimeFormatter并将输入解析为LocalDateTime(表示日期和时间的类,与输入数据完美匹配)。然后,使用此类获取所需的字段:

String input = "2017/5/2 9:5";
// pattern with optional zero for month, day, hour and minute
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy/M/d H:m")
// use strict mode to validate dates like Feb 29th
.withResolverStyle(ResolverStyle.STRICT);
LocalDateTime dt = LocalDateTime.parse(input, fmt);
int year = dt.getYear();
int month = dt.getMonthValue();
int day = dt.getDayOfMonth();
int hour = dt.getHour();
int minute = dt.getMinute();

这也适用于"2017/05/02 09:05"。这也具有检查无效值的优点(例如 12>月或闰年的 2 月 29等)。

如果不使用严格模式,则在非闰年中将 2 月29 日调整为 2 月 28(这是智能旋转变压器样式的行为,这是默认值)。

检查 javadoc 中是否有DateTimeFormatter接受的所有可用模式。

相关内容

最新更新