giftcards/fixed-width

用于处理固定宽度文件及其相关规范的库

1.5.2 2019-11-21 18:37 UTC

README

目的

固定宽度库的目的是使处理固定宽度文件变得更容易。包括诸如轻松访问行和字段索引以及使用文件规范来允许通过字段名而不是索引和范围来设置加载值等功能。

用法

基本

对于真正的简单固定宽度文件工作,您可以创建一个新的File实例,并向其中添加行,设置行索引并添加行

<?php

use Giftcards\FixedWidth\File;
use Giftcards\FixedWidth\Line;

$file = new File(
    'name' /* the name of the file required */,
    20 /* width required */,
    array() /* initial lines if you have any defaults to an empty array */,
    "\r\n" /* line separator defaults to \r\n */
);

$line1 = $file->newLine(); //this instantiates a new line and adds it to the file
                        //then returns it to be edited
$line1[2] = 'w'; //the line class follows ArrayAccess so can be used as an array to set chars
$line1['2:3'] = 'w'; //the index can also be a string with 2 numbers separated by
                 //a colon to denote a range this is the equivalent range to the above
                 //index it set the value from and including index a until and excluding
                 //index b
                 //you can also get a value using an index or range.
                 //you can also pass an instance of Slice which is what it is converted
                 //to internally
var_dump('w' == $line[new Slice(2, 3)]);//this will be true
$line1['3:5'] = 'er';//so index 3 and 4 will be set with this value.
$line2 = new Line($file->getWidth()); //you can also create a line
$file[] = $line2; //and add it to the file. it follows ArrayAccess as well
               //you can also use addLine for the same effect
$file[1] = $line2; //this has the same effect and setLine(1, $line2) can be used as well
unset($file[0]); //this removes the line at that index and reindex's the lines
              //so $file[0] === $line2
$line2['0:5'] = 'hello';
$line2['6:11'] = 'world'; //$line2 now says 'hello world         '
echo $line2; //the Line class has a __toString method so this will print out the contents
$file[] = $line1;
echo $file; //this will echo out the whole file line by line separated by the line

最后echo的输出将类似于以下内容

hello world
  wer

文件工厂

您无需自己实例化文件类,可以使用文件工厂来完成。基本文件工厂为您提供了两种方法:

  • create - 用于创建文件的便利方法
  • createFromFile - 这可以传递一个SplFileInfo实例以创建文件。

规范

通常在处理固定宽度文件时,您不想记住不同字段的位置。为了实现这一点,固定宽度库提供了一种定义记录规范的方法。记录规范是一个定义文件中特定类型行的规范。通常,文件将具有几种不同类型的记录,规范系统允许您定义这些规范,每个规范都有自己的记录规范以及其中的字段。您可以将记录规范作为PHP数组或作为yaml文件,此时定义新的规范加载器很容易,它需要遵循Giftcards\FixedWidth\Spec\Loader\SpecLoaderInterface。有一个抽象类,它使定义从文件系统加载的加载器变得容易AbstractSpecFileLoader

一个示例规范可以在Tests/Fixtures/spec.yml中查看。

规范选项

在文件级别,您可以定义两个直接选项。

  • width - 文件和由该规范生成的所有行的宽度
  • line_separator - 应用作行分隔符的字符串。默认为\r\n

之后,主要定义集中在具有字段的记录上。字段具有以下选项。

  • default - 如果没有为该字段提供值,则将提供此默认值
  • slice - 这是行中设置此值应使用的范围/索引
  • format_specifier - 这是用于格式化值的实际规范符。通常这和在sprint中使用的相同字符,例如s或.2f
  • padding_char - 应用于填充值和完整字段长度之间缺失空间的字符。默认为空格
  • padding_direction - 应放置填充的方向。有效选项为left或right
  • type - 这是字段具有的类型。这将在下一部分中解释

字段类型

非常常见的情况是,许多字段在上述选项中有很多共同点。这就是为什么在定义规范定义时,可以使用类型来定义共享属性,然后设置字段的类型,它将继承为该类型设置的选项。请参阅上述示例规范以获取更多详细信息。

构建文件

要使用规范构建文件,您需要定义一个规范加载器。之后,最好的方法是实例化上面提到的基工厂类的实例 Giftcards\FixedWidth\Spec\FileFactory,它继承自基工厂类并添加了一些处理规范的方法。它需要规范加载器来执行其功能。

<?php

use Giftcards\FixedWidth\Spec\FileFactory;
use Giftcards\FixedWidth\Spec\Loader\YamlSpecLoader;
use Symfony\Component\Config\FileLocator;


