Regex Problem
lima-city → Forum → Programmiersprachen → PHP, MySQL & .htaccess
array
barbar
beispiel
code
computer
ende
ergebnis
fund
hexerei
kreatur
land
muster
performance
problem
run
sagen
solution
test
text
zahl
-
Servus,
bräuchte ma bisal hilfe bei einem Regex.
Folgende zwei Möglichkeiten von strings soll er zerlegen:
Buchstaben1 - Buchstaben2 */*, BuchstabeZahl (Zahl)
Buchstaben1, BuchstabeZahl (Zahl)
Buchstaben1 taucht immer auf. Buchstaben2 */* nicht immer, wobei * entweder eine Zahl oder ein * ist. Der "-" nur wenn Buchstaben2...
BuchstabeZahl (Zahl) taucht auch nicht immer auf.
Sop mein Regex dazu sieht wie folgt aus:
(\w*)-?(\w*)([0-9\*])/?([0-9\*]),(\w*)\(?\(d?)\)?
Nur der Matcht ned also wirklich auf garnichts.
Was habe ich falsch gemacht?
Danke für Hilfe!
PS: Kann mir jemand alternativ ne Datenbank für Magic-Karten sagen (also keine onlineDB oder fertiges Prog das gibts alle scho). -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage
-
Sollte
nicht eher so\(?\(d?)\)?
aussehen?(\(\d\))?
Wenns das nicht war, poste doch einmal ein Beispiel, dann könnte man den Regex selber ausprobieren. -
@theuntouchables
des passt so schon, soll mir explizit nur die Zahl zwischen den Klammern rausholen.
nach kuzem googeln kann man bald fündig werden: überall dort, wo eine verhältnismäßig einfache php lösung vorhanden ist, sollte man davon kontra regexp. gebrauch machen (performance). eine mögliche lösung:
<?php /* provisional solution for data */ $data = array( 'Kreatur - Egel 1/3, W (1)', 'Land', 'Hexerei, 2W (3)', 'Kreatur - Legende */*, 4GGG (7)' ); /* extracting numbers */ $cnt = 0; foreach ($data as $row => $text) { if (count($row_slash = explode('/', $text)) > 1) { $num[$cnt] = array( 'slash_left' => end($expl = explode(' ', $row_slash[0])) + 0, 'slash_right' => reset($expl) + 0 ); } if (count($row_parenthesis = explode('(', $text)) > 1) { $parenthesis_num = end($row_parenthesis) + 0; $num[$cnt]['parenthesis'] = $parenthesis_num; } $cnt++; /* test output 1 */ echo $cnt . ' - ' . print_r($num[$cnt-1], true) . '<br />'; } /* test output 2 */ echo '<hr /><pre>' . print_r($num, true) . '</pre>';
(am ende fehlt das stumpfsinnige '?>' nicht! in anderen fällen aber wohl! googeln? :)
wenn du die zahlen links und rechts vom slash nicht haben willst kommentierst einfach das erste 'if' aus.
lg
Beitrag zuletzt geändert: 8.1.2011 19:48:54 von hemiolos -
Ich hatte mit folgendem Ausdruck Erfolg:
^(\w+)( - (\w+) ([\d\*])/([\d\*]))?(, (\w+) \((\d)\))?$
Getestet habe ich das ganze mit einem kurzen PHP-Script:
Dieses Beispiel sollte erklären, was ich mir im Einzelnen bei meinem regulären Ausdruck gedacht habe .<html> <head></head> <body> <?php $res = array( "Kreatur - Egel 1/3, W (1)", "Land", "Hexerei, 2W (3)", "Kreatur - Legende */*, 4GGG (7)"); // Muster: // Buchstaben1 - Buchstaben2 */*, BuchstabeZahl (Zahl) // sowie // Buchstaben1, BuchstabeZahl (Zahl) // rxa: vereinfacht -> sucht, filtert aber nichts heraus $rxa = "#^\w+( - \w+ [\d\*]/[\d\*])?(, \w+ \(\d\))?$#"; // rxb: komplett -> sucht, und filtert Ergebnisse heraus $rxb = "#^(\w+)( - (\w+) ([\d\*])/([\d\*]))?(, (\w+) \((\d)\))?$#"; foreach ($res as $str) { if ($funde = preg_match_all($rxb, $str, $erg)) { echo "Funde: $funde<br>\n<pre>\n"; print_r($erg); echo "\n</pre><hr>\n"; } else echo "Kein Fund!<hr>\n"; } /* Funde: $erg[1][0] --> Buchstaben1 $erg[3][0] --> Buchstaben2 $erg[4][0] --> * $erg[5][0] --> * $erg[7][0] --> BuchstabeZahl $erg[8][0] --> Zahl */ ?> </body> </html>
Zu dem Beispiel von hemiolos möchte ich noch sagen, dass es zwar richtig ist, das reguläre Ausdrücke sehr leistungsfressend sind, bei einem so komplexen Beispiel letztenden Endes aber doch performanter (und viel übersichtlicher) als zig explode()-Aufrufe.
Beitrag zuletzt geändert: 8.1.2011 19:50:47 von metalmachine -
metalmachine schrieb:
Zu dem Beispiel von hemiolos möchte ich noch sagen, dass es zwar richtig ist, das reguläre Ausdrücke sehr Leistungsfressenden sind, bei einem so komplexen Beispiel letztenden Endes aber doch performanter (und viel übersichtlicher) als zig explode()-Aufrufe.
ja da magst eventuell recht haben. ich habe keine zeitmessungen angestellt. wenn er daran interesse hat, sollte er das machen und sein urteil hier kund tun. könnte interessant sein.
wegen zig explode! man schreibt oft in programmen so etwas (und das zeilenweise!!!!):
echo '...';
echo '... ';
echo '...';
echo '... ';
echo '...';
etc.
na ja! (ich habe in allen meinen arbeiten eine einzige ausgabe, nämlich die ausgabe der fertigen seite.)
lg -
@metalmachine: Ich würde den Code noch wie folgt abändern:
^(\w+)(?: - (\w+) ([\d*])/([\d*]))?(?:, (\w+) \((\d)\))?$
Damit fallen die Matches 2 und 6 selbst weg, du musst sie nicht extra auslassen ;) Dafür sind die Non-Capturing-Groups ja da ^^ -
Ui da is ja rege beteiligung!!! Das freut mich, dass ich so eine schöne Diskussion lostreten konnte.
Testen werde ich die Regexe (und die Non-Regex Methode) beide gleich, weil mich der Performanceunterschied wirklich interessiert (und in nem Edit präsentieren). Für die Anwendung is es egal, da das Ganze auf nem localen System läuft und ne onetimegeschichte wird.
Und für die Verfolger des Ganzen werde ich im Folgenden ma versuchen die Unterschiede der Regexe herauszuarbeiten.
^ wie $ also match auf Start und ende werden wegfallen, da das ganze in nem größeren Kontext steht. (\w*) -?(\w*) ([0-9\*])/?([0-9 \*]) , (\w*) \(?\(d?) \)? ^(\w+) ( - (\w+) ([\d \*])/ ([\d \*]))? (, (\w+) \( (\d)\))?$ ^(\w+)(?: - (\w+) ([\d *])/ ([\d *]))?(?:, (\w+) \( (\d)\))?$ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ | | | | | | | | | | | | | | | | | | | | | Das erste ? ist der ")" gewidmet während die beiden Folgenden | | | | | | | | | | dem Kompletten letzten Block ", (\w+) \( (\d)\)" betreffen | | | | | | | | | Da hab ich den "\" wergessen | | | | | | | | | und mein "?" ist nötig da es keinen Block gibt | | | | | | | | Selbe Sache mit dem "?" wie oben | | | | | | | Ich brauche ein "*" aus dem selben wie oben | | | | | | Einmal Blockdeklarierung und einmal "Nicht-in-Matches (?:)" zusätzlich | | | | | Selbes Thema mit dem Block | | | | "*" wird in na Eckigen nicht escaped | | | "\d" is natürlich wesentlich eleganter | | s.o. | s.o. gilt auch für das nachfolgende "+" is natürlich richtig da am Anfang immer was steht
Dan bis glaich mit den Performance ergebnissen!
EDIT:
So, leider erstmal die ernüchternde nachricht:
Er matcht zwar super aber nicht auf "Land, " was mir unverständlich is.
Eine weitere art von Text habe ich noch gefunden, hier is das zweite "," das Problem "Kreatur - Barbar, Zombie 2/2, BR (2)"
Rauskommen soll "Barbar, Zombie" die erweiterung des Regex um (\w+)(?: - ([\w,]+) ([\d*])/([\d*]))?,(?: (\w+) \((\d)\))? half leider nicht, da bräuchte ich nochma eure Hilfe.
Die Explode-Methode ist hier natürlich problematischer mit zwei Seperators wobei einer keiner is...
EDIT 2:
Das Problem mit "Kreatur - Barbar, Zombie 2/2, BR (2)" ist gelöst es musste natürlich auchd as Whitespace mit einbezogen werden.
Aktuelles regex: (\w+)(?: - ([\w\s,]+) ([\d*])/([\d*]))?,(?: (\w+) \((\d)\))?
Das "Land, " Problembesteht immernoch.
Und um die Performancefreunde zu befriedigen das ergebnis bei ca 350 Datensätzen über 10 durchgänge gemittelt lautet:
0.02903429508202 s mit 62% CPU für die Regex
0.08286313056945 s mit 89% CPU für Explode
Interessant würde ich sagen!
Beitrag zuletzt geändert: 9.1.2011 21:12:17 von theuntouchables -
Es sind immer die einfachen Fehler, die man ewig sucht - du hast bei deinem Regex den Beistrich quasi "statisch" eingebaut; ein Fragezeichen löste das Problem:
Damit wird nun schließlich auch das "Land" wieder gefunden.(\w+)(?: - ([\w\s,]+) ([\d*])/([\d*]))?,?(?: (\w+) \((\d)\))?
Danke nikic für den Hinweis auf die Non-Capturing-Groups, damit hab ich mal wieder was dazugelernt .
Das mit den Performancetests find ich auch echt interessant - hätte nicht gedacht, dass man das dann wirklich so deutlich sieht -
Das mit der Performance kann ich bestätigen, ich hab das ganze mit jeweils 100.000 Durchläufen auf der Konsole getestet und folgendes Ergebnis erhalten:
Außerdem habe ich das dann nochmal mit Perl nachgetestet und da hat sich das Ergebnis bestätigt:user@computer:~/www/php/lima-city$ php regex-performance-test.php runtime (reines PHP): 0.98900604248047 sec/100000 runs user@computer:~/www/php/lima-city$ php regex-performance-test.php runtime (RegExp): 0.24291586875916 sec/100000 runs
user@computer:~/www/php/lima-city$ perl regex-performance-test.pl runtime (reines Perl): 0.702342987060547 sec/100000 runs user@computer:~/www/php/lima-city$ perl regex-performance-test.pl runtime (RegExp): 0.144210815429688 sec/100000 runs
Abschließend sind hier noch mal die Snippets, damit ihr die Tests auch nachvollziehen könnt.
PHP:<?php $regex = 0; /* provisional solution for data */ $data = array( /* [...] 100.000 Einträge [...] */ ); $l = count($data); $s = microtime(true); /* extracting numbers */ $cnt = 0; foreach ($data as $row => $text) { if($regex){ preg_match("/(\w+)(?: -? (\w+) ([\d *])\/ ([\d *]))?(?:, (\w+) \( (\d)\))?/",$data[$cnt]); }else{ if (count($row_slash = explode('/', $text)) > 1) { $num[$cnt] = array( 'slash_left' => end($expl = explode(' ', $row_slash[0])) + 0, 'slash_right' => reset($expl) + 0 ); } if (count($row_parenthesis = explode('(', $text)) > 1) { $parenthesis_num = end($row_parenthesis) + 0; $num[$cnt]['parenthesis'] = $parenthesis_num; } } $cnt++; } $e = microtime(true); echo 'runtime ('.($regex ? 'RegExp' : 'reines PHP').'): '.($e-$s)." sec/$l runs\n";
Perl:#!/usr/bin/perla use strict; use Time::HiRes qw(gettimeofday); my $regex = 0; # provisional solution for data my @data = ( # [...] 100.000 Einträge [...] ); my $l = scalar @data; my $s = microtime(1); # extracting numbers my $cnt = 0; my $text; my @num = (); foreach $text(@data) { if($regex){ $text =~ m/(\w+)(?: -? (\w+) ([\d *])\/ ([\d *]))?(?:, (\w+) \( (\d)\))?/; }else{ my %hash = (); if((scalar (my @row_slash = split(/\//, $text))) > 1){ my @expl = split(/ /, $row_slash[0]); %hash = ( 'slash_left' => $expl[-1]+0, 'slash_right' => $expl[0]+0 ); } if((scalar (my @row_parenthesis = split(/\(/, $text))) > 1){ $hash{'slash_left'} = ($row_parenthesis[-1]+0); } $num[$cnt] = %hash; } $cnt++; } my $e = microtime(1); print 'runtime ('.($regex ? 'RegExp' : 'reines Perl').'): '.($e-$s)." sec/$l runs\n"; sub microtime{ my $asFloat = 0; my $microtime = '';; if(@_){ $asFloat = shift; } (my $epochseconds, my $microseconds) = gettimeofday; if($asFloat){ while(length("$microseconds") < 6){ $microseconds = "0$microseconds"; } $microtime = "$epochseconds.$microseconds"; }else{ $microtime = "$epochseconds.$microseconds"; } return $microtime; }
-
Sehr schön wenn meine ergebnisse nochmal verifiziert wurden!!!
Ich finds aber schon krass das Regex so performant sind, hätte ich echt nicht gedacht (interne Notiz: öfters Regex verwenden).
Mal n kleines Update:
Folgende neue Strings wurden entdeckt
"Land, " GELÖST Ganz wichtig das Leerzeichen am Ende, das war das Problem
"Irgendwas Land, " GELÖST Hier das Leerzeichen vor dem ","
"Artefaktkreatur 4/4, 7 (7)" UNGELÖST Das ding fällt halt ma völlig ausm Muster (warum können sich die Blöden entwickler ned an eine Muster halten?! )
Und der Regex sieht im Moment so aus:
([\w\s-]+)(?: - ([\w\s,]+) ([\d*])/([\d*]))?,\s?(?: (\w+) \((\d)\))?
@metalmachine:
Jop das "," ist und SOLL statisch eingebaut sein, da ja IMMER eins kommt...
Beitrag zuletzt geändert: 10.1.2011 13:28:27 von theuntouchables -
Und wieder ist der kleine Regex ein Stück gewachsen:
Damit konnte ich folgende Zeichenketten erfolgreich zerlegen:(\w+)(?:(?: - ([\w, ]+))? ([\d*])/([\d*]))?,(?: (\w+) \((\d)\))?
"Kreatur - Egel 1/3, W (1)",
"Land,",
"Hexerei, 2W (3)",
"Kreatur - Legende */*, 4GGG (7)",
"Kreatur - Barbar, Zombie 2/2, BR (2)",
"Artefaktkreatur 4/4, 7 (7)"
Die Gruppe ([\w\s-]+) habe ich wieder vereinfacht, da mir das so nicht sehr zweckmäßig erscheint und in ([\w\s,]+) habe ich \s durch ein einfaches Leerzeichen ersetzt, da \s auch Zeilenumbrüche entahlten kann, was eher kontraproduktiev ist, wenn man in einem längerem Text sucht.
@metalmachine:
Das hatte ich etwas missverstanden...
Jop das "," ist und SOLL statisch eingebaut sein, da ja IMMER eins kommt... -
Diskutiere mit und stelle Fragen: Jetzt kostenlos anmelden!
lima-city: Gratis werbefreier Webspace für deine eigene Homepage