Daten aus einem String auslesen

dsool

Grünschnabel
Wahrscheinlich wurde die Frage schon öfters beantwortet, doch ich bekomme es nicht alleine hin.

Ich habe heute mit PHP angefangen und mein Ziel ist es, das ich variable Werte aus einem Text der statische Elemente hat auslesen kann.

Das Speichern in einer Datenbank funktioniert soweit.

Nun zu dem Problem Text:


Code:
Quellcode:

Geologischer Sondierungsbericht von 11:167:14
Typ    Steinklumpen
Objekt    ---
Eisengehalt    90 %
vorkommen chem. Elemente    104 %
Eisdichte    36 %
Lebensbedingungen    33 %
Gravitation    0.72
Besonderheiten    
//Hier stehen ab und zu Werte
Forschungmod.    100 %
Gebäudebau Kosten Mod.    1.00
Gebäudebau Dauer Mod.    1.00
Schiffbau Kosten Mod.    1.00
Schiffbau Dauer Mod.    1.00


Die fetten Werte sind die die ich brauche [bzw. die die sich ändern]


Quellcode:

HTML:
<html>
<head>
<title>Parser</title>
</head>
<body>

<form action="Parser.php">
<p>Bitte geben Sie den gesamten Geoscannbericht ein [Nicht den Quellcode]<br>
<textarea name="user_eingabe" cols="60" rows="25"></textarea>
</p>
<input type="Submit">
</form>

</body>
</html>


Soweit die HTML Datei, nun die Parser.php

PHP-Quellcode:

PHP:
<?php

$Variable="Nix gefunden";
$pos = strpos ($user_eingabe, "Typ");
$Variable= substr("$user_eingabe", $pos+4,12);

echo $Variable;
?>


Wenn da nun "Steinklumpen" steht funktioniert das [Steinklumpen steht in $Variable], das Problem ist aber, das statt Steinklumpen, auch andere Wörter da stehen können [<>Länge12].

Die selbe Frage stellt sich bei den Prozentwerten. Diese können eine Länge zwischen 3 [1 %] und 5 [100 %] haben. Auch da finde ich keinen Befehl, der mir hilft diese Aufgabe zu lösen.

Die Frage ist also, wie gehe ich mit diesen [in der Länge] änderbaren Werten um und wie bekomme ich sie in Variablen?

Falls Fragen bestehen, bitte posten.
Danke im Vorraus
dsool
 
Die Lösung sind reguläre Ausdrücke, auch wenn das unten stehende Skript von einem Experten bestimmt kürzer und effizienter geschrieben wäre.
Es zerlegt den Text in ein 2-Dimensionales Array, welches in der zweiten Ebene die Indizes 'bezeichner', 'wert' und 'einheit' enthält. Die Zahlenkombination aus der ersten Zeile wird gesondert ausgelesen und steht alleine in der Variable $von.
PHP:
<pre>
<?
$test='Geologischer Sondierungsbericht von 11:167:14
Typ    Steinklumpen
Objekt    ---
Eisengehalt    90 %
vorkommen chem. Elemente    104 %
Eisdichte    36 %
Lebensbedingungen    33 %
Gravitation    0.72
Besonderheiten
Forschungmod.    100 %
Gebäudebau Kosten Mod.    1.00
Gebäudebau Dauer Mod.    1.00
Schiffbau Kosten Mod.    1.00
Schiffbau Dauer Mod.    1.00';
//mit dem regulärem wird der Text in einzelne Zeilen zerlegt
// \r: Carriage-Return \n: Line-break
// | steht für "oder"
// die einzelnen Zeilen landen in einem Array
$arr=preg_split("/\r\n|\r|\n/",$test);

//siehe Funktionsbeschreibung zu array_shift
$head=array_shift($arr);

//der reguläre Ausdruck sucht nach:
// beliebige Zeichenkette
// dann ein oder mehr Ziffern (\d), dann ein :
// usw. das $ bedeutet, dass der String dort zu Ende
// sein muss
preg_match("/.*?(\d+?:\d+?:\d+?)$/",$head,$von);

//Funktionsbeschreibung preg_match!
// $von war bis eben noch ein Array
$von=$von[1];

//Ein leeres Array erzeugen, in das die
// Daten später angefügt werden
$daten=array();

