是否有任何框架/库可以帮助在java中编写固定长度的平面文件?
我想将 bean/实体的集合写入一个平面文件中,而不必担心转换、填充、对齐、填充等
例如,我想解析一个豆子,如下所示:
public class Entity{
String name = "name"; // length = 10; align left; fill with spaces
Integer id = 123; // length = 5; align left; fill with spaces
Integer serial = 321 // length = 5; align to right; fill with '0'
Date register = new Date();// length = 8; convert to yyyyMMdd
}
。到。。。
name 123 0032120110505
mikhas 5000 0122120110504
superuser 1 0000120101231
。
你不太可能遇到一个可以处理"遗留"系统格式的框架。在大多数情况下,遗留系统不使用标准格式,但框架需要它们。 作为遗留COBOL系统和Java/Groovy转换的维护者,我经常遇到这种不匹配。 "担心转换、填充、对齐、填充等"主要是您在处理遗留系统时所做的。 当然,您可以将其中的一些封装到方便的助手中。 但最有可能的是,你需要真正熟悉java.util.Formatter。
例如,您可以使用修饰器模式来创建修饰器以进行转换。下面是一些时髦的(很容易转换为Java):
class Entity{
String name = "name"; // length = 10; align left; fill with spaces
Integer id = 123; // length = 5; align left; fill with spaces
Integer serial = 321 // length = 5; align to right; fill with '0'
Date register = new Date();// length = 8; convert to yyyyMMdd
}
class EntityLegacyDecorator {
Entity d
EntityLegacyDecorator(Entity d) { this.d = d }
String asRecord() {
return String.format('%-10s%-5d%05d%tY%<tm%<td',
d.name,d.id,d.serial,d.register)
}
}
def e = new Entity(name: 'name', id: 123, serial: 321, register: new Date('2011/05/06'))
assert new EntityLegacyDecorator(e).asRecord() == 'name 123 0032120110506'
如果您没有太多这些对象并且对象不太复杂,这是可行的。但很快格式字符串就变得无法忍受了。然后,您可能需要日期的装饰器,例如:
class DateYMD {
Date d
DateYMD(d) { this.d = d }
String toString() { return d.format('yyyyMMdd') }
}
因此,您可以使用 %s 进行格式化:
String asRecord() {
return String.format('%-10s%-5d%05d%s',
d.name,d.id,d.serial,new DateYMD(d.register))
}
但是对于大量的 bean 属性,字符串仍然太粗了,所以你想要一些理解列和长度的东西,看起来像你交给你的 COBOL 规范,所以你会写这样的东西:
class RecordBuilder {
final StringBuilder record
RecordBuilder(recordSize) {
record = new StringBuilder(recordSize)
record.setLength(recordSize)
}
def setField(pos,length,String s) {
record.replace(pos - 1, pos + length, s.padRight(length))
}
def setField(pos,length,Date d) {
setField(pos,length, new DateYMD(d).toString())
}
def setField(pos,length, Integer i, boolean padded) {
if (padded)
setField(pos,length, String.format("%0" + length + "d",i))
else
setField(pos,length, String.format("%-" + length + "d",i))
}
String toString() { record.toString() }
}
class EntityLegacyDecorator {
Entity d
EntityLegacyDecorator(Entity d) { this.d = d }
String asRecord() {
RecordBuilder record = new RecordBuilder(28)
record.setField(1,10,d.name)
record.setField(11,5,d.id,false)
record.setField(16,5,d.serial,true)
record.setField(21,8,d.register)
return record.toString()
}
}
在你写了足够多的setField()方法来处理你的遗留系统之后,你会简要地考虑把它作为一个"框架"发布在GitHub上,这样下一个糟糕的sap就不必再这样做了。 但是,您将考虑所有荒谬的方式,您看到COBOL存储"日期"(MMDDYY,YYMMDD,YYDDD,YYYYDDD)和数字(假定为十进制,显式十进制,符号为尾随分隔或符号为前导浮动字符)。 然后你会意识到为什么没有人为此制作一个好的框架,并偶尔将你的生产代码的一部分发布到 SO 中作为示例...... ;)
如果您仍在寻找框架,请查看 BeanIO http://www.beanio.org
uniVocity解析器在支持棘手的固定宽度格式方面有很长的路要走,包括具有不同字段,填充等的行。
看看这个例子来写虚构的客户和账户细节。这将使用前瞻值来标识编写行时要使用的格式:
FixedWidthFields accountFields = new FixedWidthFields();
accountFields.addField("ID", 10); //account ID has length of 10
accountFields.addField("Bank", 8); //bank name has length of 8
accountFields.addField("AccountNumber", 15); //etc
accountFields.addField("Swift", 12);
//Format for clients' records
FixedWidthFields clientFields = new FixedWidthFields();
clientFields.addField("Lookahead", 5); //clients have their lookahead in a separate column
clientFields.addField("ClientID", 15, FieldAlignment.RIGHT, '0'); //let's pad client ID's with leading zeroes.
clientFields.addField("Name", 20);
FixedWidthWriterSettings settings = new FixedWidthWriterSettings();
settings.getFormat().setLineSeparator("n");
settings.getFormat().setPadding('_');
//If a record starts with C#, it's a client record, so we associate "C#" with the client format.
settings.addFormatForLookahead("C#", clientFields);
//Rows starting with #A should be written using the account format
settings.addFormatForLookahead("A#", accountFields);
StringWriter out = new StringWriter();
//Let's write
FixedWidthWriter writer = new FixedWidthWriter(out, settings);
writer.writeRow(new Object[]{"C#",23234, "Miss Foo"});
writer.writeRow(new Object[]{"A#23234", "HSBC", "123433-000", "HSBCAUS"});
writer.writeRow(new Object[]{"A#234", "HSBC", "222343-130", "HSBCCAD"});
writer.writeRow(new Object[]{"C#",322, "Mr Bar"});
writer.writeRow(new Object[]{"A#1234", "CITI", "213343-130", "CITICAD"});
writer.close();
System.out.println(out.toString());
输出将是:
C#___000000000023234Miss Foo____________
A#23234___HSBC____123433-000_____HSBCAUS_____
A#234_____HSBC____222343-130_____HSBCCAD_____
C#___000000000000322Mr Bar______________
A#1234____CITI____213343-130_____CITICAD_____
这只是一个粗略的例子。还有许多其他选项可用,包括对带注释的 Java Bean 的支持,您可以在此处找到。
披露:我是这个库的作者,它是开源的,免费的(Apache 2.0许可证)
库 Fixedformat4j 是一个非常简洁的工具,可以做到这一点: http://fixedformat4j.ancientprogramming.com/
有一个FlatFileItemWriter
,但除非你使用整个 Spring Batch API,否则这对你没有帮助。
但除此之外,我想说你只需要一个使写入文件变得容易的库(除非你想自己编写整个IO代码)。
我想到的两个是:
番石榴
Files.write(stringData, file, Charsets.UTF_8);
共享资源/IO
FileUtils.writeStringToFile(file, stringData, "UTF-8");
不知道任何框架工作,但你可以使用RandomAccessFile。您可以将文件指针定位到文件中的任意位置,以便进行读取和写入。
我刚刚找到了一个我正在使用的不错的库:
http://sourceforge.net/apps/trac/ffpojo/wiki
使用XML或注释进行配置非常简单!
将 bean/实体写入平面文件的一种简单方法是使用 ObjectOutputStream。
public static void writeToFile(File file, Serializable object) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));
oos.writeObject(object);
oos.close();
}
您可以使用
FileUtils.writeByteArrayToFile(new File(filename), new byte[length]);
您需要更具体地说明要对文件执行的操作。 ;)
尝试 FFPOJO API,因为它具有创建固定长度的平面文件所需的一切,并且还会将文件转换为对象,反之亦然。
@PositionalRecord
public class CFTimeStamp {
String timeStamp;
public CFTimeStamp(String timeStamp) {
this.timeStamp = timeStamp;
}
@PositionalField(initialPosition = 1, finalPosition = 26, paddingAlign = PaddingAlign.RIGHT, paddingCharacter = '0')
public String getTimeStamp() {
return timeStamp;
}
@Override
public String toString() {
try {
FFPojoHelper ffPojo = FFPojoHelper.getInstance();
return ffPojo.parseToText(this);
} catch (FFPojoException ex) {
trsLogger.error(ex.getMessage(), ex);
}
return null;
}
}