如何使用正则表达式解析无分隔符的固定格式文件并格式化输出

本文讲解如何解析类似"000001bilbobagginsesq.1020"这样无分隔符的紧凑字符串,通过正则匹配提取字段,并用`printf`或`string.formatted()`实现对齐排版输出。

原始问题中,文件内容是连续拼接、无任何分隔符(如空格、逗号、制表符)的字符串,例如 "000001BilboBagginsEsq.1020"。直接调用 System.out.printf("%-60s", rec) 只是对整行做宽幅左对齐,无法自动拆分字段——这正是你看到 1 000001BilboBagginsEsq.1020 的原因:printf 并未“理解”字段边界,它只是把整个 rec 当作一个字符串处理。

要实现目标格式:

000001          Bilbo              Baggins           Esq.          1020

必须先结构化解析原始字符串,再按字段分别格式化。推荐使用 java.util.regex.Pattern 和 Matcher 进行稳健的字段提取。

✅ 正确步骤:解析 + 格式化

  1. 定义字段规则(基于示例数据推断):

    • 字段1(ID):6位纯数字 → \\d{6}
    • 字段2(First Name):首字母大写 + 小写字母/点/下划线 → [A-Z][a-z._]+
    • 字段3(Last Name):同上
    • 字段4(Title):同上(如 "Esq." 包含点)
    • 字段5(Year):4位数字 → \\d{4}
  2. 编写解析类(推荐封装)

import java.util.regex.*;

class Entry {
    String id, firstName, lastName, title, birthYear;

    static Entry parse(String line) {
        Entry e = new Entry();
        // 精确匹配全部5个字段(避免过度匹配)
        String regex = "(\\d{6})([A-Z][a-z._]+)([A-Z][a-z._]+)([A-Z][a-z._]+)(\\d{4})";
        Pattern p = Pattern.compile(regex);
        Matcher m = p.matcher(line);
        if (m.matches()) { // 使用 matches() 确保整行匹配,更安全
            e.id = m.group(1);
            e.firstName = m.group(2);
            e.lastName = m.group(3);
            e.title = m.group(4);
            e.birthYear = m.group(5);
        } else {
            System.err.println("Warning: Line format mismatch — '" + line + "'");
        }
        return e;
    }

    @Override
    public String toString() {
        // 每字段预留15字符宽度,左对齐;年份右对齐更自然(可选)
        return String.format("%-15s%-15s%-15s%-15s%-15s", 
            id, firstName, lastName, title, birthYear);
    }
}
? 提示:String.format() 或 String.formatted()(Java 15+)比链式 += 更高效、可读性更强;%-15s 表示左对齐、最小宽度15。
  1. 在文件读取循环中使用(注意资源管理与异常处理):
JFileChooser chooser = new JFileChooser();
if (chooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
    File file = chooser.getSelectedFile();
    try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
        String line;
        int lineNumber = 0;
        while ((line = reader.readLine()) != null) {
            lineNumber++;
            Entry entry = Entry.parse(line.trim()); // trim() 去除可能的首尾空白
            System.out.printf("%-4d %s%n", lineNumber, entry); // 输出:行号 + 格式化记录
        }
    } catch (IOException e) {
        System.err.println("读取文件失败: " + e.getMessage());
    }
}
System.out.println("\nData file read!");

⚠️ 注意事项与优化建议

  • 正则健壮性:若实际数据存在变长(如 ID 不总是 6 位),应调整为 (\\d+) 并配合业务逻辑校验,而非硬编码 {6}。
  • 大小写敏感:当前正则依赖 Bilbo/Baggins 首字母大写。若数据不规范(如全小写),需改用 (?i)[a-z] 或预处理标准化。
  • 性能考虑:若文件极大,可将 Pattern.compile(regex) 提升为 static final Pattern,避免重复编译。
  • 替代方案:若字段长度严格固定(如 ID=6, FirstName=10, LastName=12…),可用 substring() 切片,更快但缺乏容错性。

最终输出效果(含行号):

1    000001         Bilbo          Baggins        Esq.           1020      
2    000002         Frodo          Baggins        Mr.            1050      

通过结构化解析 + 字段级格式化,你就能精准控制每一列的宽度与对齐方式,彻底解决“printf 被忽略”的表象问题——本质不是 printf 失效,而是输入数据尚未被正确分解。