Есть ли умный способ написать файл с фиксированной длиной?

Есть ли какой-либо фреймворк/библиотека, помогающая писать плоские файлы фиксированной длины в java?

Я хочу написать коллекцию beans/entities в плоский файл, не беспокоясь о конверсиях, заполнении, выравнивании, наполнителях и т.д.

Например, я хотел бы проанализировать 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

Если вы все еще ищете фреймворк, посмотрите BeanIO на http://www.beanio.org

Ответ 2

Вероятно, вы не столкнетесь с каркасом, который может справиться с системным форматом "Наследие". В большинстве случаев системы 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%<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 в качестве "рамки", чтобы следующий плохой сок не приходил к нему снова. Но тогда вы рассмотрите все нелепые способы, с помощью которых COBOL хранит "дату" (MMDDYY, YYMMDD, YYDDD, YYYYDDD) и численные значения (предполагаемый десятичный, явный десятичный знак, знак как отдельный отдельный или знак как ведущий плавающий символ). Тогда вы поймете, почему никто не создал хорошие рамки для этого и иногда записывает бит вашего производственного кода в SO в качестве примера...;)

Ответ 3

uniVocity-parsers долгий путь для поддержки сложных форматов фиксированной ширины, включая строки с разными полями, paddings и т.д.

Посмотрите этот пример, чтобы написать мнимые данные клиента и учетных записей. Это использует значение lookahead для определения формата, который будет использоваться при записи строки:

    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 pad client ID 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 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 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 beans, которую вы можете найти .

Раскрытие информации: я являюсь автором этой библиотеки, с открытым исходным кодом и бесплатным (Apache 2.0 License)

Ответ 4

Spring Пакет имеет FlatFileItemWriter, но это не поможет вам, если вы не используете целый Spring пакетный API.


Но кроме этого, я бы сказал, вам просто нужна библиотека, которая упрощает запись в файлы (если вы не хотите сами писать весь код ввода-вывода).

Два, которые приходят на ум, следующие:

Guava

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

Commons/IO

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

Ответ 6

Не знаю никакой работы фрейма, но вы можете просто использовать RandomAccessFile. Вы можете поместить указатель файла в любом месте файла, чтобы читать и записывать.

Ответ 7

Я только что нашел хорошую библиотеку, которую я использую:
http://sourceforge.net/apps/trac/ffpojo/wiki

Очень просто настроить XML или аннотации!

Ответ 8

Простым способом записи beans/объектов в плоский файл является использование 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]);

Вам нужно быть более конкретным о том, что вы хотите сделать с файлом.;)

Ответ 9

Попробуйте 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;
    }
}