У нас есть веб-приложение, которое отслеживает время, зарплату и HR. В результате мы должны написать много файлов данных фиксированной ширины для экспорта в другие системы (государственные налоговые документы, файлы ACH и т. Д.). Кто-нибудь знает хорошую библиотеку для этого, где вы можете определить типы / структуры записи, а затем действовать на них в парадигме ООП?
Идея была бы классом, который вы передадите спецификациям, а затем работать с экземпляром указанной спецификации. IE:
$icesa_file = new FixedWidthFile(); $icesa_file->setSpecification('icesa.xml'); $icesa_file->addEmployer( $some_data_structure );
Где icesa.xml – это файл, содержащий спецификацию, хотя вы можете просто использовать вызовы ООП, чтобы определить его самостоятельно:
$specification = new FixedWidthFileSpecification('ICESA'); $specification->addRecordType( $record_type_name = 'Employer', $record_fields = array( array('Field Name', Width, Vailditation Type, options) ) );
EDIT: Я не ищу совета о том, как написать такую библиотеку – я просто хотел узнать, существует ли она уже. Спасибо!!
Я не знаю о библиотеке, которая делает именно то, что вы хотите, но должно быть довольно просто перевернуть свои собственные классы, которые справятся с этим. Предполагая, что вы в основном заинтересованы в написании данных в этих форматах, я бы использовал следующий подход:
(1) Напишите легкий класс форматирования для строк фиксированной ширины. Он должен поддерживать определенные пользователем типы записей и должен быть гибким в отношении разрешенных форматов
(2) Создайте этот класс для каждого используемого формата файла и добавьте необходимые типы записей
(3) Используйте этот форматтер для форматирования данных
Как вы предположили, вы можете определить типы записей в XML и загрузить этот XML-файл на этапе (2). Я не знаю, насколько вы опытны с XML, но по моему опыту форматы XML часто вызывают много головных болей (возможно, из-за моей собственной некомпетентности в отношении XML). Если вы собираетесь использовать эти классы только в своей программе PHP, вам нечего извлечь из определения вашего формата в XML. Использование XML – хороший вариант, если вам нужно будет использовать определения формата файла во многих других приложениях.
Чтобы проиллюстрировать мои идеи, вот как я думаю, вы бы использовали этот предложенный класс форматирования:
<?php include 'FixedWidthFormatter.php' // contains the FixedWidthFormatter class include 'icesa-format-declaration.php' // contains $icesaFormatter $file = fopen("icesafile.txt", "w"); fputs ($file, $icesaFormatter->formatRecord( 'A-RECORD', array( 'year' => 2011, 'tein' => '12-3456789-P', 'tname'=> 'Willie Nelson' ))); // output: A2011123456789UTAX Willie Nelson // etc... fclose ($file); ?>
Файл icesa-format-declaration.php
может содержать объявление формата так или иначе:
<?php $icesaFormatter = new FixedWidthFormatter(); $icesaFormatter->addRecordType( 'A-RECORD', array( // the first field is the record identifier // for A records, this is simply the character A 'record-identifier' => array( 'value' => 'A', // constant string 'length' => 1 // not strictly necessary // used for error checking ), // the year is a 4 digit field // it can simply be formatted printf style // sourceField defines which key from the input array is used 'year' => array( 'format' => '% -4d', // 4 characters, left justified, space padded 'length' => 4, 'sourceField' => 'year' ), // the EIN is a more complicated field // we must strip hyphens and suffixes, so we define // a closure that performs this formatting 'transmitter-ein' => array( 'formatter'=> function($EIN){ $cleanedEIN = preg_replace('/\D+/','',$EIN); // remove anything that's not a digit return sprintf('% -9d', $cleanedEIN); // left justified and padded with blanks }, 'length' => 9, 'sourceField' => 'tein' ), 'tax-entity-code' => array( 'value' => 'UTAX', // constant string 'length' => 4 ), 'blanks' => array( 'value' => ' ', // constant string 'length' => 5 ), 'transmitter-name' => array( 'format' => '% -50s', // 50 characters, left justified, space padded 'length' => 50, 'sourceField' => 'tname' ), // etc. etc. )); ?>
Тогда вам нужен FixedWidthFormatter
класс FixedWidthFormatter
, который может выглядеть так:
<?php class FixedWidthFormatter { var $recordTypes = array(); function addRecordType( $recordTypeName, $recordTypeDeclaration ){ // perform some checking to make sure that $recordTypeDeclaration is valid $this->recordTypes[$recordTypeName] = $recordTypeDeclaration; } function formatRecord( $type, $data ) { if (!array_key_exists($type, $this->recordTypes)) { trigger_error("Undefinded record type: '$type'"); return ""; } $output = ''; $typeDeclaration = $this->recordTypes[$type]; foreach($typeDeclaration as $fieldName => $fieldDeclaration) { // there are three possible field variants: // - constant fields // - fields formatted with printf // - fields formatted with a custom function/closure if (array_key_exists('value',$fieldDeclaration)) { $value = $fieldDeclaration['value']; } else if (array_key_exists('format',$fieldDeclaration)) { $value = sprintf($fieldDeclaration['format'], $data[$fieldDeclaration['sourceField']]); } else if (array_key_exists('formatter',$fieldDeclaration)) { $value = $fieldDeclaration['formatter']($data[$fieldDeclaration['sourceField']]); } else { trigger_error("Invalid field declaration for field '$fieldName' record type '$type'"); return ''; } // check if the formatted value has the right length if (strlen($value)!=$fieldDeclaration['length']) { trigger_error("The formatted value '$value' for field '$fieldName' record type '$type' is not of correct length ({$fieldDeclaration['length']})."); return ''; } $output .= $value; } return $output . "\n"; } } ?>
Если вам нужна поддержка чтения, класс Formatter может быть расширен и для чтения, но это может выходить за рамки этого ответа.
Я уже давно использовал этот класс для аналогичного использования. Это файл php-классов, но он очень хорошо оценен и много проверен и проверен. Это не ново (2003), но несмотря на то, что он по-прежнему отлично работает + имеет очень приличный и чистый API, который выглядит несколько как пример, который вы опубликовали, добавив много других полезных добавлений.
Если вы можете игнорировать немецкое использование в примерах, а возрастной фактор – это очень приличный фрагмент кода.
Posted from the example: //CSV-Datei mit Festlängen-Werten echo "<p>Import aus der Datei fixed.csv</p>"; $csv_import2 = new CSVFixImport; $csv_import2->setFile("fixed.csv"); $csv_import2->addCSVField("Satzart", 2); $csv_import2->addCSVField("Typ", 1); $csv_import2->addCSVField("Gewichtsklasse", 1); $csv_import2->addCSVField("Marke", 4); $csv_import2->addCSVField("interne Nummer", 4); $csv_import2->addFilter("Satzart", "==", "020"); $csv_import2->parseCSV(); if($csv_import->isOK()) { echo "Anzahl der Datensätze: <b>" . $csv_import2->CSVNumRows() . "</b><br>"; echo "Anzahl der Felder: <b>" . $csv_import2->CSVNumFields() . "</b><br>"; echo "Name des 1.Feldes: <b>" . $csv_import2->CSVFieldName(0) . "</b><br>"; $csv_import2->dumpResult(); }
Мои 2 цента, удачи!
Я не знаю никакой библиотеки PHP, которая специально обрабатывает записи фиксированной ширины. Но есть несколько хороших библиотек для фильтрации и проверки строки данных, если вы можете выполнить задачу разбивки каждой строки файла самостоятельно.
Взгляните на компоненты Zend_Filter и Zend_Validate из Zend Framework. Я думаю, что оба компонента довольно автономны и требуют только Zend_Loader. Если вы хотите, вы можете вытащить только эти три компонента из Zend Framework и удалить остальную часть.
Zend_Filter_Input действует как набор фильтров и валидаторов. Вы определяете набор фильтров и валидаторов для каждого поля записи данных, которое вы можете использовать для обработки каждой записи набора данных. Есть много полезных фильтров и валидаторов, которые уже определены, а интерфейс для написания собственного достаточно прост. Я предлагаю фильтр StringTrim для удаления дополняющих символов.
Чтобы разбить каждую строку на поля, я бы расширил класс Zend_Filter_Input и добавил метод setDataFromFixedWidth (), например:
class My_Filter_Input extends Zend_Filter_Input { public function setDataFromFixedWidth($record, array $recordRules) { if (array_key_exists('regex', $recordRules) { $recordRules = array($recordRules); } foreach ($recordRules as $rule) { $matches = array(); if (preg_match($rule['regex'], $record, $matches)) { $data = array_combine($rule['fields'], $matches); return $this->setData($data); } } return $this->setData(array()); } }
И определите различные типы записей с помощью простых регулярных выражений и совпадающих имен полей. ICESA может выглядеть примерно так:
$recordRules = array( array( 'regex' => '/^(A)(.{4})(.{9})(.{4})/', // This is only the first four fields, obviously 'fields' => array('recordId', 'year', 'federalEin', 'taxingEntity',), ), array( 'regex' => '/^(B)(.{4})(.{9})(.{8})/', 'fields' => array('recordId', 'year', 'federalEin', 'computer',), ), array( 'regex' => '/^(E)(.{4})(.{9})(.{9})/', 'fields' => array('recordId', 'paymentYear', 'federalEin', 'blank1',), ), array( 'regex' => '/^(S)(.{9})(.{20})(.{12})/', 'fields' => array('recordId', 'ssn', 'lastName', 'firstName',), ), array( 'regex' => '/^(T)(.{7})(.{4})(.{14})/', 'fields' => array('recordId', 'totalEmployees', 'taxingEntity', 'stateQtrTotal'), ), array( 'regex' => '/^(F)(.{10})(.{10})(.{4})/', 'fields' => array('recordId', 'totalEmployees', 'totalEmployers', 'taxingEntity',), ), );
Затем вы можете читать файл данных по строкам и подавать его в фильтр ввода:
$input = My_Filter_Input($inputFilterRules, $inputValidatorRules); foreach (file($filename) as $line) { $input->setDataFromFixedWidth($line, $recordRules); if ($input->isValid()) { // do something useful } else { // scream and shout } }
Чтобы форматировать данные для записи в файл, вы, вероятно, захотите написать собственный фильтр StringPad, который обертывает внутреннюю функцию str_pad. Затем для каждой записи в вашем наборе данных:
$output = My_Filter_Input($outputFilterRules); foreach ($dataset as $record) { $output->setData($record); $line = implode('', $output->getEscaped()) . "\n"; fwrite($outputFile, $line); }
Надеюсь это поможет!
Я думаю, вам нужно немного больше информации, чем вы указали: какие структуры данных вы бы хотели использовать для своих записей и определения столбцов? Похоже, что это довольно специализированный класс, который потребует настройки для вашего конкретного варианта использования.
У меня есть класс PHP, который я написал, который в основном делает то, что вы ищете, но полагаясь на другие классы, которые мы используем в нашей системе. Если вы можете предоставить типы структур данных, которые вы хотите использовать, я могу проверить, будет ли он работать для вас и отправить его.
Примечание. Я публиковал этот ответ раньше с общедоступного компьютера, и я не мог заставить его казаться от меня (он был показан как случайный пользователь). Если вы видите это, пожалуйста, проигнорируйте ответ от «john».
Если это текстовый файл с разделенными полями, вам нужно написать его самостоятельно. Наверное, это не большая проблема. Хорошая организация, сэкономит много времени.
Итак, это одно:
<group> <entry>123</entry> <entry>123</entry> <entry>123</entry> </group>
Легко интерпретироваться в тесте с помощью этого шаблона:
{section name=x1 loop=level1_arr} {--output root's--} {section name=x2 loop=level1_arr[x1].level2_arr} {--output entry's--} {/section} {/section}
Это просто идея.
Но представьте себе:
т.е. 2 определения для абстракции любой текстовой структуры
Возможно, функции dbase – это то, что вы хотите использовать. Они не OOP, но, вероятно, было бы не слишком сложно построить класс, который будет действовать на функции, предоставляемые в наборе dbase.
Взгляните на приведенную ниже ссылку для получения подробной информации о функциях dbase, доступных в PHP. Если вы просто хотите создать файл для импорта в другую систему, эти функции должны работать на вас. Просто убедитесь, что вы обратите внимание на предупреждения. Некоторые из основных предупреждений:
Мне жаль, что я не могу помочь вам с прямым классом, я видел кое-что, что делает это, но я не могу вспомнить, где это так жаль, но это должно быть просто для кодера,
Итак, как я видел эту работу в примере:
php читает данные
Затем php использует флаг (EG a $ _GET ['type']), чтобы знать, как выводить данные EG Printer, HTML, Excel
Таким образом, вы создаете файлы шаблонов для каждой версии, а затем в зависимости от загружаемого флага и использования определенного шаблона, так как для Fixed Width это HTML-вещь, а не PHP, поэтому это должно быть сделано в шаблонах CSS
Затем из этого вы можете выводить свои данные, как когда-либо любой пользователь этого требует,
Smarty Templates неплохо подходит для этого, а затем заголовок php для отправки типа контента, когда это необходимо.