//Das array wird nun Zeilenweise durchgegangen
foreach($arr as $a) {
    //temporäres Array, in dem 'bezeichner', 'wert'
    // und 'einheit' stehen
    // es wird unten an $daten angefügt
    $temp=array();
    
    //Der reg. Ausdruck muss am Anfang des
    // Strings beginnen (^) und
    // sucht nach:
    // einer Zeichenkette, Leerzeichen (min. 1, wg. +),
    // Ziffer oder . (min. 1)
    // min. 1 Leerzeichen
    // min. 1 weiteres Zeichen
    // das Ende der Zeichenkette muss erreicht sein
    preg_match("/^(.*)\ +?([\d\.]+)\ +?(.+)$/",$a,$vals);
    
    //wenn er "getroffen" hat, sind alle drei
    // Angaben vorhanden
    if (count($vals)>1) {
        $temp['bezeichner']=$vals[1];
        $temp['wert']=$vals[2];
        $temp['einheit']=$vals[3];
    } else {
        //ansonsten fehlt die Einheit
        // Ausdruck ist wie oben, jedoch ohne die
        // Einheit und statt den Ziffern oder Punkt
        // darf auch ein beliebiges Zeichen dort stehen
        preg_match("/^(.*)\ +?([\d\.]+|.+)$/",$a,$vals);
        if (count($vals)>1) {
            $temp['bezeichner']=$vals[1];
            $temp['wert']=$vals[2];
            $temp['einheit']="";
        } else {
            //wenn auch dieser Ausdruck nicht
            // getroffen hat, wird der gesamte
            // String als Bezichner übernommen
            // (z.B. bei der Zeile "Besonderheiten")
            $temp['bezeichner']=$a;
            $temp['wert']="";
            $temp['einheit']="";
        }
    }
    //das temporäre Array wird an
    // $daten angehängt
    // Du könntest hier auch
    // $daten[$temp['bezeichner']]=$temp;
    // schreiben, dann könntest Du später
    // über den Bezeichner auf die Arrayelemente
    // zugreifen, z.B.:
    // $Eisdichte=$daten['Eisdichte']['wert']
    $daten[]=$temp;
}

//ab hier gibt es $von und $daten
// $von ist ein String
// $daten ist ein Array
// Du kannst z.B. mit foreach durch
// das Array laufen und so jeden
// Wert einzeln in die DB schreiben
echo "<b>".$von."</b>\n";
print_r($daten);
?>
</pre>
Gruß hpvw
 
Zuletzt bearbeitet:
Das Ergebnis deines Codes ist genau das was ich brauche!

