3

I have a text file (a zone file actually) which looks like this:

...
;
; base config
; -----------------------------------------------------------------
@       14400   IN      A       1.2.3.4
@       14400   IN      AAAA    1:2:3::4
;
; mail config
; -----------------------------------------------------------------
mail    14400   IN      A       1.2.3.4
mail    14400   IN      AAAA    1:2:3::4
@       14400   IN      MX 10   mail.example.com.
;
; www config
; -----------------------------------------------------------------
www     14400   IN      CNAME   example.com.
...

I would like to parse each not commented out lines into an array, block by block. So it should look like this:

$array = array(
    "base" => array(
        "0" => "@       14400   IN      A       1.2.3.4",
        "1" => "@       14400   IN      AAAA    1:2:3::4"
    ),
    "mail" => array (
        "0" => "mail    14400   IN      A       1.2.3.4",
        "1" => "mail    14400   IN      AAAA    1:2:3::4",
        "1" => "@       14400   IN      MX 10   mail.example.com."
    ),
    "www" => array(
        "0" => "www     14400   IN      CNAME   example.com."
    )
);

In this case, the comments are ";" so it does not need to parse at all. The commented out sections are fix, like the "base config" and "mail config" and the "www config" does not changing, it looks every time as in my example. But the records (not ommented out with ;) in each block can be changing, so it is possible to have only 1 record, or 5, 10, or any. I did a try to put the text file into an array an process it with a foreach loop, then search the corresponding lines with preg_match() but it doesn not solved, because I don't know how many records (lines) comes until the next section of the file. Her is my try:

<?php
// Get lines
$lines = file('file.txt');

// Loop through our array
foreach ($lines as $line_num => $line) {
    if (preg_match("/\bbase config\b/i", $line)) {
        $line++
        // I don't know how to continue and find the next section
    }
}
?>

If you could help me to process one block (base, mail or www) it will help me much, based on that, I can solve the others.

2
  • 1
    Post the code you have tried Commented Apr 12, 2018 at 11:11
  • 1
    Edited my question and put my try. I think it will not help too much. Commented Apr 12, 2018 at 11:20

3 Answers 3

3

Another solution

<?php
$filepath = __DIR__ . '/file.txt';

$validSection = false;
$startingSectionPattern = '/^; [a-z]+ config$/i';
$startingSections = ['base' => '; base config', 'mail' => '; mail config', 'www' => '; www config'];
$res = [];

foreach (file($filepath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
    if ($line[0] == ';' && preg_match($startingSectionPattern, $line)) {
        $validSection = false;
        $key = array_search($line, $startingSections);
        if ($key !== false) {
            $validSection = true;
        }
    }

    if ($validSection && $line[0] != ';') {
        $res[$key][] = $line;
    }
}

var_dump($res);
Sign up to request clarification or add additional context in comments.

3 Comments

It looks good at all, but it does not work. It gaves an empty array as result. I think the preg_match does not work because it don't enter into the first if clause.
Hello @ankabout, You are right. It is my fault, because I copyed my example as short as possible, therefore I wrote "base config" but in my original example it is "base configuration for zone example.com" Hence your regexp does not match. :) But your answer is simple and great, so I will accept it as the best answer. Could you please help me with regexps and modify it to [a-z]+ config <- and anything else after that? I'm very bad with the regexps at all.
@Darwick Of course, you only need to remove the trailing $ at the end, (it says the string must finish at 'config') since you don't want that remove the $.
2

Using a foreach(), you could check if the first line begins by ; and check if the content of the line is a configuration name. You could use trim() and list() to check the content of the line:

$lines = file('file.txt');

$config = '';
$array = []; // outputs array:
foreach ($lines as $line) { 
    if (strpos($line, ';') === 0) { // if starts with ';'
        $line = trim($line, '-; '); // remove unwanted characters
        if (!$line) continue; // if empty, it's an unwanted line
        list($config) = explode(' ', $line, 2); // get the config name
        continue;
    }
    $array[$config][] = $line; // store line in config array
}
print_r($array);

Outputs:

Array
(
    [base] => Array
        (
            [0] => @       14400   IN      A       1.2.3.4
            [1] => @       14400   IN      AAAA    1:2:3::4
        )
    [mail] => Array
        (
            [0] => mail    14400   IN      A       1.2.3.4
            [1] => mail    14400   IN      AAAA    1:2:3::4
            [2] => @       14400   IN      MX 10   mail.example.com.
        )
    [www] => Array
        (
            [0] => www     14400   IN      CNAME   example.com.
        )

)

2 Comments

Almost good, but the file should start with another recrods and commented out sections which will fail this processing as I tested. Should you please modify it to begin the cehck from "base config"? I did a try to modify it myself, but I didn't figured out how.
@Darwick I've edited my answer to rollback to the first version for future readers, to match to the expected output of your question. The second version after your comment is here.
1

Notes:

I've updated this answer just to make it fully functional. Now it processes all config options.

This example uses "table-based" approach and reads file line by line. Newline char(chars) are included in $output.

zones.txt

;
; base config
; -----------------------------------------------------------------
@       14400   IN      A       1.2.3.4
@       14400   IN      AAAA    1:2:3::4
;
; mail config
; -----------------------------------------------------------------
mail    14400   IN      A       1.2.3.4
mail    14400   IN      AAAA    1:2:3::4
@       14400   IN      MX 10   mail.example.com.
;
; www config
; -----------------------------------------------------------------
www     14400   IN      CNAME   example.com.
;

zones.php

<?php
$output = array();
$configName = '';
$configFound = false;

$handle = fopen("zones.txt", "r");
if ($handle) {
    while (!feof($handle)) {
        $row = fgets($handle);
        if (substr($row, 0, 1) == ';') {
            if (strpos($row, "config") === false) {
                $configFound = false;
            } else {
                $configFound = true;
                $configName = $row;
                $configName = str_replace(' ', '', $configName);
                $configName = str_replace(';', '', $configName);
                $configName = str_replace('config', '', $configName);
            }   
        } else {
            $output[$configName][] = $row;
        }
    }
    fclose($handle);
}

var_dump($output);
?>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.