Ответ 1
Если вы все еще ищете фреймворк, посмотрите BeanIO на http://www.beanio.org
Есть ли какой-либо фреймворк/библиотека, помогающая писать плоские файлы фиксированной длины в 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
...
Если вы все еще ищете фреймворк, посмотрите BeanIO на http://www.beanio.org
Вероятно, вы не столкнетесь с каркасом, который может справиться с системным форматом "Наследие". В большинстве случаев системы 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 в качестве примера...;)
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)
Spring Пакет имеет FlatFileItemWriter
, но это не поможет вам, если вы не используете целый Spring пакетный API.
Но кроме этого, я бы сказал, вам просто нужна библиотека, которая упрощает запись в файлы (если вы не хотите сами писать весь код ввода-вывода).
Два, которые приходят на ум, следующие:
Files.write(stringData, file, Charsets.UTF_8);
FileUtils.writeStringToFile(file, stringData, "UTF-8");
Библиотека Fixedformat4j - довольно аккуратный инструмент, чтобы сделать именно это: http://fixedformat4j.ancientprogramming.com/
Не знаю никакой работы фрейма, но вы можете просто использовать RandomAccessFile. Вы можете поместить указатель файла в любом месте файла, чтобы читать и записывать.
Я только что нашел хорошую библиотеку, которую я использую:
http://sourceforge.net/apps/trac/ffpojo/wiki
Очень просто настроить XML или аннотации!
Простым способом записи 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]);
Вам нужно быть более конкретным о том, что вы хотите сделать с файлом.;)
Попробуйте 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;
}
}