15

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

...

4

9 回答 9

12

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

于 2011-08-04T17:27:08.587 回答
12

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

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

如果您没有太多这些并且对象不是太复杂,这是可行的。但是很快格式字符串就变得无法忍受了。然后你可能想要 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 作为示例......;)

于 2011-05-05T22:01:30.307 回答
2

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

于 2016-05-02T03:40:41.803 回答
1

Fixedformat4j 库是一个非常简洁的工具,可以做到这一点:http: //fixedformat4j.ancientprogramming.com/

于 2013-08-15T09:24:45.273 回答
1

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


但除此之外,我想说您只需要一个使写入文件变得容易的库(除非您想自己编写整个 IO 代码)。

想到的两个是:

番石榴

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

公地/ IO

FileUtils.writeStringToFile(file, stringData, "UTF-8");
于 2011-05-04T14:35:27.820 回答
0

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

于 2011-05-04T14:35:32.157 回答
0

我刚刚找到了一个我正在使用的不错的库:http:
//sourceforge.net/apps/trac/ffpojo/wiki

使用 XML 或注释进行配置非常简单!

于 2011-05-06T12:39:09.153 回答
-1

将 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]);

您需要更具体地说明要对文件执行的操作。;)

于 2011-05-04T14:40:38.227 回答
-1

尝试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;
    }
}
于 2016-08-29T15:16:42.813 回答