我想捕获日期格式 -yyyy/mm/dd hh:mm
[^nr]*[rn]+([12]d{3}/(0[1-9]|1[0-2])/(0[1-9]|[12]d|3[01]))**
上面的表达式捕获了当天的下一行,但我也想包括时间部分,并且还想捕获月份、日期和时间的个位数,而不必输入两位数。
例如。用户可以输入2017/5/2 9:5
或2017/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.regex
Matcher
,你的代码将是这样的(另请注意,在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[01]) ([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.time
API。它比旧的 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
接受的所有可用模式。