有没有一种聪明的方法来编写固定长度的平面文件?

是否有任何框架/库可以帮助在java中编写固定长度的平面文件?

我想把一个bean /实体的集合写成一个平面文件,而不用担心转换,填充,对齐,填充等等

例如,我想解析一个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 

如果您仍在寻找框架,请访问http://www.beanio.org查看BeanIO

您不太可能遇到可以应对“遗留”系统格式的框架。 在大多数情况下,Legacy系统不使用标准格式,但框架期望它们。 作为传统COBOL系统和Java / Groovy转换的维护者,我经常遇到这种不匹配。 “担心转换,填充,对齐,填充等问题”主要是您在处理遗留系统时所做的事情。 当然,您可以将其中的一部分封装成方便的助手。 但最有可能的是,你需要熟悉java.util.Formatter。

例如,您可以使用Decorator模式创建装饰器来进行转换。 下面是一些groovy(很容易转换成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% 

如果您没有太多这些并且对象不太复杂,这是可行的。 但很快格式字符串变得无法容忍。 然后你可能想要Date的装饰器,比如:

 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中作为例子...;)

uniVocity-parsers在支持棘手的固定宽度格式方面有很长的路要走,包括具有不同字段,填充等的行。

查看此示例以编写虚构的客户端和帐户详细信息。 这使用前瞻值来标识写入行时使用的格式:

  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许可证)

Spring Batch有一个FlatFileItemWriter ,但除非你使用整个Spring Batch API,否则这对你没有帮助。


但除此之外,我要说你只需要一个能够轻松写入文件的库(除非你想自己编写整个IO代码)。

想到的两个是:

番石榴

 Files.write(stringData, file, Charsets.UTF_8); 

Commons / IO

 FileUtils.writeStringToFile(file, stringData, "UTF-8"); 

不知道任何框架工作,但你可以使用RandomAccessFile。 您可以将文件指针定位到文件中的任何位置以进行读写操作。

我刚找到一个很好的库,我正在使用它:
http://sourceforge.net/apps/trac/ffpojo/wiki

很容易配置XML或注释!

图书馆Fixedformat4j是一个非常巧妙的工具,可以做到这一点: http ://fixedformat4j.ancientprogramming.com/

将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; } }