Ultimamente mi sono imbattuto nella elaborazione di dati statistici. Il problema principale è stato quello di riuscire a estrarre dai files di log di diverse applicazioni solo le informazioni che mi interessavano. Avevo due scelte: scrivere del codice specifico per ogni singola applicazione oppure scrivere del codice generico che utilizza le espressioni regolari per estrarre le informazioni.
Un esercizio molto bello è stato quello di riuscire a estrarre le informazioni contenute nel più famoso dei log, l'access.log di apache che generalmente usa il formato NSCA Log Format.
Un esempio è questo:
125.125.125.125 - dsmith [10/Oct/1999:21:15:05 +0500] "GET /index.html HTTP/1.0" 200 1043Ho trovato questo documento dell'IBM che descrive i singoli componenti del log.
Con le espressioni regolari ho però trovato un grosso limite. Il raggruppamento è posizionale e non è possibile identificare un gruppo dal nome. Quindi se le posizioni delle informazioni all'interno dei vari log cambiano, sarà necessario sapere esattamente la posizione del campo che si vuole estrarre per ogni tipo di log.
Io ho esteso la sintassi delle regular expression in modo da dichiarare un nome per un dato gruppo. Visto che il gruppo si definisce con le parentesi tonde, io ho immaginato uno scenario del tipo:
...(...)...(...)...vs.
...({gruppo1}...)...({gruppoN}...)...In questo modo posso pensare di utilizzare un'etichetta che mi identifica la posizione del gruppo all'interno della regular expression.
Uno strumento molto utile per testare le mie espressioni regolari è stato questo: Regular Expression Hilighting. Io l'ho utilizzato come plugin di eclipse ma è disponibile anche la versione applet fruibile online.
Dopo tutto questo pensare alla fine è uscita questa classe Java molto semplice.
/** * @author Diego Vicentini (digolo) [digolo-gmail.com] */ import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; public class RegExTester { // Pattern per il calcolo del numero dei gruppi private Pattern groupPattern = Pattern.compile("\\((.*?)\\)"); // Pattern per l'estrazione del nome campo private Pattern fieldnamePattern = Pattern.compile("\\(\\{(.*?)\\}.*?\\)"); // Pattern per l'estrazione dei flag che non sono da considerare gruppi // non è previsto il flag commento (?x) private Pattern flagPattern = Pattern.compile("(\\(\\?i\\)|\\(\\?d\\)|\\(\\?m\\)|\\(\\?s\\)|\\(\\?u\\))"); private MapIl risultato ottenuto è:fieldMap = new HashMap (); private Pattern pattern; public RegExTester(String rawPattern) { // Inizializzo il nuovo pattern StringBuffer cleanPattern = new StringBuffer(); Matcher matcher = groupPattern.matcher(rawPattern); int i = 1; int start = 0; while (matcher.find()) { String matchGroup = matcher.group(); Matcher flagMatcher = flagPattern.matcher(matchGroup); if (!flagMatcher.find()) { Matcher fieldNameMatcher = fieldnamePattern.matcher(matchGroup); if (fieldNameMatcher.find()) { // Aggiungo il campo alla mappa fieldMap.put(fieldNameMatcher.group(1), i); // Aggiungo il pattern senza il nome campo cleanPattern.append(rawPattern.substring(start, matcher.start())); cleanPattern.append("("); cleanPattern.append(matchGroup.substring(3 + fieldNameMatcher.group(1).length())); // Mi posiziono alla fine del match start = matcher.end(); } i++; } } cleanPattern.append(rawPattern.substring(start)); // Preparo il pattern applicativo pattern = Pattern.compile(cleanPattern.toString()); } public Pattern getPattern() { return pattern; } public String group(Matcher matcher, String name) { return matcher.group(fieldMap.get(name)); } public static void main(String[] args) { // La mia RegEx per estrarre IP e data dal log String ncsaPattern = "(?i)({ip}\\b(?:\\d{1,3}.){3}\\d{1,3}) - ({user}\\w+)* \\[({date}.*)\\] \"({method}.*?)\\s({url}.*) ({protocol}.*?)\" ({status}\\d{3}) ({bytes}\\d+)"; String logLine = "125.125.125.125 - dsmith [10/Oct/1999:21:15:05 +0500] \"GET /index.html HTTP/1.0\" 200 1043"; RegExTester tester = new RegExTester(ncsaPattern); Pattern ncsa = tester.getPattern(); Matcher matcher = ncsa.matcher(logLine); while (matcher.find()) { System.out.println("IP :" + tester.group(matcher, "ip")); System.out.println("USER :" + tester.group(matcher, "user")); System.out.println("DATE :" + tester.group(matcher, "date")); System.out.println("METHOD :" + tester.group(matcher, "method")); System.out.println("URL :" + tester.group(matcher, "url")); System.out.println("PROTOCOL :" + tester.group(matcher, "protocol")); System.out.println("STATUS :" + tester.group(matcher, "status")); System.out.println("BYTES :" + tester.group(matcher, "bytes")); } } }
IP :125.125.125.125 USER :dsmith DATE :10/Oct/1999:21:15:05 +0500 METHOD :GET URL :/index.html PROTOCOL :HTTP/1.0 STATUS :200 BYTES :1043Spero che i commenti all'interno della classe siano chiari.
0 commenti:
Posta un commento