$factory = new FileFactory(new YamlSpecLoader(new FileLocator(__DIR__.'/Tests/Fixtures/')));
//instantiate a builder
$builder = $factory->createBuilder('fileName', 'spec1');
$builder
    ->addRecord('record1', array(
        //field1 isnt required since it has a default
        'field2' => 'go'
    ))
    ->addRecord('record1', array(
        'field1' => 2.3,
        'field2' => 'hi'
    ))
    ->addRecord('record2', array(
        'field3' => 12345
    ))
;
echo $builder->getFile();

这将输出:

                                  23.34 go
                                  x2.30 hi
                                                   12345

读取文件

您还可以使用规范来读取已构建的文件。比如说,您将上面的输出保存到文件中,您可以加载它并读取字段或读取整行到数组中。

<?php

use Giftcards\FixedWidth\Spec\FileFactory;
use Giftcards\FixedWidth\Spec\Loader\YamlSpecLoader;
use Symfony\Component\Config\FileLocator;

$file = new \SplFileInfo('path');

$factory = new FileFactory(new YamlSpecLoader(new FileLocator(__DIR__.'/Tests/Fixtures/')));


$reader = $factory->createReader($factory->createFromFile($file), 'spec1');

$field1 = $reader->parseField(
    0 /* index of the line you want to read */,
    'field1' /* name of the field you want to read */,
    'record1' /* record spec name to use */
);

var_dump($field1); //output will be double(23.34)

$record = $reader->parseLine(0, 'record1');
var_dump($record); //output will be
/*
array(2) {
  'field1' =>
  double(23.34)
  'field2' =>
  string(2) "go"
}
*/

您还可以添加记录识别器,这样就不必传递记录规范名称。这将在高级部分中讨论。

行读取器

如果您只想获取特定行并读取其数据,可以调用 $reader->getLineReader($index [, $specName]); 如果没有传递规范名称,它将尝试识别规范。

您也可以遍历文件读取器,这将使其返回一个迭代器,该迭代器按顺序返回每行的行读取器。

行读取器遵循 ArrayAccess,因此您可以使用记录规范中的字段名称来检索值。您还可以使用 getFieldgetFields 方法分别获取特定字段或所有字段。在所有情况下,所有字段都由文件读取器的值格式化器格式化。

高级

记录识别器

如果您的所有/部分记录可以根据行数据推断出来,您可以使用记录规范识别器,这样您就不必传递记录规范名称来解析字段和解析行。它们必须遵循 RecordSpecRecognizerInterface。要为文件规范启用记录识别,请调用文件工厂的 addRecordSpecRecognizer 方法,将您希望其工作的文件规范名称作为第一个参数,将识别器作为第二个参数。

$factory->addRecordSpecRecognizer('spec1', $recognizer);

//now when you read a file using spec1 the recognizer will be used if no record
//spec name is passed as the last arg to parseField and parseLine

库中有一个识别器实现 SpecFieldRecognizer,您可以为它提供一个字段名称来识别记录。它将比较该字段规范默认值与行中的值,如果它们相等,则返回该记录规范名称。字段名称默认为 $id,为了使其工作,请确保您想要自动识别的记录具有它检查的字段。

值格式化器

值格式化器是使用字段规范中的信息来格式化值并添加填充等类的实际使用信息。它们还被赋予了在读取值时格式化值的机会。默认实现是 SprintfValueFormatter,它将使用 sprintf 格式化值以添加到文件行中,并在输出时尝试推断 PHP 类型。所有格式化器都必须遵循 ValueFormatterInterface

要更改您正在使用的值格式化器,请将其作为第二个参数传递给 FileFactory 的构造函数。

use Giftcards\FixedWidth\Spec\FileFactory;

$factory = new FileFactory($specLoader, $customValueFormatter);

###使用大文件时降低内存使用### 在您不希望在处理时将整个文件加载到内存中的情况下,您可以使用 Giftcards\FixedWidth\FileSystemFile 类。当可能时,它将直接与提供的 SplFileObject 一起工作,而不是在内存中保存数据。这个类可以与规范、文件构建器和文件读取器类一起使用,就像 Giftcards\FixedWidth\File 类一样。

示例

use Giftcards\FixedWidth\FileSystemFile;

$fileObject = new \SplFileObject('filename.txt', 'w+');
$file = new FileSystemFile($fileObject, 20, "\r\n");

$builder = $fileFactory->createBuilder($file, 'spec_name');
$reader = $fileFactory->createReader($file, 'spec_name');