Библиотека PHP для создания / обработки текстовых файлов фиксированной ширины

У нас есть веб-приложение, которое отслеживает время, зарплату и 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».

Если это текстовый файл с разделенными полями, вам нужно написать его самостоятельно. Наверное, это не большая проблема. Хорошая организация, сэкономит много времени.

  1. Ваша потребность в универсальном способе определения структур. Т.е. xml.
  2. Ваша потребность что-то генерировать … специально я предпочитаю Smarty templating для этого.

Итак, это одно:

  <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} 

Это просто идея.

Но представьте себе:

  1. Вам нужен xml
  2. Вам нужен шаблон

т.е. 2 определения для абстракции любой текстовой структуры

Возможно, функции dbase – это то, что вы хотите использовать. Они не OOP, но, вероятно, было бы не слишком сложно построить класс, который будет действовать на функции, предоставляемые в наборе dbase.

Взгляните на приведенную ниже ссылку для получения подробной информации о функциях dbase, доступных в PHP. Если вы просто хотите создать файл для импорта в другую систему, эти функции должны работать на вас. Просто убедитесь, что вы обратите внимание на предупреждения. Некоторые из основных предупреждений:

  • Нет поддержки индексов или полей заметок.
  • Нет поддержки для блокировки.
  • Два параллельных процесса веб-сервера, изменяющих один и тот же файл dBase, скорее всего, испортят вашу базу данных.

http://php.net/manual/en/book.dbase.php

Мне жаль, что я не могу помочь вам с прямым классом, я видел кое-что, что делает это, но я не могу вспомнить, где это так жаль, но это должно быть просто для кодера,

Итак, как я видел эту работу в примере:

php читает данные

Затем php использует флаг (EG a $ _GET ['type']), чтобы знать, как выводить данные EG Printer, HTML, Excel

Таким образом, вы создаете файлы шаблонов для каждой версии, а затем в зависимости от загружаемого флага и использования определенного шаблона, так как для Fixed Width это HTML-вещь, а не PHP, поэтому это должно быть сделано в шаблонах CSS

Затем из этого вы можете выводить свои данные, как когда-либо любой пользователь этого требует,

Smarty Templates неплохо подходит для этого, а затем заголовок php для отправки типа контента, когда это необходимо.