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

是否有任何框架/库可以帮助在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

...


答案 1

您不太可能遇到可以应对“Legacy”系统格式的框架。在大多数情况下,遗留系统不使用标准格式,但框架需要它们。作为传统 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,YYYYDDD)和数字(假设十进制,显式十进制,符号作为尾随分隔或符号作为前导浮动字符)的所有荒谬方式。然后你就会意识到为什么没有人为此制作一个好的框架,偶尔会把你的生产代码的一部分发布到SO中作为例子......;)


答案 2

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


推荐