【问题标题】:How to parse a regular text file in PHP如何在 PHP 中解析常规文本文件
【发布时间】:2014-05-22 11:26:53
【问题描述】:

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

  1. 状态机(根据
    Best way to parse a dynamic text list in PHP)
  2. 在换行符上分解它explode("\n", $data)

假设您有一个具有特定规则的基于记录(每条记录一行)的文本文件。字符 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 - 

【问题讨论】:

  • 如果你的数据和你建议的一样有规律,那么正则表达式会很好地分解它。编辑:现在您已经添加了一些示例数据,我将使用正则表达式。
  • 不需要正则表达式。只需在太空中爆炸,您就会拥有所需的所有领域
  • 您仍然可以使用 fgetcsv() 之类的函数,只需将分隔符更改为单个空格或制表符 '\t'
  • 数据不是我提供的,按原样给出。我不完全确定,但我不认为空格分隔是一种选择。这就是为什么他们根据性格制定规则的原因。我将添加一些规则以使其清楚。否决票和关闭,你能评论为什么吗?

标签: php parsing


【解决方案1】:

由于这种文本格式似乎没有任何分隔符,除了换行符,我会逐行阅读并使用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 方法来包装getter。这样,你可以写:

$amount += $file->amount;

而不是

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

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

【讨论】:

  • 是的,我已经开始使用substr()。就像你说的那样,使用正则表达式是可能的,但问题是它是否值得。目前我还没有对类型做任何事情(文档确实谈到了它)所以我会让它比你建议的更简单,但我喜欢这种方法。
猜你喜欢
  • 1970-01-01
  • 2011-08-29
  • 2019-01-11
  • 2013-03-22
  • 1970-01-01
  • 1970-01-01
  • 2010-12-21
  • 1970-01-01
  • 2012-07-09
相关资源
最近更新 更多