【问题标题】:Text could not be parsed, unparsed text found无法解析文本,找到未解析的文本
【发布时间】:2021-09-20 21:55:27
【问题描述】:

当我通过的文本符合格式时,我无法弄清楚为什么会收到 DateTimeParseException 错误。以下是导致问题的代码:

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

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

线程“主”java.time.format.DateTimeParseException 中的异常: 无法解析文本“00:02:30​”,在索引 8 处找到未解析的文本

我首先想到的是,它可能会引入额外的空白或类似的东西。所以为了检查这一点,我在变量的每一侧打印了 3 行并打印了这个:

---00:02:30---

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

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

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

以下是我在 Formatter 中选择的每个选项的含义(来自documentation):

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

提前感谢大家的帮助!

【问题讨论】:

  • 格式 k (1-24) 似乎与您的值 00 不匹配。
  • 我忘了在文档中添加它,给定的 k 示例是 0,因此我认为 00 就可以了。无论哪种方式,当我对其进行硬编码时,当用户输入它以及通过该方法传递 00:00:00 字符串时,为什么它会起作用。很坚持这个哈哈!
  • 你能显示读取文件的代码的minimal reproducible example吗?
  • 可以!请给我几分钟。
  • 顺便说一句,你确定你真的想要LocalTime吗?如果这是一首歌曲的长度,Duration 将是更合适的类型。

标签: java duration localtime datetime-parsing datetimeparseexception


【解决方案1】:

使用 java 9 的 strip() 方法。 假设 songTime 是您从文本文件中读取的字符串,然后使用 songTime.strip()。

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

【讨论】:

  • 感谢您的建议!我尝试了 songTime.strip() 并导致完全相同的异常。
【解决方案2】:

你使用了错误的类型

00:02:30 是指Duration,而不是LocalTime

您可以将字符串转换为ISO 8601 format for a Duration,然后将生成的字符串解析为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

ONLINE DEMO

您不需要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

ONLINE DEMO

通过 Trail: Date Time 了解有关现代日期时间 API 的更多信息。

【讨论】:

  • 非常感谢您的建议!我刚刚开始了解 Duration 课程,这个答案帮助我以与我的项目相关的方式理解它!欣赏它。然而,评论中的某个人确实为我的问题提供了解决方案。
【解决方案3】:

文件中的非打印字符

你的字符串中有一个非打印字符(所以不是空格,因为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 格式of day 和LocalTime 解析此格式而不指定格式化程序。最后,Joachim Isaksson 在注释中是正确的,即模式字母 k 对于一天中的小时不正确,因为它可以是 0。kclock-hour-of-day (1-24) .对于一天中的一个小时(00-23),您需要HH

链接

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-01
    • 2020-08-10
    • 2016-10-02
    • 2019-07-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多