PHP - 遍历文件并为JSON分解文本块

I have a file on a server that I need to parse and build a JSON object to return. I am using PHP.

The file contents look something like this:

########################################
#             NOTES FILE
#
# THIS FILE IS AUTOMATICALLY GENERATED
#        DO NOT MODIFY THIS FILE!
########################################

info {
    created=1552596653
    version=4.4.3
    last_update_check=1552554585
    update_available=0
    last_version=4.4.3
    new_version=4.4.3
    }

programstatus {
    modified_host_attributes=0
    modified_service_attributes=0
    pid=11523
    daemon_mode=1
    program_start=1552593834
    last_log_rotation=0
    ...

Ideally, I would like to grab EACH segment (eg: "info", "programstatus", etc...) and add them to the JSON obj/array as I parse through it. With each attribute = value being assigned accordingly.

So something like:

$data = array();

// Loop here for each segment
$data['info'] = array(
    "created" => "1552596653",
    "version" => "4.4.3",
    etc...
)

// Then wrap it up with something like
return json_encode($data);

I just can't "think" to loop through the file while breaking it out into chunks.

I have the file contents via:

$statusFile = '/location/to/my/data/file';

ob_start();
include( $statusFile );
$statusFileContent = ob_get_contents();
ob_end_clean();

You need to accomplish 3 things:

  1. Load the file contents in a way that will allow you to read very large files without storing it entirely in memory.
  2. When getting each line, you need to run it through a parsing algorithm of your own design to be able to extract the data efficiently.
  3. You finally need to write each line's data to memory (or a file if you expect the data to be too large and might run out.

Here is a rough chunk of code I threw together that illustrates the approach you could take. I tested it on PHP Fiddle, but couldn't figure out how to share a link.

<?php

    // File path to load
    // $file = "/path/to/file.txt";
    $file = "https://pastebin.com/raw/gu2AC7qy";

    // Flag indicating we are inside of a "block"
    $inBlock = false;

    // Name/Key of current "block"
    $blockName = null;

    // Container for our data
    $data = [];

    // Open for reading
    $handle = fopen($file, 'r');

    // If we opened it (you should add better error handling)
    if ($handle) {

        // Grab each line one at a time
        while(($line = fgets($handle)) !== false) {

            // Cleanup line
            $line = trim($line);

            // Throw away useless lines (comments, empty, etc.)
            if (empty($line)) {
                // Skip blank lines
                continue;
            }
            if (substr($line, 1) == '#') {
                // Skip comments
                continue;
            }

            // Check if start of "block"
            if (substr($line, -1) == '{') {
                // Set the flag
                $inBlock = true;
                // Get the block name
                $blockName = trim(str_replace('{', '', $line));
                // Create new data section
                $data[$blockName] = [];
                // Get next line
                continue;
            }

            // If currently inside block
            if ($inBlock === true && ! empty($blockName)) {
                // Get a data attribute
                $dataRow = trim($line);
                // Parse as key/value
                $dataRowParts = explode("=", $dataRow);
                $key = isset($dataRowParts[0]) ? trim($dataRowParts[0]) : null;
                $value = isset($dataRowParts[1]) ? trim($dataRowParts[1]) : "";
                // Store in current block's data
                if ($key !== null) {
                    $data[$blockName][$key] = $value;
                }
                // Get next line
                continue;
            }

            // Check if end of "block"
            if (substr($line, -1) == '}') {
                // Clear flag
                $inBlock = false;
                // Unset block name
                $blockName = null;
                // Get next line 
                continue;
            }
        }

        // Close the file
        fclose($handle);
    }

    // Output data as JSON
    echo json_encode($data);

?>

Ideally you would put this logic in classes and methods so it's not a giant wall of code -- and of course add appropriate error handling. Good luck!

I made an elegant solution for you. You don't need loops, you can do it with 5 lines of code. Just use regex and transform the file you have.

$file = "Your file as a string";

//Get the titles like 'info' and put it around quotion marks
$titles_changed = preg_replace('/(.*)\{/', '"$1":{', $file, -1 );

//Get strings like created=1552596653 and transform to "created"="1552596653",
$values_changed = preg_replace('/(.*)=(.*)/', '"$1":"$2",', $titles_changed );

//Remove spaces from the string
$no_spaces = preg_replace('/\s/s', '', $values_changed);

//Fix all that became ",}" from the second replacement and transforms it into "},"
$limits_fixed = preg_replace('/\,\}/', '},', $no_spaces);

//Remove a "," that lasts on the end of the file and put all string around brakets
$json = "{". rtrim($limits_fixed, ',') . "}";

$object = json_decode($json);

You can take advantage of the fact that this is almost a .ini file.

Remove that top part and convert the bracketed groups into ini sections

$sections = preg_replace(['/#.*/', '/(\S+) \{/', '/}/'], ['', '[$1]', ''], $file_contents);

Then it's a .ini string.

$result = parse_ini_string($sections, true);

echo json_encode($result);