Aloha ;)
...und hier der PHP-Code mit rekursiv aufgerufenen Closures:
// das Posting war zu lang
<?php
function decode_strange($raw) {
$decoded = [];
$lines = explode("\n", $raw);
// remove "\r" character if present
foreach ($lines as $i=>$line) {
$lines[$i] = (strrpos($line, "\r") === strlen($line)-1)
? substr($lines[$i], 0, -1)
: $lines[$i];
// better-to-parse formatting:
// "[category]" -> "category"
// "data"/" data" -> ".data"
// ".prop" -> "..prop" and so on
if (preg_match('~^\[([^\]]+)\]$~', trim($lines[$i]), $matches)) {
$lines[$i] = $matches[1];
} else {
// remove starting whitespaces
if (preg_match('~^\s*(.+)$~', $lines[$i], $matches)) {
// add another dot in front to make space for category
$lines[$i] = '.'.$matches[1];
}
}
}
$parse_layer = function ($layer_lines, $depth, $func) {
$layer = [];
$sub_heading = '';
$sub_lines = [];
foreach ($layer_lines as $line) {
// count starting dots
if (preg_match('~^(\.*)([^\.].*)$~', $line, $matches)) {
$dotcount = strlen($matches[1]);
// if count of starting dots matches depth, this is a new heading or property
if ($dotcount == $depth) {
if (preg_match('~(.+)=(.*)~', $matches[2], $prop_matches)) {
// this is a property
// arrayfy for case of duplicate headings
if (!array_key_exists($prop_matches[1], $layer)) {
$layer[$prop_matches[1]] = [];
}
$layer[$prop_matches[1]][] = $prop_matches[2];
} else {
// if we already had a heading, save old heading in layer
if ($sub_heading !== '') {
// arrayfy for case of duplicate headings
if (!array_key_exists($sub_heading, $layer)) {
$layer[$sub_heading] = [];
}
$layer[$sub_heading][] = $func($sub_lines, $depth + 1, $func);
}
$sub_heading = $matches[2];
$sub_lines = [];
}
} elseif ($dotcount > $depth) {
$sub_lines[] = $line;
} else {
throw new Exception('Dot count does not match: Found '.$dotcount.' dots in depth '.$depth);
}
}
}
// if there is a leftover heading, save it
if ($sub_heading !== '') {
// arrayfy for case of duplicate headings
if (!array_key_exists($sub_heading, $layer)) {
$layer[$sub_heading] = [];
}
$layer[$sub_heading][] = $func($sub_lines, $depth + 1, $func);
} else {
// this means there was not even one heading!
}
// de-arrayfy where possible
foreach ($layer as $li => $larr) {
if (count($larr) == 1) {
$layer[$li] = $layer[$li][0];
}
}
return $layer;
};
return $parse_layer($lines, 0, $parse_layer);
}
function encode_strange ($data, $newline = "\r\n") {
$parse_layer = function($layer_data, $depth, $newline, $func) {
$depth_prefix = ($depth >= 1)
? ' ' : '';
for ($i = 1; $i < $depth; $i++) {
$depth_prefix .= '.';
}
$depth_suffix = '';
if ($depth == 0) {
$depth_prefix = '[';
$depth_suffix = ']';
}
$ltext = '';
foreach ($layer_data as $ltitle => $lelm) {
// only properties are non-arrays
if (!is_array($lelm)) {
$ltext .= $depth_prefix.$ltitle.$depth_suffix.'='.$lelm.$newline;
} else {
// numeric indices mean this is a list
$fully_numeric = true;
foreach (array_keys($lelm) as $lek) {
$fully_numeric = $fully_numeric && is_numeric($lek);
}
if ($fully_numeric) {
// add each list element
foreach ($lelm as $llistelm) {
$ltext .= $depth_prefix.$ltitle.$depth_suffix.$newline;
$ltext .= $func($llistelm, $depth+1, $newline, $func);
}
} else {
// add this single element
$ltext .= $depth_prefix.$ltitle.$depth_suffix.$newline;
$ltext .= $func($lelm, $depth+1, $newline, $func);
}
}
}
return $ltext;
};
return $parse_layer($data, 0, $newline, $parse_layer);
}
$decoded = decode_strange(file_get_contents('./strangefile.txt'));
echo '<pre>';
echo print_r($decoded, true);
echo '</pre>';
file_put_contents('./strangefile_reencoded.txt', encode_strange($decoded));
?>