
当用 for 循环替代 while 遍历 In 类输入流时,因循环条件在每次迭代末尾才检查,导致最后一次 readLine() 的结果被丢弃,从而跳过文件末尾的括号或数据。
当用 for 循环替代 while 遍历 `in` 类输入流时,因循环条件在每次迭代末尾才检查,导致最后一次 `readline()` 的结果被丢弃,从而跳过文件末尾的括号或数据。
在使用 Princeton 算法课程配套的 edu.princeton.cs.algs4.In 类处理文本输入时,一个常见误区是将 while (!input.isEmpty()) { String s = input.readLine(); ... } 直接改写为 for 循环形式:
for (String s = input.readLine(); !input.isEmpty(); s = input.readLine()) {
// 处理 s
}
该写法存在两个关键缺陷:
-
逻辑执行顺序导致数据丢失
for 循环的结构为 for (初始化; 条件判断; 迭代更新),其执行流程是:
✅ 执行初始化(第一次 readLine())→
✅ 检查条件(此时已读一行)→
✅ 执行循环体 →
✅ 执行迭代更新(再次 readLine())→
❌ 再检查条件(若此时 isEmpty() 返回 true,则循环立即终止,刚读取的 s 从未被处理!)如答案中模拟所示,当 readLine() 返回最后一行后,isEmpty() 变为 true,循环直接退出,该行内容被彻底忽略。
空输入时仍会调用 readLine()
即使文件为空,初始化语句 String s = input.readLine() 也会被执行一次,可能返回 null 或空字符串,引发 NullPointerException 或逻辑错误(如 s.equals("(") 报错)。
✅ 推荐解决方案:优先使用 while 循环(最安全清晰)
In input = new In("parenthesis.txt");
LinkedListStack<string> stack = new LinkedListStack();
String line;
while ((line = input.readLine()) != null) { // 显式检查读取结果
if ("(".equals(line)) {
stack.push(line);
} else if (")".equals(line)) {
if (!stack.isEmpty()) stack.pop();
else stack.push(line); // 多余右括号入栈标记
}
// 忽略其他字符(如空格、换行符),或按需扩展
}
StdOut.println(stack.isEmpty() ? "Balanced" : "Unbalanced");</string>
✅ 若坚持用 for 循环:封装为 Iterable(符合 Java 习惯且健壮)
public class InIterable implements Iterable<string> {
private final In in;
public InIterable(In in) { this.in = in; }
@Override
public Iterator<string> iterator() {
return new Iterator() {
@Override
public boolean hasNext() { return !in.isEmpty(); }
@Override
public String next() { return in.readLine(); }
};
}
}
// 使用方式:
for (String s : new InIterable(input)) {
// 安全处理每一行,无遗漏、无冗余读取
}</string></string>
⚠️ 额外建议:优化括号匹配逻辑
- 当前代码逐行读取,但题目要求“读入文本流”,更合理的做法是逐字符处理(如 input.readChar()),以支持单行内多个括号(如 "(){}")。
- 使用 Character 而非 String 可节省内存并提升效率:
while (!input.isEmpty()) { char c = input.readChar(); if (c == '(' || c == '[' || c == '{') stack.push(String.valueOf(c)); else if (c == ')' || c == ']' || c == '}') { /* 匹配逻辑 */ } }
总结:for 循环不适用于“先读取、再判断”的流式输入场景;务必以 while ((line = readLine()) != null) 模式确保每行都被处理,或通过 Iterable 封装实现安全的增强 for 循环。










