Anmelden

View Full Version : Regex mit RPG und Java



Rainer Ross
04-11-21, 18:21
Hallo Kollegen,

ich habe ein Java-Programm, das aus einer HTML-Seite bestimmte "div" findet.
Das Pattern sieht so aus und hat drei Gruppen


(<div class=\"card\">)([\\s\\S]*?)(</div>)


Das Java-Programm sieht so aus



import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Regex_Loop_1 {
public static void main(String[] args) {
long t = System.currentTimeMillis();
String patternString = "(<div class=\"card\">)([\\s\\S]*?)(</div>)";

String text = "</div><div class=\"card\">abcdex</div><div>sdfssdf</div>" +
"<div class=\"card\">bsdfsd</div></div>";

Pattern pattern = Pattern.compile(patternString);
Matcher matcher = pattern.matcher(text);

System.out.println("Compile & Matcher " + (System.currentTimeMillis() - t) + " ms");

int count = 0;

while(matcher.find()) {
count++;
System.out.println("found: " + count + " -> " + matcher.start() + " - " + matcher.end());
System.out.println("group 1: " + matcher.group(1));
System.out.println("group 2: " + matcher.group(2));
System.out.println("group 3: " + matcher.group(3));
System.out.println("Find: " + (System.currentTimeMillis() - t) + " ms");
}
}
}


und findet zwei Elemente in jeweils 3 groups



Compile & Matcher 10 ms
found: 1 -> 6 - 36
group 1: <div class="card">
group 2: abcdex
group 3: </div>
Find: 11 ms
found: 2 -> 54 - 84
group 1: <div class="card">
group 2: bsdfsd
group 3: </div>
Find: 11 ms


Jetzt habe ich das in ein RPG implementiert und möchte dort genauso über einen Index auf die groups zugreifen.

Dazu habe ich zwei Fragen:
1) wie muss ich den Programmaufruf dcl-pr Group definieren, damit ich wie im Java- Programm den Index mitgeben kann: matcher.group(1)
2) wie muss ich den Aufruf in RPG mit Index machen

Mir ist schon klar, dass es auch mit SQL geht, aber in diesem Fall möchte ich es mit RPG & Java machen.



ctl-opt dftactgrp(*no) alloc(*teraspace);
//------------------------------------------------------------------//
// Includes //
//------------------------------------------------------------------//

/include QSYSINC/QRPGLESRC,JNI

//------------------------------------------------------------------//
// Prototypes //
//------------------------------------------------------------------//

dcl-s JAVA_String object(*JAVA:'java.lang.String');

dcl-pr newString object(*JAVA:'java.lang.String')
extproc(*JAVA:'java.lang.String':*constructor);
Value varchar(1000000) const;
end-pr;

dcl-pr getBytes varchar(65535)
extproc(*JAVA:'java.lang.String':'getBytes');
end-pr;

dcl-pr Compile object(*JAVA:'java.util.regex.Pattern')
extproc(*JAVA:'java.util.regex.Pattern':
'compile') static;
Pattern like(JAVA_String);
end-pr;

dcl-pr Matcher object(*JAVA:'java.util.regex.Matcher')
extproc(*JAVA:'java.util.regex.Pattern':'matcher') ;
String object(*JAVA:'java.lang.CharSequence');
end-pr;

dcl-pr Find ind
extproc(*JAVA:'java.util.regex.Matcher':'find');
end-pr;

dcl-pr Start int(10)
extproc(*JAVA:'java.util.regex.Matcher':'start');
end-pr;

dcl-pr End int(10)
extproc(*JAVA:'java.util.regex.Matcher':'end');
end-pr;

dcl-pr Group object(*JAVA:'java.lang.String')
extproc(*JAVA:'java.util.regex.Matcher':'group');
//xxx Index object(*JAVA:'java.lang.Integer') const;
end-pr;

dcl-s RegExPattern object(*JAVA:'java.util.regex.Pattern');
dcl-s RegExMatcher object(*JAVA:'java.util.regex.Matcher');

//------------------------------------------------------------------//
// Variablen //
//------------------------------------------------------------------//

dcl-s RegPattern like(JString);
dcl-s RegString like(JString);
dcl-s RegResult like(JString);

dcl-s LocPattern varchar(256);
dcl-s LocString varchar(256);
dcl-s LocInd int(10);
dcl-s LocResult varchar(50);
dcl-s LocGroup1 varchar(50);
dcl-s LocGroup2 varchar(50);
dcl-s LocGroup3 varchar(50);

