
本文介绍一种鲁棒的 java 解析方案,用于读取格式不规范的 n×n 滑动拼图输入文件——其中空格数量不定、空白位置无显式标记(如 0),需准确定位并填充为 0。
本文介绍一种鲁棒的 java 解析方案,用于读取格式不规范的 n×n 滑动拼图输入文件——其中空格数量不定、空白位置无显式标记(如 0),需准确定位并填充为 0。
在实现十五/八数码等滑动拼图求解器时,输入文件常以“原始排版”形式提供:首行为维度 n,后续 n 行为网格数据,但空格数量不固定,且空白格以连续空格(而非 0 或 *)表示。这导致标准 Scanner.nextInt() 无法区分“数字缺失”与“多余分隔符”,从而错位填充。
核心挑战在于:
- 空白格在文本中表现为多个空格形成的“间隙”(如 "8 3" 中的 5 个空格),而非一个占位符;
- 同一行内数字间空格数可能不同(如 "38 51 15 17 11 27 8 36 7" 中有单空格和双空格);
- 必须精确定位空白格的列索引(0-based),而非仅识别其存在。
✅ 正确思路是:逐行处理 + 基于空格宽度推断空白列位置。关键观察是:当某行拆分后数字个数
以下是经过优化、健壮性增强的完整实现:
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
public class PuzzleInputReader {
public static int[][] readBoard(String filename) throws FileNotFoundException {
Scanner scanner = new Scanner(new File(filename));
int n = scanner.nextInt();
scanner.nextLine(); // consume newline after n
int[][] board = new int[n][n];
int row = 0;
while (scanner.hasNextLine() && row numbers = Arrays.stream(line.split("\s+"))
.filter(s -> !s.isEmpty())
.map(Integer::parseInt)
.collect(Collectors.toList());
// 若数字个数不足 n,说明该行含空白格
if (numbers.size() == n) {
// 正常填充整行
for (int col = 0; col maxGapLen) {
maxGapLen = gapLen;
maxGapStart = start;
}
} else {
i++;
}
}
if (maxGapStart == -1) return 0; // fallback
// 粗略估算:假设数字平均宽度为 2 字符 + 间隔,用总宽度反推列位置
// 更稳健做法:按实际数字起始位置分割(见下方进阶建议)
int estimatedCol = Math.max(0, Math.min(n - 1,
(int) Math.round(maxGapStart / 3.0))); // 3 是经验系数(适配示例格式)
return estimatedCol;
}
// ✅ 进阶推荐:使用正则精确提取每个数字的起始列(100% 准确)
public static int[][] readBoardPrecise(String filename) throws FileNotFoundException {
Scanner scanner = new Scanner(new File(filename));
int n = scanner.nextInt();
scanner.nextLine();
int[][] board = new int[n][n];
int row = 0;
while (scanner.hasNextLine() && row numberPositions = new ArrayList(); // [value, startCol]
Matcher m = Pattern.compile("\d+").matcher(line);
while (m.find()) {
numberPositions.add(new int[]{Integer.parseInt(m.group()), m.start()});
}
if (numberPositions.size() == n) {
for (int i = 0; i p[1]).toArray();
int blankCol = 0;
for (int c = 0; c <p>? <strong>关键注意事项</strong>: </p>
- 不要依赖 scanner.hasNextInt() 循环:它会跨行读取,破坏行列结构;必须 nextLine() 逐行处理。
- 避免硬编码空格数:使用 \s+ 正则分割或 String.trim().split("\s+") 可安全处理任意空格数。
- 定位空白格的黄金法则:优先使用正则 \d+ 提取数字+位置信息,再通过列坐标间隙推断空白列(比纯空格长度更可靠)。
- 边界防护:始终校验 row
? 总结:解决此类问题的核心是「放弃逐词扫描,转向逐行结构分析」。将输入视为带位置信息的文本流,而非扁平化的 token 序列,才能在格式噪声中精准还原语义结构。对于竞赛或生产级代码,推荐使用 readBoardPrecise 方法——它不依赖空格数量启发式,而是基于数字真实坐标,100% 兼容题目所示的所有输入变体。










