1

我对 PHP 知之甚少,因此决定创建一个用于生成 Red Hat kickstart 文件的基于 Web 的工具将是一个很好的学习项目。除其他外,该工具将解析 CSV 文件并生成包含从中获取的数据的表格。输入文件格式如下:

主机1,10.153.196.248,255.255.255.0,10.153.196.1,00:50:56:ac:69:cb
,10.153.157.113,255.255.255.128,10.153.157.1,
,10.153.157.241,255.255.255.128,10.153.157.129,
,/家,10,,,
,交换,10,,,
,/opt,60,,,
,/数据,30,,,
,,,,,
主机2,10.153.155.124,255.255.255.128,10.153.155.1,00:50:56:ac:69:ce
,10.153.157.114,255.255.255.128,10.153.157.1,
,10.153.157.242,255.255.255.128,10.153.157.129,
,/家,10,,,
,交换,10,,,
,/opt,60,,,
,/数据,30,,,
,,,,,

每个文本部分代表一个服务器的信息。字段如下:

主机名、eth0 IP、eth0 网络掩码、eth0 网关、eth4 MAC
空,eth1 IP,eth1 网络掩码,eth1 网关,空
空白,eth2 IP,eth2 网络掩码,eth2 网关,空
null,分区名称,分区大小(GB),null,null
null,分区名称,分区大小(GB),null,null
null,分区名称,分区大小(GB),null,null
null,分区名称,分区大小(GB),null,null
空,空,空,空,空

目前,我可以解析它并生成一个表格,输入文件中的每一行都是表格中的一行。处理这个的函数:

function processFile($workFile) {

    if (file_exists($workFile)) {
        print '<table>';
        $fh = fopen("$workFile", 'rb');
        if ($fh) {
            for ($line = fgets($fh); !feof($fh); $line = fgets($fh)) {
                $line = trim($line);
                $info = explode(',', $line);
                print '<tr><td>' . $info[0] . '</td><td>' . $info[1] . '</td><td>' . $info[2] . '</td><td>' . $info[3] . '</td></tr>';
            }
        } else {
            print "Failed to open $workFile";
        }
        print '</table>';
    } else {
        print "File $workFile does not exist";
    }

}

生成:

host1 eth0 IP eth0 网络掩码 eth0 网关
          eth1 IP eth1 网络掩码 eth1 网关
          eth2 IP eth2 网络掩码 eth2 网关
          分区 1 分区 1 大小
          分区 2 分区 2 大小
          分区 3 分区 3 大小
          分区 4 分区 4 大小
host2 eth0 IP eth0 网络掩码 eth0 网关
          eth1 IP eth1 网络掩码 eth1 网关
          eth2 IP eth2 网络掩码 eth2 网关
          分区 1 分区 1 大小
          分区 2 分区 2 大小
          分区 3 分区 3 大小
          分区 4 分区 4 大小

这是一个开始。然而,并不是每台服务器都会有四个分区。有些人会有更多,有些人会少一两个。并且不提前知道这些信息会阻碍我想要做的事情,即在每个服务器的分区信息下方添加一行,并可能将每个服务器分解成自己的表。类似这样的东西:

host1 eth0 IP eth0 网络掩码 eth0 网关
          eth1 IP eth1 网络掩码 eth1 网关
          eth2 IP eth2 网络掩码 eth2 网关
          分区 1 分区 1 大小
          分区 2 分区 2 大小
          分区 3 分区 3 大小
          分区 4 分区 4 大小
          分区 5 分区 5 大小
          分区 6 分区 6 大小
          多少个磁盘?[文本域}    

host2 eth0 IP eth0 网络掩码 eth0 网关
          eth1 IP eth1 网络掩码 eth1 网关
          eth2 IP eth2 网络掩码 eth2 网关
          分区 1 分区 1 大小
          分区 2 分区 2 大小
          分区 3 分区 3 大小
          分区 4 分区 4 大小
          多少个磁盘?[文本域}

我的普遍想法是,我必须在 CSV 文件中有一个字段表明该行包含分区信息。这似乎是最简单的方法。不过,我想知道是否还有其他方法可以使用,不需要更改输入文件的格式。

我还必须弄清楚如何使用包含所有空字段的行作为节分隔符。

任何关于我如何解决这个问题的想法都将不胜感激。

4

2 回答 2

0
echo('<table>');
$i = 0;
while($data = fgetcsv($fh)) {
    echo('<tr>');
    $cellTag = 'td';
    if($i == 0) {
        $cellTag = 'th';
    }
    $i++;
    foreach($data as $cell) {
        echo('<'.$cellTag.'>'.$cell.'</'.$cellTag.'>');
    }
    echo('</tr>');
}
echo('</table>');

