将定长字符串按位解析为Java对象通常涉及以下步骤:定义数据结构、解析字符串、映射到Java对象。以下是完整的实现方案:
1. 定义数据结构
首先需要明确定长字符串的结构,例如:
-
前2位:版本号
-
接下来4位:用户ID
-
接下来8位:交易金额(单位:分)
-
接下来6位:日期(YYYYMMDD)
-
接下来3位:状态码
示例字符串:01001234560012345620230515123
2. 创建Java对象
public class Transaction {
private String version;
private String userId;
private long amount; // 以分为单位
private LocalDate date;
private String statusCode;
// 构造方法、getter和setter省略
}
3. 使用序列化手段解析
方案一:手动解析
public class StringParser {
public static Transaction parse(String fixedString) {
if (fixedString == null || fixedString.length() != 23) {
throw new IllegalArgumentException("字符串长度必须为23位");
}
Transaction transaction = new Transaction();
transaction.setVersion(fixedString.substring(0, 2));
transaction.setUserId(fixedString.substring(2, 6));
transaction.setAmount(Long.parseLong(fixedString.substring(6, 14)));
transaction.setDate(parseDate(fixedString.substring(14, 22)));
transaction.setStatusCode(fixedString.substring(22));
return transaction;
}
private static LocalDate parseDate(String dateStr) {
return LocalDate.of(
Integer.parseInt(dateStr.substring(0, 4)),
Integer.parseInt(dateStr.substring(4, 6)),
Integer.parseInt(dateStr.substring(6, 8))
);
}
}
方案二:使用注解驱动解析(更优雅)
-
创建字段注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FixedWidthField {
int start();
int length();
}
-
增强Transaction类:
public class Transaction {
@FixedWidthField(start = 0, length = 2)
private String version;
@FixedWidthField(start = 2, length = 4)
private String userId;
@FixedWidthField(start = 6, length = 8)
private long amount;
@FixedWidthField(start = 14, length = 8)
private LocalDate date;
@FixedWidthField(start = 22, length = 1)
private String statusCode;
// getter和setter
}
-
创建通用解析器:
public class FixedWidthParser {
public static <T> T parse(String data, Class<T> clazz) throws Exception {
T obj = clazz.getDeclaredConstructor().newInstance();
for (Field field : clazz.getDeclaredFields()) {
FixedWidthField annotation = field.getAnnotation(FixedWidthField.class);
if (annotation != null) {
String value = data.substring(annotation.start(),
annotation.start() + annotation.length());
field.setAccessible(true);
if (field.getType() == String.class) {
field.set(obj, value);
} else if (field.getType() == long.class || field.getType() == Long.class) {
field.setLong(obj, Long.parseLong(value));
} else if (field.getType() == LocalDate.class) {
field.set(obj, LocalDate.parse(value, DateTimeFormatter.BASIC_ISO_DATE));
}
// 添加其他类型的处理...
}
}
return obj;
}
}
4. 使用示例
public class Main {
public static void main(String[] args) throws Exception {
String data = "01001234560012345620230515123";
// 手动解析
Transaction t1 = StringParser.parse(data);
System.out.println(t1);
// 注解驱动解析
Transaction t2 = FixedWidthParser.parse(data, Transaction.class);
System.out.println(t2);
}
}
5. 性能优化建议
-
对于高频调用场景,可以考虑:
-
缓存反射结果
-
使用字节码生成技术(如ASM)生成高效解析代码
-
预编译正则表达式
-
-
对于超大字符串处理:
-
使用流式处理
-
考虑内存映射文件
-
6. 序列化为定长字符串
反向操作,将Java对象序列化为定长字符串:
public class FixedWidthSerializer {
public static String serialize(Transaction transaction) {
return String.format("%2s%4s%08d%8s%1s",
transaction.getVersion(),
transaction.getUserId(),
transaction.getAmount(),
transaction.getDate().format(DateTimeFormatter.BASIC_ISO_DATE),
transaction.getStatusCode());
}
}
7. 使用现有库
如果不想自己实现,可以考虑以下库:
-
FixedFormat4J:专门处理定长格式的库
-
Bindy:Camel项目的固定长度格式化工具
-
JRecord:处理各种固定长度和变长记录
这些方案提供了从简单到复杂的多种实现方式,可以根据项目需求选择合适的方案。