LocPattern = '(<div class="card">)([\s\S]*?)(</div>)';

LocString = '</div><div class="card">abcdex</div>' +
'<div>sdfssdf</div>' +
'<div class="card">bsdfsd</div></div>';


RegPattern = newString(LocPattern);
RegString = newString(LocString);

RegExPattern = Compile(RegPattern);
RegExMatcher = Matcher(RegExPattern:RegString);

dow Find(RegExMatcher) = *on;
RegResult = Group(RegExMatcher);
LocResult = getBytes(RegResult);

dsply (%subst(LocResult:1:30));

LocInd += 1;
dsply ('Found: ' + %char(LocInd) + ' -> ' +
%char(Start(RegExMatcher)) + ' - ' +
%char(End(RegExMatcher)));
enddo;

*inlr = *on;



Ergebnis



DSPLY <div class="card">abcdex</div>
DSPLY Found: 1 -> 6 - 36
DSPLY <div class="card">bsdfsd</div>
DSPLY Found: 2 -> 54 - 84

Fuerchau
04-11-21, 20:52
Statt Java nimm einfach SQL, das ist einfach mit dem Aufruf.
https://www.itjungle.com/2015/05/19/fhg051915-story01/

Das gibte mit irgendeiner Installations-Option und ist schneller als den aufwändigen Umweg über Java.

Rainer Ross
05-11-21, 16:07
mein Ziel war es die Performance zwischen JAVA und den Regex SQL Statements zu testen, weil es sich bei meiner Aufgabe um eine größere Datenmenge handelt und außerdem habe ich dann mehr Verständnis für JAVA.

Mit etwas probieren habe ich selbst die Lösung gefunden, die ich euch nicht vorenthalten will



ctl-opt dftactgrp(*no) alloc(*teraspace);
//------------------------------------------------------------------//
// Includes //
//------------------------------------------------------------------//

/include QSYSINC/QRPGLESRC,JNI

//------------------------------------------------------------------//
// Prototypes //
//------------------------------------------------------------------//

dcl-s JAVA_String object(*JAVA:'java.lang.String');

dcl-pr newString object(*JAVA:'java.lang.String')
extproc(*JAVA:'java.lang.String':*constructor);
Value varchar(1000000) const;
end-pr;

dcl-pr getBytes varchar(65535)
extproc(*JAVA:'java.lang.String':'getBytes');
end-pr;

dcl-pr Compile object(*JAVA:'java.util.regex.Pattern')
extproc(*JAVA:'java.util.regex.Pattern':
'compile') static;
Pattern like(JAVA_String);
end-pr;

dcl-pr Matcher object(*JAVA:'java.util.regex.Matcher')
extproc(*JAVA:'java.util.regex.Pattern':'matcher') ;
String object(*JAVA:'java.lang.CharSequence');
end-pr;

dcl-pr Find ind
extproc(*JAVA:'java.util.regex.Matcher':'find');
end-pr;

dcl-pr Start int(10)
extproc(*JAVA:'java.util.regex.Matcher':'start');
end-pr;

dcl-pr End int(10)
extproc(*JAVA:'java.util.regex.Matcher':'end');
end-pr;

dcl-pr Group object(*JAVA:'java.lang.String')
extproc(*JAVA:'java.util.regex.Matcher':'group');
Index int(10) value;
end-pr;

dcl-s RegExPattern object(*JAVA:'java.util.regex.Pattern');
dcl-s RegExMatcher object(*JAVA:'java.util.regex.Matcher');

//------------------------------------------------------------------//
// Variables //
//------------------------------------------------------------------//

dcl-s RegPattern like(JString);
dcl-s RegString like(JString);

dcl-s LocPattern varchar(256);
dcl-s LocString varchar(256);
dcl-s LocInd int(10);

dcl-s LocGroup0 char(50);
dcl-s LocGroup1 char(50);
dcl-s LocGroup2 char(50);
dcl-s LocGroup3 char(50);

LocPattern = '(<div class="card">)([\s\S]*?)(</div>)';

LocString = '</div><div class="card">abcdex</div>' +
'<div>sdfssdf</div>' +
'<div class="card">bsdfsd</div></div>';

RegPattern = newString(LocPattern);
RegString = newString(LocString);

RegExPattern = Compile(RegPattern);
RegExMatcher = Matcher(RegExPattern:RegString);

dow Find(RegExMatcher) = *on;
LocInd += 1;
dsply ('Found: ' + %char(LocInd) + ' -> ' +
%char(Start(RegExMatcher)) + ' - ' +
%char(End(RegExMatcher)));