Das Problem ist nur, ich verstehe ihn nicht :(
Könntest du ihn mir mal etwas genauer erläutern?

Z.B: Wo kann ich den Inhalt von "Wert" und "Einheit" in eine neue Variable schreiben [und somit in eine DB]?

Das Hauptproblem ist, dass am Ende das ja alles wieder in eine Variable geschrieben wird und nicht in meine benötigten verschiedenen.

Wäre nett wenn mir das noch jemand am Beispiel erklären könnte [den Quelltext vll kommentieren]
 
Habe oben den Quelltext kommentiert.
dsool hat gesagt.:
Das Hauptproblem ist, dass am Ende das ja alles wieder in eine Variable geschrieben wird und nicht in meine benötigten verschiedenen.
Es steht jedoch strukturiert in einem Array, bei dem Du auf jedes Element einzeln zugreifen kannst.
Bevor weitere Fragen kommen, bitte den Quelltext zeilenweise durchgehen und wenn Du irgendwo hängst erst einen Blick in die Funktionsreferenz werfen.

Gruß hpvw
 
Zuletzt bearbeitet:
Danke für deine Hilfe!
Das auslesen in Variablen funktioniert jetzt perfekt.

Nun taucht aber ein weiteres Problem auf:
Bei Eingabe dieses Ursprungstextes
Code:
$test='Geologischer Sondierungsbericht von 11:165:8
Typ	Steinklumpen
Objekt	---
Eisengehalt	143 %
vorkommen chem. Elemente	88 %
Eisdichte	29 %
Lebensbedingungen	103 %
Gravitation	0.69
Besonderheiten	Ureinwohner
mystische Quelle
Forschungmod.	100 %
Gebäudebau Kosten Mod.	1.00
Gebäudebau Dauer Mod.	1.00
Schiffbau Kosten Mod.	1.00
Schiffbau Dauer Mod.	1.00';

wird "Besonderheiten" als Bezeichner und "Ureinwohner" als Wert genommen.
Danach wird "mystische" als Bezeichner und "Quelle" als Wert genommen.
Code:
[7] => Array
        (
            [bezeichner] => Besonderheiten	Ureinwohner
            [wert] => 
            [einheit] => 
        )

    [8] => Array
        (
            [bezeichner] => mystische
            [wert] => Quelle
            [einheit] => 
        )
[Zur Verdeutlichung]

In der Variable sollte aber "Besonderheiten" als Bezeichner und "Ureinwohner [nächste Zeile, oder Trennzeichen] mystische Quelle" als Wert stehen.

Code:
[7] => Array
        (
            [bezeichner] => Besonderheiten
            [wert] => Ureinwohner, mystische Quellen
            [einheit] => 
        )

Kannst du mir bitte bei dem Problem auch noch einen Tipp geben?

MfG
dsool
 
Da müßtest Du Dir eine andere Syntax überlegen oder explizit bei "Besonderheiten" auf andere Weise auslesen.
Das Problem besteht darin, dass der Bezeichner in einigen Fällen Leerzeichen enthält, die Werte (bisher) jedoch nicht. Das ließ sich gut auslesen. Nun befinden sich aber sowohl in einigen Bezeichnern, als auch in Text-Werten, Leerzeichen und das Trennzeichen ist auch ein Leerzeichen. Zu allem Überfluss enthält der Wert auch noch einen Zeilenumbruch.
Der nächste Bezeichner beginnt dann wieder als Text, ohne dass auf irgendeine Art gekennzeichnet wird, dass der Wert beendet wurde.
Ich wüßte nicht, wie man das auslesen soll ohne neue Fehler bei weiteren Fällen einzubauen.
Des Weiteren verwendest Du nun auch noch Tabs.

Ich setze jetzt folgendes voraus:
Nur bei dem Bezeichner Besonderheiten gibt es derartige Konstrukte.
Ein Wert bleibt in derselben Zeile.
Trennzeichen zwischen Bezeichner und Wert können beliebig viele Punkte oder Tabs sein (inkl. oder).

Das funktioniert dann nach ersten Tests mit folgendem Code:
PHP:
<pre>
<?
$test='Geologischer Sondierungsbericht von 11:165:8
Typ	Steinklumpen
Objekt	---
Eisengehalt	143 %
vorkommen chem. Elemente	88 %
Eisdichte	29 %
Lebensbedingungen	103 %
Gravitation	0.69
Besonderheiten	Ureinwohner, mystische Quelle
Forschungmod.	100 %
Gebäudebau Kosten Mod.	1.00
Gebäudebau Dauer Mod.	1.00
Schiffbau Kosten Mod.	1.00
Schiffbau Dauer Mod.	1.00';

$arr=preg_split("/\r\n|\r|\n/",$test);
$head=array_shift($arr);
preg_match("/.*?(\d+?:\d+?:\d+?)$/",$head,$von);
$von=$von[1];
$daten=array();
foreach($arr as $a) {
    $temp=array();

    // \t ist der zusätzlich abgefragte Tab
    preg_match("/^(.*)[\ \t]+?([\d\.]+)\ +?(.+)$/",$a,$vals);
    if (count($vals)>1) {
        $temp['bezeichner']=$vals[1];
        $temp['wert']=$vals[2];
        $temp['einheit']=$vals[3];
    } else {
        // Mit oder (|) wird explizit nach Besonderheiten gesucht
        // wird dies nicht gefunden wird beliebiges genommen, da
        // dann nach dem zweiten Teil des Oder gesucht wird
        preg_match("/^(Besonderheiten|.*)[\ \t]+?([\d\.]+|.+)$/",$a,$vals);
        if (count($vals)>1) {
            $temp['bezeichner']=$vals[1];
            $temp['wert']=$vals[2];
            $temp['einheit']="";
        } else {
            $temp['bezeichner']=$a;
            $temp['wert']="";
            $temp['einheit']="";
        }
    }
    $daten[]=$temp;
}
echo "<b>".$von."</b>\n";
print_r($daten);
?>
</pre>
Gruß hpvw
 
Wenn man den Text [mit Komma] so übernimmt funktioniert das wunderbar.
Das Problem ist aber, dass ich den Text nicht selber ändern kann [mit Komma trennen].

Er wird automatisiert [von einer externen Quelle] geschrieben. Das heißt ich muss mit diesen Zeilenumbrüchen umgehen können. Eine weitere Komplikation ist ja, dass die Anzahl an Werten hinter Besonderheiten variieren kann. [Ich glaube zwischen 0 und 3 Zeilen].

Kann man [bevor man den String in Zeilen zerlegt], den Teil zwischen "Besonderheiten" und "Forschungmod." nicht so abändern, dass man dazwischen nur noch Kommata, statt Zeilenumbrüchen nimmt?
So müsste man das doch dann hinbekommen können :confused:
 
Du müßtest dann ja wissen, in welcher Zeile Du aufhören mußt.
Mir fällt dazu ad hoc keine einfache Möglichkeit ein, die ich mal eben so hinschreiben könnte.
Das geht ja schon richtig intelligente Inhaltliche Texterkennung. Wenn der nächste mögliche Bezeichner nun einer aus einer begrenzten Auswahl ist, sehe ich noch ein paar Chancen, zumindest solange einer dieser Bezeichner nicht auch als Besonderheit auftauchen kann.
Da habe ich aber ehrlich gesagt im Moment keine Zeit für.
Da es sowieso schwierig ist, alle Fälle automatisch abzuhandeln, würde ich nach dem Einlesen das Ergebnis strukturiert ausgeben und vom User bestätigen bzw. korrigieren lassen. Wenn das mit den zusätzlichen Zeilen das Hauptproblem ist, könntest Du z.B. mit einem einfachen Button in den Zeilen nach 'Besonderheiten' den User angeben lassen: "Die Zeilen von Besonderheiten bis zu dieser Zeile gehören noch zu 'Besonderheiten'."

Gruß hpvw
 
Ich weiß ja in welcher Zeile ich aufhören muss.
Nämlich beim Wort "Forschungmod."

Also denke ich mir, dass man die Postition den Wortes "Besonderheiten" finden müssen könnte. Danach die Position von "Forschungmod." und dann diese zwei Positionen als Anfangs und Endpunkt an Replace übergeben können müsste.

Quasi Replace(Start: hinter "Besonderheiten", Ende: Vor "Forschungmod.", Ersetze Zeilenumbruch durch ",")

Die Frage ist nur, wie baue ich diese Replace Funktion in PHP?
 
Hier nur eine kurze Idee, besonders flexibel ist es nicht:
PHP:
<pre>
<?
$test='Geologischer Sondierungsbericht von 11:165:8
Typ	Steinklumpen
Objekt	---
Eisengehalt	143 %
vorkommen chem. Elemente	88 %
Eisdichte	29 %
Lebensbedingungen	103 %
Gravitation	0.69
Besonderheiten	Ureinwohner
mystische Quelle
Forschungmod.	100 %
Gebäudebau Kosten Mod.	1.00
Gebäudebau Dauer Mod.	1.00
Schiffbau Kosten Mod.	1.00
Schiffbau Dauer Mod.	1.00';

//hier einstellen:
$bezeichnerNachBesonderheiten = 'Forschungmod.';

$arr=preg_split("/\r\n|\r|\n/",$test);

$head=array_shift($arr);

preg_match("/.*?(\d+?:\d+?:\d+?)$/",$head,$von);

$von=$von[1];

$daten=array();

//Merkvariable:
$isBesonderheiten=false;
foreach($arr as $a) {
    //Wenn in Besonderheiten und nicht Forschungmod.
    // dann den Text einfach zu Besonderheiten geben
    if ($isBesonderheiten
            && substr($a,0,strlen($bezeichnerNachBesonderheiten))
                != $bezeichnerNachBesonderheiten) {
        $daten[count($daten)-1]['wert'] .= "\n".$a;
    }
    //Wenn in Besonderheiten und jetzt Forschungmod.
    // dann kennzeichnen, dass nicht mehr in Besonderheiten
    if ($isBesonderheiten
            && substr($a,0,strlen($bezeichnerNachBesonderheiten))
                == $bezeichnerNachBesonderheiten) {
        $isBesonderheiten = false;
    }
    //Wenn man nicht gerade in den Besonderheiten wuselt,
    // wird das gleiche gemacht, wie bisher
    if (!$isBesonderheiten) {
        $temp=array();

        preg_match("/^(.*)[\ \t]+?([\d\.]+)\ +?(.+)$/",$a,$vals);

        if (count($vals)>1) {
            $temp['bezeichner']=$vals[1];
            $temp['wert']=$vals[2];
            $temp['einheit']=$vals[3];
        } else {
            preg_match(
                "/^(Besonderheiten|.*)[\ \t]+?([\d\.]+|.+)$/",
                $a,
                $vals);
            if (count($vals)>1) {
                $temp['bezeichner']=$vals[1];
                $temp['wert']=$vals[2];
                $temp['einheit']="";
            } else {
                $temp['bezeichner']=$a;
                $temp['wert']="";
                $temp['einheit']="";
            }
        }
        $daten[]=$temp;
    }

    //Prüfen ob Besonderheiten erreicht ist:
    if ($temp['bezeichner']=='Besonderheiten') {
        $isBesonderheiten=true;
    }
}

echo "<b>".$von."</b>\n";
print_r($daten);
?>
</pre>
Ich sehe jedoch sofort ein Problem:
Sollte es einmal keinen Wert "Forschungmod." geben, dieser später kommen oder anders geschrieben sein, so ist alles, was danach kommt, plötzlich eine Besonderheit.

Gruß hpvw
 
Zurück