Java-- do-while 循环跳转到下一次迭代而不检查退出条件?

Java-- do-while loop jumping to next iteration without checking exit condition?

提问人:ChronicallyOverEngineered 提问时间:8/18/2023 更新时间:8/19/2023 访问量:91

问:

我已经使用 BufferedReader 为从文本文件中读取的方法编写了 do-while 代码。如果你想要上下文,我正在做臭名昭著的基于文本的冒险游戏,这种方法是从文本文件中提取我的游戏“物品”,以便它们可以在我的 HashMap 中制作成对象。

无论如何,我的一些描述有多行,所以我想确保它能捕捉到每一行。

下面是完整的代码块:

while ((currentLine = readerObject.readLine()) != null) {
     if (currentLine.startsWith("name: ")) {
          name = currentLine.substring(6);
     } else if (currentLine.startsWith("location: ")) {
          location = currentLine.substring(10);
     } else if (!currentLine.startsWith("DONE")) {
           do {
                if (currentLine.startsWith("description: ")) {
                     description = currentLine.substring(13);
                     currentLine = readerObject.readLine();
                } else { 
                     description = description.concat(currentLine);
                     currentLine = readerObject.readLine();
                }
           } while (!currentLine.startsWith("DONE"));
           itemsAll.put(name, new Items(name, location, description));
     }
}

这是我代码中存在问题的特定部分:

do {
     if (currentLine.startsWith("description: ")) { 
           description = currentLine.substring(13);
           currentLine = readerObject.readLine();
     } else { 
           description = description.concat(currentLine);
           currentLine = readerObject.readLine();
     }
} while (!currentLine.startsWith("DONE"));

如果以“description:”开头的行紧随其后的是“DONE”行,那么我的目的是在第一次迭代结束时评估退出条件时退出循环。相反,它循环回另一个迭代,所以当它到达退出条件时,它认为currentLine为null,并给了我这个:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.startsWith(String)" because "<local2>" is nullat TextInput.inputItems(TextInput.java:64)

我很困惑为什么一旦 currentLine 更新到 DONE,do-while 循环就不会退出?我已经在纸上手动完成了它(确定每个步骤的每个值和输出是什么),并在打印语句中添加了测试问题所在,但我只是不明白为什么。

我已经想出了一个更简化的解决方案,但我觉得我错过了一些关于为什么原始代码不起作用的基本知识......谁能解释一下?

对不起,如果原因很明显......我今天编码了太多小时,所以我的大脑可能很糊。

Java Do-While (java do-while)

评论

0赞 user16320675 8/18/2023
实现不是很稳定。在 I 会首先检查 (而不是 ) 并在该块循环中,而不是else if"description"! "DONE"if"DONE"

答:

0赞 hermit 8/18/2023 #1

我看到两件事可能出错了:

  1. 尽管内部末端,但最顶层的循环仍在执行中。所以,这就是为什么你的程序没有退出。do/whilewhile

  2. 您需要以与最顶层循环相同的方式在内部执行 null 检查。具体而言,在执行以下操作后:do/whilewhilecurrentLine = readerObject.readLine();

评论

0赞 ChronicallyOverEngineered 8/18/2023
关于你的第一点......如果以“DONE”开头,则下一行是...1、满足退出条件; 休息时间 2.执行 3.返回外环 4 的起点。检查条件 () 将 readerObject 移动到下一个文件行,即 。5.是,所以条件不满足。在执行内部代码块之前中断循环。这就是我认为它会执行的方式,我做错了什么?currentLinenulldo/whiledo/whileitemsAll.put(name, new Items(name, location, description));whilecurrentLine = readerObject.readLine() != nullnullcurrentLinenullwhile
0赞 hermit 8/18/2023
请学习使用断点调试代码。仅通过查看代码很难推理代码,尽管这是一项很好的技能。
1赞 user22390653 8/18/2023 #2

我觉得我错过了一些关于为什么原版的基本知识 代码不起作用

你缺少的是你不需要另一个做。while 循环

您需要做的就是:

  • 逐行读取文件
  • 如果找到带有“DONE”的行,请保存(使用代替 ,它更直观,并强制用户不要将“DONE”与其他行混淆)ItemcontainsstartsWith()
  • 将任何其他行保存到变量中。

样本:

while ((currentLine = readerObject.readLine()) != null) {
    if (currentLine.contains("DONE")) { 
        itemsAll.put(name, new Items(name, location, description));
    }
    else if (currentLine.startsWith("name: ")) {
        name = currentLine.substring(6);
    }
    else if (currentLine.startsWith("location: ")) {
        location = currentLine.substring(10);
    }
    else if (currentLine.startsWith("description: ")) {
        description = currentLine.substring(13);
    } else {
        description = description.concat(currentLine);
    }
}
-1赞 Oleg Cherednik 8/19/2023 #3

这是您的模型:Record

@Getter
@Builder
public final class Record {

    private final String name;
    private final String location;
    private final String description;

}

这是主要方法的样子:

Path file = Path.of("foo.txt");
List<Record> records = readRecords(file);

这是从文件中读取记录的方法,其中任何参数都可以有多行:

public static List<Record> readRecords(Path file) throws IOException {
    try (Stream<String> stream = Files.lines(file, StandardCharsets.UTF_8)) {
        List<Record> records = new ArrayList<>();
        Queue<String> recordLines = new LinkedList<>();

        stream.forEach(line -> {
            if ("DONE".equalsIgnoreCase(line)) {
                if (!recordLines.isEmpty()) records.add(createRecord(recordLines));
            } else recordLines.add(line);
        });

        if (!recordLines.isEmpty()) records.add(createRecord(recordLines));

        return records.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }
}

private static Record createRecord(Queue<String> recordLines) {
    Map<String, String> map = new HashMap<>();

    while (!recordLines.isEmpty()) {
        Map.Entry<String, String> entry = readMultiLine(recordLines);
        if (!entry.getValue().isBlank()) map.put(entry.getKey(), entry.getValue());
    }

    return createRecords(map);
}

private static Map.Entry<String, String> readMultiLine(Queue<String> recordLines) {
    String marker = null;
    StringBuilder buf = new StringBuilder();

    while (!recordLines.isEmpty()) {
        String recordLine = recordLines.element();
        int pos = recordLine.indexOf(':');

        if (pos > 0) {
            if (marker != null) break;
            marker = recordLine.substring(0, pos);
        }

        buf.append(System.lineSeparator()).append(recordLine.substring(pos + 1));
        recordLines.remove();
    }

    return Map.entry(marker, buf.toString().trim());
}

private static Record createRecords(Map<String, String> map) {
    return Record.builder()
            .name(map.get("name"))
            .location(map.get("location"))
            .description(map.get("description"))
            .build();
}