试试看。让我知道它是否第一次不起作用。我已经测试过了,但我手头没有 CSV 文件。

于 2012-10-30T22:26:24.237 回答
0

当您解析应该匹配特定格式的数据时,它本质上应该是正则的,允许您使用正则表达式来严格匹配您想要的输入(并丢弃您不想要的输入):

$rows = explode("\n", $input);

// @link http://stackoverflow.com/questions/106179/regular-expression-to-match-hostname-or-ip-address
$regIp = "(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])";
$regHost = "(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])";

$hosts = array();
foreach ($rows as $row) {
  $matches = null;
  if (preg_match("/(?<host>$regHost),(?<ip>$regIp),(?<netmask>$regIp),(?<gateway>$regIp),(?<mac>.*)/", $row, $matches)) {
    $host = $matches['host'];
    $hosts[$host]['name'] = $matches['host'];
    $hosts[$host]['mac'] = $matches['mac'];
    $hosts[$host]['eth'][] = array(
      'ip'      => $matches['ip'],
      'netmask' => $matches['netmask'],
      'gateway' => $matches['gateway'],
    );
  } else if (preg_match("/,(?<ip>$regIp),(?<netmask>$regIp),(?<gateway>$regIp),/", $row, $matches)) {
    $hosts[$host]['eth'][] = array(
      'ip'      => $matches['ip'],
      'netmask' => $matches['netmask'],
      'gateway' => $matches['gateway'],
    );
  } else if (preg_match("/,(?<name>.+),(?<size>\d+),,,/", $row, $matches)) {
    $hosts[$host]['partition'][] = array(
      'name' => $matches['name'],
      'size' => $matches['size'],
    );
  } else if (preg_match("/,,,,,/", $row)) {
    // we already partition output array with value of `$host` variable.
    echo "Found terminating row\n"; 
  } else {
    echo "Unrecognised data on row: $row\n";
  }
}

var_export($hosts);

由于我们处理的是 CSV 文件,因此也可以$fields = str_getcsv($row)在循环内部使用来获取包含每个字段的数组。然后需要计算数组有多少字段,有多少是空的,并验证每个字段包含什么。

直接在每个 CSV 行的字符串表示上使用正则表达式允许我们在单个表达式中严格匹配上述所有内容。(?<var>...)每个正则表达式的部分被命名为匹配项。使用 OP 提供的输入,上述脚本输出:

Found terminating row
Found terminating row
array (
  'host1' => 
  array (
    'name' => 'host1',
    'mac' => '00:50:56:ac:69:cb',
    'eth' => 
    array (
      0 => 
      array (
        'ip' => '10.153.196.248',
        'netmask' => '255.255.255.0',
        'gateway' => '10.153.196.1',
      ),
      1 => 
      array (
        'ip' => '10.153.157.113',
        'netmask' => '255.255.255.128',
        'gateway' => '10.153.157.1',
      ),
      2 => 
      array (
        'ip' => '10.153.157.241',
        'netmask' => '255.255.255.128',
        'gateway' => '10.153.157.129',
      ),
    ),
    'partition' => 
    array (
      0 => 
      array (
        'name' => '/home',
        'size' => '10',
      ),
      1 => 
      array (
        'name' => 'swap',
        'size' => '10',
      ),
      2 => 
      array (
        'name' => '/opt',
        'size' => '60',
      ),
      3 => 
      array (
        'name' => '/data',
        'size' => '30',
      ),
    ),
  ),
  'host2' => 
  array (
    'name' => 'host2',
    'mac' => '00:50:56:ac:69:ce',
    'eth' => 
    array (
      0 => 
      array (
        'ip' => '10.153.155.124',
        'netmask' => '255.255.255.128',
        'gateway' => '10.153.155.1',
      ),
      1 => 
      array (
        'ip' => '10.153.157.114',
        'netmask' => '255.255.255.128',
        'gateway' => '10.153.157.1',
      ),
      2 => 
      array (
        'ip' => '10.153.157.242',
        'netmask' => '255.255.255.128',
        'gateway' => '10.153.157.129',
      ),
    ),
    'partition' => 
    array (
      0 => 
      array (
        'name' => '/home',
        'size' => '10',
      ),
      1 => 
      array (
        'name' => 'swap',
        'size' => '10',
      ),
      2 => 
      array (
        'name' => '/opt',
        'size' => '60',
      ),
      3 => 
      array (
        'name' => '/data',
        'size' => '30',
      ),
    ),
  ),
)

由于我们现在有一组正确结构化的数据,因此以所需的任何格式(即 HTML 表格)输出数据应该是微不足道的。

于 2012-10-30T23:33:33.527 回答