LocGroup0 = getBytes(Group((RegExMatcher):0));
LocGroup1 = getBytes(Group((RegExMatcher):1));
LocGroup2 = getBytes(Group((RegExMatcher):2));
LocGroup3 = getBytes(Group((RegExMatcher):3));

dsply (%subst(LocGroup0:1:30));
dsply (%subst(LocGroup1:1:30));
dsply (%subst(LocGroup2:1:30));
dsply (%subst(LocGroup3:1:30));

enddo;

*inlr = *on;



Ergebnis



DSPLY Found: 1 -> Pos 6 - 36
DSPLY <div class="card">abcdex</div>
DSPLY <div class="card">
DSPLY abcdex
DSPLY </div>
DSPLY Found: 2 -> Pos 54 - 84
DSPLY <div class="card">bsdfsd</div>
DSPLY <div class="card">
DSPLY bsdfsd
DSPLY </div>

Fuerchau
05-11-21, 17:17
Ich denke schon, dass SQL schneller ist als RPGJAVA, da die JVM und Interjob-Kommunikation benötigt wird.
Aber die Messergebnisse kannst du ja mal bereitstellen.

Rainer Ross
06-11-21, 15:57
Jetzt liegen die Meßergebnisse vor und sind sehr überraschend. Zunächst die Basisdaten für meine Messung

- IBM i Modell 914 Power 9, 2 Prozessoren, NVMe Speicher, 7.4
- HTML Datei 160 KB im IFS
- Das Ergebnis des Regex Patterns kommt in der HTML-Datei 21 Mal vor

