使用多个可选模式时顺序的重要性

Importance of order when using multiple optional patterns

DateTimeFormatter 中可选模式的顺序如何影响解析操作?

我是 运行 这个程序,想知道为什么最后一行抛出异常而不是前三行。

public static void main(String[] args) {
  String p1 = "[EEEE][E] dd-MM-yyyy";
  String p2 = "[E][EEEE] dd-MM-yyyy";
  String date1 = "Thu 07-01-2016";
  String date2 = "Thursday 07-01-2016";
  parse(date1, p1); //OK
  parse(date1, p2); //OK
  parse(date2, p1); //OK
  parse(date2, p2); //Exception
}

private static void parse(String date, String pattern) {
  DateTimeFormatter fmt = DateTimeFormatter.ofPattern(pattern, Locale.ENGLISH);
  System.out.println(fmt.parse(date));
}

最后一行的异常是:

java.time.format.DateTimeParseException: Text 'Thursday 07-01-2016' could not be parsed at index 3

可选格式的顺序很重要:

当格式 [E][EEEE] dd-MM-yyyy 的解析器解析 "Thursday 07-01-2016" 时,它

  • 使用可选部分 [E] 使用 Thu
  • 跳过 [EEEE] 因为它无法识别长 day-of-week
  • 现在需要一个 space 并且因为它看到 r 而失败,因此抛出一个错误索引为 3 的异常。

因此,如果您使用可选部分来允许解析替代版本(此处使用长或短 day-of-weeks)首先添加更具体的格式。

How does the order of optional patterns in a DateTimeFormatter affect the parsing operation?

解析器尝试按照每个可选部分在模式中出现的顺序进行匹配。

注意字符串"Thursday"以"Thu"开头,可以用模式片段"E"匹配。接下来观察索引3处报匹配失败,对应"Thursday"中的'r'。在错误情况下发生的是解析器将字符串的前三个字符匹配到第一个可选部分,跳过第二个可选部分,因为它不匹配字符串的下一部分,然后无法匹配 'r'.

换句话说,这些格式化程序不会回溯以尝试替代匹配。在正则表达式中,可选部分是贪婪的。

另请注意,您的两种模式都比您想要的更宽松。例如,您的模式 p1 将匹配字符串 "ThursdayThu 07-01-2016".

文档没有提到任何优先级,我会争辩说你得到的结果是正常的。它是从左到右读取String格式的结果。

  • 让我们考虑第一种格式"[EEEE][E] dd-MM-yyyy"

    • "Thu 07-01-2016":API 尝试查找是否可以匹配第一个可选部分 "[EEEE]"。引用 DateTimeFormatter Javadoc 关于文本标记

      Exactly 4 pattern letters will use the full form.

      在本例中是星期几的完整形式。这与 "Thu" 不匹配,因此将跳过可选部分。然而,第二个可选部分是 "[E]",并且仍然引用

      Less than 4 pattern letters will use the short form.

      所以这将匹配 "Thu"。这样才能正确理解要解析的String

    • "Thursday 07-01-2016":同上,只是会在第一个可选部分匹配"Thursday"。但是 API 仍将继续搜索下一个可选部分的有效部分,"[E]" 但它不会找到任何部分,因此跳过可选部分。
  • 现在让我们考虑第二种格式"[E][EEEE] dd-MM-yyyy"
    • "Thu 07-01-2016":API 尝试查找第一个可选部分 "[E]" 是否可以匹配并且它确实适用于 "Thu"。如上所述,API 现在将尝试查找 "[EEEE]" 的匹配项,但找不到任何匹配项,因此跳过可选部分。
    • "Thursday 07-01-2016":API 再次尝试匹配 "[E]",这就是事情发生的地方:它确实匹配。 "Thursday""Thu" 开头,因此格式化程序能够找到匹配项。但是随后,它会尝试解析其余部分 "rsday 07-01-2016"[EEEE] 可选部分将不匹配,因此将被跳过。然后它以 space 失败,因为剩下的没有 space (取而代之的是 "r")。

所以如果你 运行 你的代码

parse("ThuThursday 07-01-2016", "[E][EEEE] dd-MM-yyyy");

您会发现它有效:"[E]" 匹配 "Thu" 并且 "[EEEE]" 匹配 "Thursday".

注意异常消息是如何暗示这一点的(重点是我的):

java.time.format.DateTimeParseException: Text 'Thursday 07-01-2016' could not be parsed at index 3

索引 3 对应于 "rsday""r",所以这意味着它能够解析,直到此时。