无法分析文本,找到未分析的文本



当我传递的文本符合格式时,我无法弄清楚为什么会出现DateTimeParseException错误。下面是导致此问题的代码:

LocalTime lt = LocalTime.parse(songTime, 
DateTimeFormatter.ofPattern("k:m:s"));

这是奇怪的事情。每当我查询用户一段时间(让我们以 00:02:30 为例)时,它都会完全按照我的意愿运行。但是当我使用我的方法(从文本文件中提取时间)时,它会给出错误:

线程"main"中的异常 java.time.format.DateTimeParseException: 无法分析文本"00:02:30",在索引 8 中找到未分析的文本

我假设的第一件事是,它可能带来了额外的空格或类似的东西。所以为了检查这一点,我在变量的每一侧打印了 3 行,它打印了这个:

---00:02:30---

正如你在上面看到的,没有空格。如果我硬编码 00:02:30,那么它也可以完美运行。然后我遇到了另一件困扰我的事情。我的文本文件如下所示:

00:00:00 First
00:02:30 Second

第一次完美通过,但之后的任何人都会导致错误。它们都具有完全相同的格式,两边都没有空格,所以我没有看到问题所在。我检查了关于这个问题的每个论坛帖子,其中大多数是使用错误格式、错误字符串等的个人。我不确定这里的情况是否如此,因为当我对其进行硬编码或查询用户输入时,它可以完美运行。

以下是我在格式化程序中选择的每个选项的含义(来自文档):

k       clock-hour-of-am-pm (1-24)
m       minute-of-hour 
s       second-of-minute

以下是读取文件的方法:

public static ArrayList<Song> scanInSongs () {
ArrayList<Song> songArray = new ArrayList<Song>();
try { 
BufferedReader reader = new BufferedReader(new FileReader("Description.txt"));
String line;
while ((line = reader.readLine()) != null) {
String key = line.substring(0, line.indexOf(' '));
System.out.println("Fetched timestamp: "+ key);
String value = line.substring(line.indexOf(' ') + 1);
System.out.println("Fetched name: "+ value);
Song song = new Song(value, "", key);
songArray.add(song);
}
} catch (IOException e) {
System.out.println("File not found, exception: "+ e);
} 
return songArray;

}

歌曲类:

public class Song {
private String duration = "";
private String name = "";
private String timestampFromVideo = "";
public Song(String name, String timestampFromVideo, String duration) {
if (name == "") {
this.name = "";   
} else {
this.name = name;
}
this.duration = duration;
this.timestampFromVideo = timestampFromVideo;
}
public String getName() {
return this.name;
}
public String getDuration() {
return this.duration;
}
public String getTimestampFromVideo() {
return this.timestampFromVideo;
}
public void setDuration(String duration) {
this.duration = duration;
}
}

主要:

public static void main(String[] args) {
ArrayList<Song> songArray = scanInSongs();
String songTime = songArray.get(0).getDuration();
LocalTime lt = LocalTime.parse(songTime, 
DateTimeFormatter.ofPattern("k:m:s"));       
}

最后如前所述是文件:

00:00:00 First
00:02:30 Second

提前感谢大家的帮助!

文件中的非打印字符

你的字符串中有一个非打印字符(所以不是空格,因为strip()没有帮助)。由于字符串已从文件中读取,因此该字符必须位于文件中。

如何检查:

key.chars().forEach(System.out::println);

如果key只是"00:02:30",这将打印

48
48
58
48
50
58
51
48

我敢打赌你得到的产出比这更多。例如,如果您有:

48
48
58
48
50
58
51
48
8203

在这里我们可以看到,字符串末尾有一个 unicode 值为 8203(十进制)的字符。这是一个零宽度的空间,这解释了为什么我们在打印字符串时看不到它。LocalTime.parse()可以看到它,这解释了您收到的错误消息。

有趣(也许令人失望)零宽度的空格不算作 anish sharma 在他的答案中建议的strip方法的空格,这就是为什么该答案没有解决问题的原因。

好的解决方案是从文件中删除该字符。

另一个改进建议:java.time.Duration