<div class="\&quot;d-flex" justify-content-between"="">
(<div class=\"d-flex justify-content-between\">)
([\s\S]*?)(<div class=\"subtitle\">)([\s\S]*?)(</div>)
([\s\S]*?)(<div>)([\s\S]*?)(</div>)
<div class="d-flex justify-content-between">
Messergebnisse mit SQL REGEXP_SUBSTR

- 8 Sek

Messergebnisse mit JAVA

- 0,4 Sek - davon 0,3 Sek zum Laden der JVM
</div></div>

Rainer Ross
06-11-21, 16:13
Anbei das modifizierte Programm (JAVA in Copy-Strecke ausgelagert), damit es einfacher lesbar ist



ctl-opt dftactgrp(*no) alloc(*teraspace) main(main);
//------------------------------------------------------------------//
// //
// Test - Regex with JAVA //
// //
//----------------- //
// R.Ross 11.2021 * //
//------------------------------------------------------------------//
// Prototypes //
//------------------------------------------------------------------//

/include QSYSINC/QRPGLESRC,JNI
/include DEVS/QCPYSRC,REGEXJAVA

//------------------------------------------------------------------//
// Main //
//------------------------------------------------------------------//
dcl-proc main;

dcl-s LocPattern varchar(256);
dcl-s LocString varchar(256);

dcl-s LocGroup0 char(50);
dcl-s LocGroup1 char(50);
dcl-s LocGroup2 char(50);
dcl-s LocGroup3 char(50);

LocPattern = '(<div class="card">)([\s\S]*?)(</div>)';

LocString = '</div><div class="card">abcdex</div>' +
'<div>sdfssdf</div>' +
'<div class="card">bsdfsd</div></div>';

RegPattern = newString(LocPattern);
RegString = newString(LocString);

RegExMatcher = Matcher(Compile(RegPattern):RegString);

dow Find(RegExMatcher) = *on;
LocGroup0 = getBytes(Group((RegExMatcher):0));
LocGroup1 = getBytes(Group((RegExMatcher):1));
LocGroup2 = getBytes(Group((RegExMatcher):2));
LocGroup3 = getBytes(Group((RegExMatcher):3));
enddo;

end-proc;
//------------------------------------------------------------------//



und die Copy-Strecke



//------------------------------------------------------------------//
// JAVA String 1 MB //
//------------------------------------------------------------------//

dcl-pr newString like(JAVA_String)
extproc(*JAVA:'java.lang.String':*constructor);
Value varchar(1000000) const;
end-pr;

//------------------------------------------------------------------//
// Get Bytes //
//------------------------------------------------------------------//

dcl-pr getBytes varchar(100000)
extproc(*JAVA:'java.lang.String':'getBytes');
end-pr;

//------------------------------------------------------------------//
// Regex Compile //
//------------------------------------------------------------------//

dcl-pr Compile object(*JAVA:'java.util.regex.Pattern')
extproc(*JAVA:'java.util.regex.Pattern':
'compile') static;
Pattern like(JAVA_String);
end-pr;

//------------------------------------------------------------------//
// Regex Matcher //
//------------------------------------------------------------------//

dcl-pr Matcher object(*JAVA:'java.util.regex.Matcher')
extproc(*JAVA:'java.util.regex.Pattern':'matcher') ;
String object(*JAVA:'java.lang.CharSequence');
end-pr;

//------------------------------------------------------------------//
// Regex Matches //
//------------------------------------------------------------------//

dcl-pr Matches ind
extproc(*JAVA:'java.util.regex.Matcher':'matches') ;
end-pr;

//------------------------------------------------------------------//
// Regex Find //
//------------------------------------------------------------------//

dcl-pr Find ind
extproc(*JAVA:'java.util.regex.Matcher':'find');
end-pr;

//------------------------------------------------------------------//
// Regex Start Position from Find //
//------------------------------------------------------------------//

dcl-pr Start int(10)
extproc(*JAVA:'java.util.regex.Matcher':'start');
end-pr;

//------------------------------------------------------------------//
// Regex End Position from Find //
//------------------------------------------------------------------//

dcl-pr End int(10)
extproc(*JAVA:'java.util.regex.Matcher':'end');
end-pr;

//------------------------------------------------------------------//
// Regex Group //
//------------------------------------------------------------------//

dcl-pr Group object(*JAVA:'java.lang.String')
extproc(*JAVA:'java.util.regex.Matcher':'group');
Index int(10) value;
end-pr;

//------------------------------------------------------------------//
// Variables //
//------------------------------------------------------------------//

dcl-s JAVA_String object(*JAVA:'java.lang.String');

dcl-s RegExPattern object(*JAVA:'java.util.regex.Pattern');
dcl-s RegExMatcher object(*JAVA:'java.util.regex.Matcher');

dcl-s RegPattern like(JString);
dcl-s RegString like(JString);

//------------------------------------------------------------------//

Fuerchau
07-11-21, 11:42
Na, dann ist das ja eine fürterliche Implementation der Aufrufe.
Irgendwo war hier mal der native Aufruf der C-Funktionen gemacht worden.
Ggf. sind die ja noch schneller.
Das damalige Problem war nur die Umgebung per Locale-Objekt, da die API's sonst immer per CCSID 037 per Default gearbeitet haben.
Ich finde allerdings den Beitrag nicht mehr.

Rainer Ross
08-11-21, 09:42
Deine Idee mit den C-Funktionen ist gut und ich habe es zuerst damit probiert und auch dass Pattern mit iconv in die CCSID 037 convertiert.

Aber die C-Funktionen unterstützen bestimmte Patterns nicht und das habe ich nicht lösen können

Pattern:
(<div class="card">)(</div>)
funktioniert
<div class="card">
aber wenn im String zwischen <div" und="" "class"="" mehrere="" leerzeichen="" sind,="" dann="" geht="" das="" mit="" diesem
"div" und "class" mehrere Leerzeichen sind, dann muss ich mit

Pattern:
(<div+\s+class="card">)(<\/div>) arbeiten<div+\s+class="card">

und hier bekomme ich bei den C-API's kein Ergebnis</div+\s+class="card"></div">
</div>

Andreas_Prouza
08-11-21, 13:07
Hallo Rainer,

funktioniert nicht folgender Code:

(<div +class="card">)(<\/div>)
Btw wenn du div+... schreibst, dann heißt das Plus nach dem "v", dass das v 1-n mal vorkommen darf.
Hat dir bis jetzt kein Fehler geworfen, da es zufällig immer gepasst hat.

lg Andreas

Rainer Ross
08-11-21, 15:01
Hallo Andreas,
danke für den Tipp. Er funktioniert.

Die Aufgabe ist leider komplexer, denn ich habe folgenden String

</div><div class="card">asdfsadfsdf</div><div class="card">bsdfsdf</div>

und möchte die Daten zwischen den div's

Mit diesem Pattern funktioniert es mit den C-API's

(<div +class="card">)(.*?)(<\/div>)

hat aber den Haken, dass
(.*?) die LineFeeds nicht findet

Mit diesem Pattern funktioniert es - siehe www.regexr.com

(<div +class="card">)([\s\S]*?)(<\/div>)
aber
([\s\S]*?) wird von den C-API's nicht unterstützt

Viele Grüße
Rainer