DNA
8/1/2013 - 3:43 AM

Process a BGP table, returning the tabulated data into an array

Process a BGP table, returning the tabulated data into an array

<?php 

/**
 * Código feito em PHP, Haters Gonna Hate! :P
 * 
 * Brincadeiras a parte, foi a forma mais eficiente de resolver o
 * problema, tenho certeza que passar a lógica pra Python pra vc vai
 * ser fichinha! :P
 */

// Input de dados para exemplo
$input = 'BGP table version is 0, local router ID is 10.0.0.2
Status codes: s suppressed, d damped, h history, * valid, > best, i - internal,
              r RIB-failure, S Stale, R Removed 
Origin codes: i - IGP, e - EGP, ? - incomplete

   Network          Next Hop            Metric LocPrf Weight Path
*  172.31.1.0/24    40.0.0.4                               0 7678 7675 i 
*>                  10.0.0.1                 0             0 7675 i 
*> 172.31.2.0/24    0.0.0.0                  0         32768 i 
*> 172.31.3.0/24    10.0.0.1                               0 7675 7677 i 
*                   40.0.0.4                               0 7678 7677 i 
*  172.31.4.0/24    10.0.0.1                               0 7675 7678 i 
*>                  40.0.0.4                 0             0 7678 i 
*> 000.00.0.0/00    00.0.0.0                 0      0      0 7678 0000 i 

Total numberof prefixes 4';

$block  = explode("\n\n", $input); // Pega só o bloco principal a ser processado
$lines  = explode("\n", $block[1]); // Gera um array com uma informação por linha
$header = array_shift($lines); // Pega a primeira linha que é o header da tabela

$header = str_replace('Next Hop', 'Next_Hop', $header); // POG, admito, shame on me! XP

/**
 * Aqui começa a maracutaia. Essa regex pega a primeira letra precedida de um espaço.
 * Dessa forma, consigo ter um array com todos os "headers" da tabela
 */
$header = preg_split('/ [a-zA-Z]/', $header);
// No split, a primeira coluna só perdeu 1 caracter, então retiramos mais um aqui
$header[0] = substr($header[0], 0, -1);
$output = array();
$regex  = array();

/**
 * Aqui a gente interage com esses headers, gerando um RegEx a ser usado depois
 * O "$column+1" acontece porquê a nossa regex anterior "comeu" os dois primeiros
 * caracteres do nome da tabela, então temos de compensar aqui!
 */
foreach ($header as $column) {
	$regex[] = '(.{' . (strlen($column)+2) . '})';
}

/**
 * Aqui removemos o último elemento da array, e substituimos por (.+), pois ele 
 * deve seguir até o final da linha. O implode serve para transformar a array em
 * uma string, sendo que neste nosso exemplo ela deve ficar assim:
 * 
 * "/(.{3})(.{16})(.{19})(.{6})(.{6})(.{6})(.+)/"
 */
array_pop($regex);
$regex = '/' . implode($regex) . '(.+)/';

/**
 * Agora vamos ao que interessa. Iteramos as linhas com os dados a serem procesados,
 * aplicando a RegEx a cada linha do array. O array_shift remove o primeiro elemento
 * desse array, que é a string completa, e depois aplicamos o comando trm() a todos
 * os elementos, restando apenas os dados que não interessa. Se a coluna estiver 
 * vazia, o trim() vai deixar nosso elemento do array vazio.
 */
foreach ($lines as $line) {
	preg_match_all($regex, $line, $result, PREG_SET_ORDER);
	array_shift($result[0]); // remove o primeiro elemento, que é a string completa
	$result = array_map('trim', $result[0]);
	$output[] = $result;
}

var_dump($output);