我完全同意Arvind Kumar Avinash的回答:你应该使用Duration类一段时间(如果你有一天遇到超过24小时的持续时间,LocalTime也将不再有效)。你应该更进一步地使用Duration,在你的模型类中将持续时间保留为Duration,而不是字符串。就像您将数字保留在int变量中而不是字符串中一样(我非常希望)。

public class Song {
private Duration duration;
private String name;
// …

由于您的实例变量始终是从构造函数初始化的,因此不要在声明中为它们提供默认值,这只会混淆。你可以编写一个方便的构造函数,将字符串解析为Duration,例如:

/** @throws DateTimeParseException if durationString is not in format hh:mm:ss */
public Song(String name, String timestampFromVideo, String durationString) {
this.name = "";   
String iso = durationString.replaceFirst(
"^(\d{2}):(\d{2}):(\d{2})$", "PT$1H$2M$3S");
duration = Duration.parse(iso);

您的if语句测试name是否为空既错误又多余,所以我省略了它。不能使用==比较字符串。要测试字符串是否为空,请使用name.isEmpty()(要求字符串为非空,当您从文件中读取它时)。由于您无论如何都将空字符串分配给name,因此我发现省略检查更简单。

我将持续时间字符串转换为Duration.parse()所需的ISO 8601格式的方式与Arvind Kumar Avinash的答案不同。如果他的方式更容易理解,一定要使用它,那就好了。

奖励:如果您希望转换为ISO 8601在秒后省略任何字符,包括零宽度空格,请使用以下变体:

String iso = durationString.replaceFirst(
"^(\d{2}):(\d{2}):(\d{2}).*$", "PT$1H$2M$3S");

我在表示字符串末尾的$之前插入了.*。这将匹配可能存在的任何字符,并导致它们不进入 ISO 8601 字符串。所以现在当文件中有零宽度的空格时,解析也有效。

此外,如果您使用Duration则无关紧要,Arvind 也是正确的,如果您想解析为LocalTime,则不需要DateTimeFormatter,因为00:02:30一天中的某个时间采用 ISO 8601 格式,并且LocalTime在没有指定格式化程序的情况下解析此格式。最后,Joachim Isaksson在注释中是正确的,即模式字母k对于一天中的小时是不正确的,因为它可以是0。k表示一天中的时钟小时 (1-24)。对于一天中的一小时 (00–23),您将需要HH.

链接

  • FileFormat.Info 上的 Unicode 字符"零宽度空格"(U+200B)(重复您自己的链接)
  • Duration类的文档
  • 维基百科文章:ISO 8601

您使用了错误的类型

00:02:30,你的意思是Duration,而不是LocalTime

您可以将字符串转换为 ISO 8601 格式的持续时间,然后将生成的字符串解析为Duration

演示:

import java.time.Duration;
public class Main {
public static void main(String[] args) {
System.out.println(parse("00:02:30"));
System.out.println(parse("00:00:00"));
}
static Duration parse(String strDuration) {
String[] arr = strDuration.split(":");
Duration duration = Duration.ZERO;
if (arr.length == 3) {
strDuration = "PT" + arr[0] + "H" + arr[1] + "M" + arr[2] + "S";
duration = Duration.parse(strDuration);
}
return duration;
}
}

输出:

PT2M30S
PT0S

在线演示

您无需DateTimeFormattter即可解析 ISO 8601 格式的时间

现代日期-时间 API 基于 ISO 8601,只要日期-时间字符串符合 ISO 8601 标准,就不需要显式使用DateTimeFormatter对象。

演示:

import java.time.LocalTime;
public class Main {
public static void main(String[] args) {
System.out.println(LocalTime.parse("00:02:30"));
System.out.println(LocalTime.parse("00:00:00"));
}
}

输出:

00:02:30
00:00

在线演示

要了解有关现代日期-时间 API 的更多信息,请参阅跟踪:日期时间

使用 Java 9 的 strip() 方法。 假设 songTime 是你从文本文件中读取的字符串,那么使用 songTime.strip()。

strip() 方法删除所有 Unicode 空格以及普通空格

最新更新