Spezielle Textdatei mit PHP auslesen und speichern
pathfinder
- datenbank
- php
0 Felix Riesterer0 Rolf B
1 Robert B.1 Camping_RIDER
Hallo,
habe hier Daten in einer Textdatei (siehe unten), welche etwas merkwürdig mittels Leerzeichen und Punkten strukturiert sind. Würde diese zur Weiterverarbeitung gerne mittels PHP auslesen... aber irgendwie fehlt mir hierzu ein Lösungsansatz, wie man das möglichst Sinnvoll (beispielsweise in ein mehrdimensionales array) bewerkstelligen kann. Schließlich soll das am Ende ja ggf. wieder in dem selben Format als Textdatei gespeichert werden können. Eventuell kennt ja jemand diese Art der Datenspeicherung oder/und hat Idee wie man das mit mögl. wenig Aufwand geschickt ausgelesen und wieder gespeichert bekommt!?
VG
[datensatz]
version
.minor=2
session
.id=32
datensatz
.name=Muster
.orientierung=1
.uid=0x1
.adresse=0x1
.typ=A
.icon=list
.symbol=2
.xy=8
.xz=4
.max=50
.min=5
.funktion
..nr=0
..typ=100
..wert=1
.funktion
..nr=1
..typ=120
..wert=5
.funktion
..nr=2
..typ=141
.funktion
..nr=3
..typ=142
.funktion
..nr=4
.funktion
..nr=5
datensatz
.name=Beispiel
.uid=0x2005
.adresse=0x5
.typ=B
.sid=0x5
.icon=block
.symbol=5
.xy=6
.xz=5
.max=80
.min=10
.funktion
..nr=0
..typ=1
.funktion
..nr=1
..typ=40
.funktion
..nr=2
..typ=20
..wert=1
.funktion
..nr=3
..typ=18
.funktion
..nr=4
.funktion
..nr=5
..typ=142
datensatz
.name=Test
.orientierung=1
.uid=0x2006
.adresse=0x6
.typ=B
.sid=0x6
.icon=tab
.symbol=3
.xy=15
.xz=15
.max=255
.min=255
.funktion
..nr=0
..typ=1
.funktion
..nr=1
.funktion
..nr=2
..typ=200
.funktion
..nr=3
.funktion
..nr=5
..typ=18
..wert=99
.funktion
..nr=6
..typ=48
Lieber pathfinder,
woher hast Du diese Datei? Sie ähnelt dem .ini-Format. Keine Ahnung, ob Dir da parse_ini_file()
helfen kann.
Liebe Grüße
Felix Riesterer
Hallo Felix,
das dachte ich auch zuerst, aber dieses Punkt und Punktpunkt-Format ist doch abweichend. Das ist kein reines INI-Format. Ich habe sowas noch nie gesehen und mein schneller Versuch, etwas über "ini-files with dots" oder "hierarchical ini file" zu finden, führte nicht zum Ziel.
Es gibt ein hierarchisches Ini-Format, aber da wird vor den Punkten jeweils der Sektionsname wiederholt. PHP kennt das nicht. Und es versteht auch keine Zeilen, die nur einen Sektionsnamen enthalten, ohne Gleichheitszeichen.
Man muss es wohl selbst bauen. Aber das ist nicht trivial, und es wird durch die PHP-Technik des "copy on write" für Arrays auch nicht einfacher. Spontan herunterschreiben kann ich das nicht, da bräuchte ich sicherlich eine Stunde oder zwei für. Sag ich jetzt. 4 Stunden später bin ich vermutlich am Rande des Nervenzusammenbruchs und frickele den siebzehnten Sonderfall hinein 😉
pathfinder - hast Du sachdienliche Hinweise auf die Anwendung, die eine Ini-Datei in diesem Format erwartet?
Rolf
Moin,
ist das eventuell eine Baumstruktur, in der die Punkte Zweige darstellen? Mich erinnert das eine mögliche Darstellung von Hierarchien – wenn ich das umformatierte:
[datensatz] version minor=2 session id=32 datensatz name=Muster orientierung=1 uid=0x1 adresse=0x1 typ=A icon=list symbol=2 xy=8 xz=4 max=50 min=5 funktion nr=0 typ=100 wert=1 funktion nr=1 typ=120 wert=5 …
Viele Grüße
Robert
Aloha ;)
Rolfs Schätzung mit den zwei Stunden war ganz gut - ich habe etwa zweieinhalb gebraucht, dafür aber auch gleich die Funktion zum Re-Codieren fertig gemacht.
Ist jetzt nicht so als wär mir langweilig, aber irgendwie hats mich gejuckt.
Das Re-Encodierte Ergebnis:
[datensatz]
version
.minor=2
session
.id=32
datensatz
.name=Muster
.orientierung=1
.uid=0x1
.adresse=0x1
.typ=A
.icon=list
.symbol=2
.xy=8
.xz=4
.max=50
.min=5
.funktion
..nr=0
..typ=100
..wert=1
.funktion
..nr=1
..typ=120
..wert=5
.funktion
..nr=2
..typ=141
.funktion
..nr=3
..typ=142
.funktion
..nr=4
.funktion
..nr=5
datensatz
.name=Beispiel
.uid=0x2005
.adresse=0x5
.typ=B
.sid=0x5
.icon=block
.symbol=5
.xy=6
.xz=5
.max=80
.min=10
.funktion
..nr=0
..typ=1
.funktion
..nr=1
..typ=40
.funktion
..nr=2
..typ=20
..wert=1
.funktion
..nr=3
..typ=18
.funktion
..nr=4
.funktion
..nr=5
..typ=142
datensatz
.name=Test
.orientierung=1
.uid=0x2006
.adresse=0x6
.typ=B
.sid=0x6
.icon=tab
.symbol=3
.xy=15
.xz=15
.max=255
.min=255
.funktion
..nr=0
..typ=1
.funktion
..nr=1
.funktion
..nr=2
..typ=200
.funktion
..nr=3
.funktion
..nr=5
..typ=18
..wert=99
.funktion
..nr=6
..typ=48
Eine Kleinigkeit ist anders - die Leerzeichen vor den Elementen der ersten Hierarchieebene sind jetzt einheitlich vorhanden, das waren sie im Beispiel nicht vollständig, ich vermute das war ein Eingabefehler hier. Ansonsten müsste das deckungsgleich sein.
Das daraus von PHP geparste Array:
Array
(
[datensatz] => Array
(
[version] => Array
(
[minor] => 2
)
[session] => Array
(
[id] => 32
)
[datensatz] => Array
(
[0] => Array
(
[name] => Muster
[orientierung] => 1
[uid] => 0x1
[adresse] => 0x1
[typ] => A
[icon] => list
[symbol] => 2
[xy] => 8
[xz] => 4
[max] => 50
[min] => 5
[funktion] => Array
(
[0] => Array
(
[nr] => 0
[typ] => 100
[wert] => 1
)
[1] => Array
(
[nr] => 1
[typ] => 120
[wert] => 5
)
[2] => Array
(
[nr] => 2
[typ] => 141
)
[3] => Array
(
[nr] => 3
[typ] => 142
)
[4] => Array
(
[nr] => 4
)
[5] => Array
(
[nr] => 5
)
)
)
[1] => Array
(
[name] => Beispiel
[uid] => 0x2005
[adresse] => 0x5
[typ] => B
[sid] => 0x5
[icon] => block
[symbol] => 5
[xy] => 6
[xz] => 5
[max] => 80
[min] => 10
[funktion] => Array
(
[0] => Array
(
[nr] => 0
[typ] => 1
)
[1] => Array
(
[nr] => 1
[typ] => 40
)
[2] => Array
(
[nr] => 2
[typ] => 20
[wert] => 1
)
[3] => Array
(
[nr] => 3
[typ] => 18
)
[4] => Array
(
[nr] => 4
)
[5] => Array
(
[nr] => 5
[typ] => 142
)
)
)
[2] => Array
(
[name] => Test
[orientierung] => 1
[uid] => 0x2006
[adresse] => 0x6
[typ] => B
[sid] => 0x6
[icon] => tab
[symbol] => 3
[xy] => 15
[xz] => 15
[max] => 255
[min] => 255
[funktion] => Array
(
[0] => Array
(
[nr] => 0
[typ] => 1
)
[1] => Array
(
[nr] => 1
)
[2] => Array
(
[nr] => 2
[typ] => 200
)
[3] => Array
(
[nr] => 3
)
[4] => Array
(
[nr] => 5
[typ] => 18
[wert] => 99
)
[5] => Array
(
[nr] => 6
[typ] => 48
)
)
)
)
)
)
...und hier der PHP-Code mit rekursiv aufgerufenen Closures:
// das Posting war zu lang
Vielleicht (hoffentlich?) trägt diese Lösung auch jenseits der Möglichkeit zum Copy+Paste zu Erkenntnissen bei. Falls es Fragen dazu gibt beantworte ich die jedenfalls gerne.
Grüße,
RIDER
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));
?>
Hallo zusammen,
war zwei Tage unterwegs und konnte mich daher erst jetz wieder damit befassen. Danke für eure Hilfe und besonders @Camping_RIDER für seine 1A Lösung 👍🍺
Habe etwas gebraucht um dahinter zu steigen, da ich reguläre Ausdrücke nicht immer gleich kappiere 😵
Sorry für meinen kleinen Eingabefehler mit dem beiden Leerzeichen vor dem 2. und 3. datensatz
. Die gehören da nicht hin, wodurch dann natürlich decode_strange
eine falsche Ausgabe liefert. Ist aber keine große Sache, habe einfach $depth >= 1
das =
geklaut und schon ist es valide.
VG
Aloha ;)
Sehr gerne. Freut mich, dass es geholfen hat!
Grüße,
RIDER