如何在PHP中解析常规文本文件

时间:2014-05-22 11:26:53

标签: php parsing

据我所知/可以找到,有两种方法可以做到这一点。

  1. 状态机(按照 Best way to parse a dynamic text list in PHP
  2. 在换行符explode("\n", $data)
  3. 上进行爆炸

    鉴于您有基于记录(每个记录一行)的文本文件,其中包含特定规则。字符1到2为您提供yy格式的年份,字符3到4为您提供mm格式的月份。字符70到72为您提供一些x参数。

    对于CSV,我们有str_getcsv(),但我认为没有任何文本文件可用。

    我个人的偏好是简单的换行选项。想法?

    文本文件如下所示:

    14 5 12466 3202313 710101010 31  7  9  5  3  4  4  4  4  50.21
    14 5 22466 41317 7 7 7 3 3 7 19  5  6  3  3  3  2  2  3  30.10
    14 5 32466 5 7 7 3 7 3172727 29  3  3  2  3  2  6 12 12  50.21
    14 5 42466 62337332330231713 60  9 22 18  9 15  9  6  5 120.73
    14 5 52466 71327202020202320 49  5 12  7  7  7  7  9  7  80.42
    14 5 62466 8 3 7 7 7 3 3 3 3 11  2  3  3  3  2  2  2  2  20.00
    14 5 72466 9 0 0 0 310 3 720 13  0  0  0  2  4  2  3  7  20.00
    14 5 82466102333334320333037 76  9 18 18 32  7 18 15 22 170.94
    14 5 924661130172017 7 3 010 31 15  6  7  6  3  2  0  4  50.21
    14 51024661220301310 7 32327 40  7 15  5  4  3  2  9 12  70.42
    14 5112466134330172713172027 58 32 15  6 12  5  6  7 12 120.73
    14 5122466142320202320101717 45  9  7  7  9  7  4  6  6  70.31
    14 51324661510131310 7 7 720 26  4  5  5  4  3  3  3  7  40.10
    14 5142466161710 3 72317 717 30  6  4  2  3  9  6  3  6  50.21
    14 51524661710101313 7101313 27  4  4  5  5  3  4  5  5  40.21
    14 5162466181713101020201710 35  6  5  4  4  7  7  6  4  50.21
    14 51724661913 7 3 7 3 3 3 3 13  5  3  2  3  2  2  2  2  30.00
    14 518246620171310 717201013 32  6  5  4  3  6  7  4  5  50.21
    14 5192466211013101013131020 30  4  5  4  4  5  5  4  7  50.21
    14 520246622 7 7101010 7 3 3 17  3  3  4  4  4  3  2  2  30.10
    14 521246623 7 3 3 3 0 3 3 7  9  3  2  2  2  0  2  2  3  20.00
    14 5222466241010109999999999     4  4  4      
    

    每条记录/行的一些规则:

     1- 2 i2 yy, last two digits of year 
     3- 4 i2 mm, month (1-12) 
     5- 6 i2 dd, day of month (1-31) 
    
     7-10 i4 Bartels solar rotation number - a sequence of 27-day 
     intervals counted continuously from February 8, 1832 
    11-12 i2 Number of day within the Bartels 27-day cycle 
    
    13-28 8i2 3-hourly Kp indices (0-3, 3-6, 6-9, 9-12, 12-15, 
     15-18, 18-21, 21-24 UT) 
    29-31 i3 Daily Kp sum, expressed to the nearest third of a unit 
     (supplied only for tradition, use Ap scientific 
     purposes!) 
    
    32-55 8i3 3-hourly ap indices (0-3, 3-6, 6-9, 9-12, 12-15, 
     15-18, 18-21, 21-24 UT) 
    56-58 i3 Ap equivalent daily amplitude - 
    

1 个答案:

答案 0 :(得分:0)

由于这种文本格式似乎没有任何分隔符,除了换行符之外,我会逐行读取它并使用substr从中获取字段。正则表达式或CSV读取器不会起作用,因为没有分隔符。好吧,正则表达式在技术上是可行的,因为您还可以指定通配符和特定数量的重复,但最终它不会增加太多值。

所以只需在$line中读一行,然后通过获取一些字符来解析它:

$year = trim(substr($line, 0, 2));
$month = trim(substr($line, 2, 2));
// etc.

这可能看起来很麻烦,但你可以把它包装成一个整洁的功能,如:

function getIntField($line, $start, $length);
{
  return (int)trim(substr($line, $start, $length));
}

如果更进一步,可以在数组中进行字段定义以帮助您阅读文件:

$fields = array(
  'year' => array(0, 2, 'int'),
  'month' => array(2, 2, 'int'),
  'day' => array(4, 2, 'int'),
  'hour' => array(6, 2, 'int'),
  'amount' => array(8, 5, 'float')
);

然后,您可以阅读如下字段:

function getField($line, $field)
{
  $fields = .... field def goes here (or use global or class variable).

  // Get the proper field definition for the field. (You can add some 
  // checking to see whether it exists.
  $def = fields[$field];

  // Read the specified part (start, length are in the field definition).
  $value = trim(substr($line, $def[0], $def[1]));

  // In my example, the third parameter of the definition is the type.
  switch ($def[2])
  {
    case 'int': return (int)$value;
    case 'float': return (float)$value;
  }

  // Not returned as a specific type? Then, just return the string.
  return $value;
}

如果在类中包含这样的函数,那么应该很容易拥有一个处理文件访问的类,并在内部跟踪当前行。然后你可以写一段这样的代码:

$amount = 0;
$file = new FixedLengthRecordFile($filePath);

while ($file->readLine())
{
  $amount += $file->getField('amount');
}

echo 'Total amount in file: ' . $amount;

如果您愿意,您甚至可以使用魔术__get方法来包装吸气剂。这样,你可以写:

$amount += $file->amount;

而不是

$amount += $file->getField('amount');

无论如何,如果我找不到已经为我做的开箱即用的课程,我可能会这样做。但我认为没有。