Geschwindigkeitsproblem durch regulären Ausdruck?
Micha
- java
Hallo,
ich lese Dateien ein und prüfe die einzelnen Zeilen mit zwei regulären Ausdrücken.
Eine Zeile kann folgenden Aufbau haben:
<String><String><Double><Double><Double><Double>
wobei der letzte Double-Wert optional ist (und nun wohl das Problem ist).
Meine Strategie war nun, prüfe die Zeile auf den vollständigen Inhalt, wenn dies fehlschlägt, prüfe, ob der optionale Wert nicht dabei ist.
// Strecke mit Standardabweichung
String withRMSRegExp = new String("(\\S+).+?(\\S+).+?(-{0,1}\\d+\\.\\d+).+?(-{0,1}\\d+\\.\\d+).+?(-{0,1}\\d+\\.\\d+).+?(-{0,1}\\d+\\.\\d+)(.*)");
// Strecke ohne Standardabweichung
String withOutRMSRegExp = new String("(\\S+).+?(\\S+).+?(-{0,1}\\d+\\.\\d+).+?(-{0,1}\\d+\\.\\d+).+?(-{0,1}\\d+\\.\\d+)(.*)");
// Pruefe String auf Vollstaendigkeit
if (str.matches( withRMSRegExp )){
Pattern p = Pattern.compile( withRMSRegExp );
Matcher matcher = p.matcher( str );
if (matcher.find() && matcher.groupCount() == 7){
try {
String piId = matcher.group(1);
String pkId = matcher.group(2);
double hi = Double.parseDouble(matcher.group(3));
double hk = Double.parseDouble(matcher.group(4));
double sd = Double.parseDouble(matcher.group(5));
double err= Double.parseDouble(matcher.group(6));
}
catch(NumberFormatException err) {
// keine Strecke; uninteressant, was es sonst
// sein koennte, da in jedem Fall unbrauchbar
}
}
}
// pruefe ohne opt. Parameter
else if (str.matches( withOutRMSRegExp )){
Pattern p = Pattern.compile( withOutRMSRegExp );
Matcher matcher = p.matcher( str );
if (matcher.find() && matcher.groupCount() == 6){
// try-catch wie oben
}
}
Ich lese nun eine Datei mit 150 Zeilen ein. Alle Zeilen entsprechen dem Typ 2 (ohne opt. Parameter) Das ging eigentlich recht fix. Mit meiner "tollen" Prüfung dauert es aber nun ewig. Ich habe dann mal nur den zweiten Ausdruck stehen lassen, was die Zeit spürbar reduziert.
Meine Frage ist nun, sind reguläre Ausdrücke hier eher ungeeignet oder was bremst den Code so aus?
Ich hatte vorher die str.matches()
Bedingungen nicht drin, da ich noch annahm, dass die Bedingung if (matcher.find() && matcher.groupCount() == <int>)
zeitintensiver wäre. Scheint aber nicht so zu sein. Eine der beiden Bedingungen ist daher sicher überflüssig. Lässt sich sagen, welche die etwas flottere ist oder gibts andere Optimierungsmöglichkeiten zB mein Prüfausdruck?
Mit freundlichem Gruß
Micha
n'abend,
Meine Strategie war nun, prüfe die Zeile auf den vollständigen Inhalt, wenn dies fehlschlägt, prüfe, ob der optionale Wert nicht dabei ist.
Du machst dadurch also pro Zeile 2 RegExp-Vergleiche, wo einer eigentlich ausreichen sollte?
»(\S+).+?(\S+).+?« sollte man auch mit »((\S+).+?){2}« angeben können. Dementsprechend für die Zahlen »{(-{0,1}\d+\.\d+).+?){3,4}«, wobei das Konstrukt 3, maximal 4 Mal auftauchen darf.
»((\S+).+?){2}{(-{0,1}\d+\.\d+).+?){3,4}« ist also eine Repräsentation deines RegExp, der zum Ersten beide Fälle betrachtet und zum Zweiten kürzer und verständlicher ist. Wenn du weisst was an den stellen ».+?« konkret auftauchen darf, kannst du das auch im RegExp angeben, dürfte sich nur positiv auf die Abarbeitungszeit auswirken.
Der Absatz Groups and capturing von JavaDoc - Regular Expressions erklärt, dass es möglich ist Gruppen zu quantifizieren (der Kern des oben resultierenden RegExp).
Vielleicht macht es auch Sinn den RegExp nur einmal kompilieren zu lassen und mit dem Matcher zu arbeiten, statt String.matches( "someRegExp" ) zu benutzen. Letzteres würde den RegExp schliesslich neu kompilieren müssen und ist vermutlich nichts weiter als ein Wrapper für
Pattern p = Pattern.compile( "derRegExp" );
Matcher m = p.matcher( "deineZuMatchendeEingabe" );
return m.matches();
Ich habe mit RegExp in Java bisher nicht viel am Hut gehabt. Auch habe ich die gelieferten RegExps nicht getestet. Das ist also lediglich als Denkanstoß anzusehen.
weiterhin schönen abend...
Hallo globe,
danke für Deine Anregungen. Ich habe es wie folgt formuliert:
final static Pattern regExp = Pattern.compile( "((\\S+).+?){2,2}((-{0,1}\\d+\\.\\d+).+?){3,4}(.*)" );
Ein primäres Probleme habe ich.
Wie komme ich nun an meine Daten? Das group-Array (oder was es ist) liefert mir nicht mehr alle Einträge.
Eine Verständnisfrage habe ich auch: Warum muß ich am Ende ein (.*) anfügen? Das musste ich schon bei meinem "alten" Muster, damit es funktioniert. Meine Daten sehen zB so aus:
Alle Werte:
300 3002401902 0.000 0.000 121.9840744655 0.0003
Ohne den Optionalwert
300 3002401903 0.000 0.000 129.9405567198
Mit freundlichem Gruß
Micha
n'abend,
danke für Deine Anregungen. Ich habe es wie folgt formuliert:
final static Pattern regExp = Pattern.compile( "((\\S+).+?){2,2}((-{0,1}\\d+\\.\\d+).+?){3,4}(.*)" );
das {2,2} müsste man auch durch {2} repräsentieren können.
Wie komme ich nun an meine Daten? Das group-Array (oder was es ist) liefert mir nicht mehr alle Einträge.
Ich hab das mal kurz getestet. In der Tat führt die von mir vorgeschlagene Notation dazu, dass mit den CaptureGroups nicht mehr ordnungsgemäß (also so, wie ich das von anderen Sprachen gewohnt bin ;) arbeiten kann. »This just *must* be java«™.
Hier der Test:
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegExp
{
public static void main( String[] args )
{
// Diese Notation scheint nur für stumpfes matchen brauchbar zu sein?!
//Pattern p = Pattern.compile( "((\\S+)\\s+){2}(\\s*(-?\\d+\\.\\d+)){3,4}" );
Pattern p = Pattern.compile( "(\\S+)\\s+(\\S+)\\s+(-?\\d+\\.\\d+)\\s+(-?\\d+\\.\\d+)\\s+(-?\\d+\\.\\d+)\\s*(-?\\d+\\.\\d+)?" );
String s = "300 3002401902 0.000 0.000 121.9840744655 0.0003" + "\n"
+ "300 3002401903 0.000 0.000 129.9405567198";
Matcher m = p.matcher( s );
int c = 0;
while( m.find() )
{
// wo stehen wir denn?
System.out.println( "find() "+c+":" );
// was haben wir denn?
System.out.println( "group(): " + m.group() );
// wie viele gruppen haben wir?
System.out.println( "groupCount(): " + m.groupCount() );
// welche gruppen haben wir?
for( int i=0; i <= m.groupCount(); i++ )
System.out.println( "group("+i+"): " + m.group(i) );
System.out.println( "-----------------------------------------" );
c++;
}
}
}
Warum splittest du eigentlich nicht nach Spaces? Dadurch bekämst du ein String Array, mit welchem sich auch wunderbar weiterarbeiten ließe...
Eine Verständnisfrage habe ich auch: Warum muß ich am Ende ein (.*) anfügen? Das musste ich schon bei meinem "alten" Muster, damit es funktioniert. Meine Daten sehen zB so aus:
Ich tippe auf das LineFeed (das /(\r\n|\r|\n)/, welches die Zeile umbricht), oder auf die erzwungenen Spaces am Ende der letzten CaptureGroup.
Alle Werte:
300 3002401902 0.000 0.000 121.9840744655 0.0003
Ich sehe da Leerzeichen (respektive irgendwelche Spaces). Du solltest dein RegExp dahingehend anpassen, um den "." zu eliminieren. bspw. »((\S+)\s+){2,2}«, dann brauchst du auch das "ungreedy" (+?) nicht mehr. Ich weiss zwar nicht, ob das tatsächlich Auswirkungen auf die Performance hat, aber testen kannst du es ja mal.
* Sind die ersten beiden Werte immer Ganzzahlen? Warum scannst du dann nicht auch danach?
* Floats müssen keinen Punkt haben. Der RegExp kann das berücksichtigen: ((-?\d+(\.\d+)?)\s+){3,4}
weiterhin schönen abend...
Guten morgen globe,
das {2,2} müsste man auch durch {2} repräsentieren können.
Ja, es war in Deinem letzten Posting ein kleiner Klammernfehler drin. Da habe ich ein wenig rum probiert und bin auf die min,max-Methode gekommen.
Dein Code funktioniert, vielen Dank!
Dadurch bekämst du ein String Array, mit welchem sich auch wunderbar weiterarbeiten ließe...
Das Programm, welches die Daten verarbeitet, kommt von mir. Ich wollte dabei "maximale" Flexibilität erreichen. Eine Zeile soll nicht auf ein Trenner festgelegt werden, drum habe ich auch "beliebiges Zeichen" gewählt.
Eine Verständnisfrage habe ich auch: Warum muß ich am Ende ein (.*) anfügen?
Die Antwort kam in Deinem Code:
i <= m.groupCount();
---^
Ich war die ganze Zeit auf ein kleiner aus. Wenn ich hinten nun noch eine Gruppe definiert habe, kam ich so an den "letzten" Wert ran, den ich suchte. Anfänger pech ;-)
Ich sehe da Leerzeichen (respektive irgendwelche Spaces).
Es können auch Kommas, Semikolons oder Klammern oder ?!? sein. Den Bereich kann man aber sinnvoll eingrenzen, ja - vielleicht so:
[\\[\\]\\(\\),;\\s\\|#]+
. Der Punkt ist nur so verdammt übersichtlich ;-)
* Sind die ersten beiden Werte immer Ganzzahlen? Warum scannst du dann nicht auch danach?
Es sind Zeichenketten, die nur in meinem wohl eher schlecht gewählten Beispiel zufällig Ziffern sind. Eine Zeile ist eine Streckenmessung:
<PunktnummerStandpunkt><PunktnummerZielpunkt><HöheStd.pkt><HöheZielpkt><SchrägStrecke><(opt)Standardabweichung>
Die Punktnummer kann ich auf ([0-9A-Za-z-_]+)
begrenzen.
* Floats müssen keinen Punkt haben. Der RegExp kann das berücksichtigen: ((-?\d+(\.\d+)?)\s+){3,4}
Ein sehr guter Hinweis, werde ich berücksichtigen!
Darf ich fragen, warum Du am Ende des Ausdrucks noch ein ? gestezt hast?
Mit freundlichem Gruß
Micha
Hallo,
Darf ich fragen, warum Du am Ende des Ausdrucks noch ein ? gestezt hast?
... damit der Ausdruck optional ist, wie auch bei den Vorzeichen.
Mit freundlichem Gruß
Micha