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

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

你遇到的问题本质并非printf失效,而是rec变量直接存储了原始未解析的整行字符串(如"000001BilboBagginsEsq.1020"),而printf("\n%4d %-60s ", line, rec)只是将整个长串按60字符左对齐打印——这当然无法产生你期望的“字段对齐”效果。

真正需要的是:先结构化解析字段,再分别格式化输出。由于原始数据无空格、逗号等分隔符,必须依赖字段的语义规律(如ID全数字、姓名首字母大写、年份结尾为数字等)进行正则切分。

以下是一个完整、健壮的解决方案:

✅ 步骤一:定义结构化数据类与解析逻辑

import java.util.regex.Matcher;
import java.util.regex.Pattern;

class Entry {
    private final String id;
    private final String firstName;
    private final String lastName;
    private final String title;
    private final String birthYear;

    // 使用正则精准捕获5个字段:ID(数字) + FirstName(大写开头单词) + LastName(同上) + Title(同上) + Year(数字)
    private static final Pattern PATTERN = Pattern.compile(
       

"(\\d+)([A-Z][a-z._]+)([A-Z][a-z._]+)([A-Z][a-z._]+)(\\d+)" ); public static Entry parse(String rawLine) { Matcher m = PATTERN.matcher(rawLine); if (!m.matches()) { throw new IllegalArgumentException("Invalid format: " + rawLine); } return new Entry( m.group(1), m.group(2), m.group(3), m.group(4), m.group(5) ); } private Entry(String id, String firstName, String lastName, String title, String birthYear) { this.id = id; this.firstName = firstName; this.lastName = lastName; this.title = title; this.birthYear = birthYear; } @Override public String toString() { // 每字段分配15字符宽度,左对齐;年份不加空格(右对齐更自然,此处保持左对齐一致性) return String.format("%-15s%-15s%-15s%-15s%s", id, firstName, lastName, title, birthYear); } }

✅ 步骤二:在文件读取循环中解析并打印

// 假设 reader 已由 JFileChooser 初始化
int lineNum = 0;
String rec;
while ((rec = reader.readLine()) != null) {
    lineNum++;
    try {
        Entry entry = Entry.parse(rec);
        // 输出:行号 + 格式化后的字段(符合你的示例样式)
        System.out.printf("%d %s%n", lineNum, entry);
    } catch (IllegalArgumentException e) {
        System.err.println("Parse error at line " + lineNum + ": " + e.getMessage());
    }
}
System.out.println("\nData file read!");

⚠️ 关键注意事项

  • 正则可靠性:当前正则假设姓名/头衔均由“首字母大写+小写字母/点/下划线”组成(如 Bilbo, Baggins, Esq.)。若实际数据含空格、连字符(如 Mary-Jane)或小写前缀(如 de Gaulle),需扩展正则,例如 (\\d+)([A-Z][a-z._\\-]+)([A-Z][a-z._\\-]+)([A-Z][a-z._\\-]+)(\\d+)。
  • 边界处理:reader.ready() 不是安全的循环条件(尤其对网络流或缓冲问题),应始终用 readLine() != null 判断。
  • 资源管理:推荐使用 try-with-resources(如答案所示),避免手动 close() 遗漏导致文件句柄泄漏。
  • 性能提示:Pattern.compile() 是开销操作,应定义为 static final,避免在循环内重复编译。

✅ 最终输出效果

输入行:000001BilboBagginsEsq.1020
输出:

1 000001         Bilbo          Baggins        Esq.           1020

完全匹配你的目标格式——字段间由空格对齐,视觉清晰,便于阅读与后续处理。

此方法将“原始字符串→结构化对象→格式化视图”的职责分离,既保证解析准确性,又赋予输出高度可控性,是处理无分隔符定长/变长记录的标准实践。