PCRE-Funktionen

CurryWurst_

Grünschnabel
Hallo liebe Community!

Mit meinem allerersten Forumeintrag möchte ich euch gleich einmal "belästigen" :p
Es handelt sich dabei um folgendes Problem:

Zurzeit schreibe ich an einem PHP-Skript, welches Daten von Sockets empfängt und sie auswertet. Bevor ich eingegangene Daten parse möchte ich sie im Voraus auf ihre Integrität überprüfen. Diese Validation möchte ich mit Hilfe von PCRE-Funktionen machen. Wobei eingehende Daten die folgende Struktur haben:

(Zahl) + tab + (String1) + tab + (String2) + tab + (String3)...

"Zahl" steht sowohl für negative als auch positive Zahlenwerte und es unbegrenzt in ihrer Länge, d.h. in regex-Form ausgedrückt: -?\d+

"tab" ist einfach ein Tabulator: \t

"String1" hat im Gegensatz zu allen weitern Strings eine besonderes Merkmal: Er darf nur aus den zeichen [a-zA-Z] bestehen und ist unbegrenzt in seiner Länge, muss jedoch mindestens ein Zeichen besitzen, also [a-zA-Z]+

Alle weiteren Strings (String1, String2, String3 ... String[n]) dürfen fast beliebige Zeichen haben, bzw. alle druckbaren Zeichen: [[:print:]]

So weit so gut ;) In der Praxis möchte ich für die Validierung die PHP-Funktion preg_match_all einsetzen, wobei sie alle Treffer, die kein Tabulatorzeichen sind, in ein Array abspeichern soll. Wie es aussieht stellt das auf den ersten Blick keine Schwierigkeit da, zumindest für gewisse PHP-Experten in disem Forum, aber für mich schon. Mit dem folgenden regex String komme ich zu keinem brauchbaren Ergebnis ...
PHP:
/^(-?\d+)\t([a-z]+)(?:\t([[:print:]]+))+/

Ich wäre um jegliche Hilfe zu dieser Problemstellung sehr dankbar.

Grüße

CurryWurst_
 
Die vorgesehenen TAB-Characters werden für die PECL-konforme Schnittstelle preg gängigerweise als \t dargestellt. Ebenfalls hast du bei den vordefinierten Character Classes jeweils den Backslash vor dem Bezeichner vergessen.
Des Weiteren - da könnte ich mich aber auch irren - existiert keine solche Zeichenklasse wie [[:print:]] in preg. Stattdessen bietet sich entweder die "word"-Klasse, oder die "non whitespace"-Klasse an.

Code:
/^(?#Zeilenanfang)
(?#erster Wert)(-?\d+)\t
(?#zweiter Wert)([a-zA-Z]+)
(?:\t(?#dritter, vierter, ... Wert)(\S+))+
(?#Zeilenende)$/

Durch das Hinzufügen des Dollarzeichens am Ende erreichst du, dass jeweils die vollständige Zeile definitiv interpretiert wird. Vorausgesetzt natürlich, dass preg den Eingabestring als Multiline-String betrachtet.
 
Ups, das mit den fehlendem Backslash vor dem Tabulatorzeichen bzw, der Charakterklasse war nicht beabsichtig, das lag an der PHP-Codeformatierung.

Die Zeichenklasse [[:print:]] sollte es geben und funktioniert bei mir auch, weil PHP standardkonform zu sämtlichen PCRE-Funktionen ist, abgesehn von den Namen der einzelnen Funktionen.

Ich hab dein regex-String mal getestet und komme zum selben Ergebnis wie mit meinem String: Er matcht zwar den ersten und zweiten Wert, jedoch matcht er, wenn es mehr als drei zusätzliche Werte gibt, immer nur den letzten String. Verständlich ausgedrückt heißt das an folgenden Beispielen:

-12 cmd param1 param2 param3

... das er (-12), (cmd) und (param3) matcht, jedoch nicht (param1) und (param2).
 
Ist gewissermaßen logisch, dass der Ausdruck entsprechend dem matcht, was du angibst, da die entsprechend zuletzt gematchte Gruppe immer wieder "überschrieben" wird. Es entspricht lediglich den Spezifikationen für PCRE-konforme reguläre Ausdrücke, da ein anderes Verfahren zu undefiniertem / nicht abschätzbaren Verhalten führen würde.

Glücklicherweise bringt PHP die entsprechende Funktionalität standardmäßig mit: Die letzten Elemente werden zuerst in einer gemeinsamen Gruppe im regulären Ausdruck eingefasst, und erst in einem weiteren Schritt durch das entsprechende Trennzeichen separiert.

PHP:
preg_match(
	"/^(?#Zeilenanfang)(?#erster Wert)(-?d+)\t(?#zweiter Wert)([a-zA-Z]+)"
	+ "((?:\t(?#dritter, vierter, ... Wert)\S+)+)(?#Zeilenende)$/",
	$text,
	$matches
);

// letzte Gruppe vom Stapel holen und seperat zur Weiterverarbeitung speichern
$appendix = array_pop( $matches );

// Gruppe aufsplitten und verwerten (hier via lazy initialization)
foreach ( explode( "\t", $appendix ) as $m ) {
	$matches[] = $m;
}

// oder alternativ via array-Funktion zusammenführen
$matches = array_merge( $matches, explode( "\t", $appendix ) );
 
Zuletzt bearbeitet:
Leider muss ich nochmals nachacken. Ich habe festgestellt, dass ich noch mehr als nur den normalen Tabulator Character brauche. Jetzte wollte ich das ganze mal mit \v ausprobieren, sprich chr(11). Nur leider hat der regex-string nur noch die ersten zwei Argumente gematcht und die verbleibenden Parameter als einen Match betrachtet.

Für weitere Hilfestellungen wäre ich sehr dankbar.

Grüße aus dem schönen Hunsrück

Markus
 
Zurück