Avatar billede Droa Seniormester
08. august 2013 - 20:20 Der er 15 kommentarer og
1 løsning

Parse en string til flere variabler med split regex m.m

Jeg har lavet denne kode, den skulle meget gerne forklare hvad jeg prøver at gøre.. problemet er bare at koden ser meget "vild" ud i forhold til det den skal.. og jeg undrede mig om der var en letter måde at gøre dette på, samt måske en god guide til at gøre det.
Jeg står og skal gøre dette ved mange forskellige typer strenge, med forskellige formateringer.. og ville gerne lære at gøre det korrekt, så jeg ikke skal ændre for meget.

Ligenu bruger jeg regex, da jeg ikke kunne overkomme og se andre måder at gøre det på, men jeg byder alle former for kodning velkommen :)

CommandUpdate.java
[div]
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class CommandUpdate {
    private List<ParamSingle> parms;

    public CommandUpdate(String commandstring) {
        parms = generateParms(splitParm(commandstring));
    }
   
    public ParamSingle[] getParms(){
        return parms.toArray(new ParamSingle[]{});
    }

    // ****************************************************************//
    // Class Tools
    // ****************************************************************//
    private String[] splitParm(String text) {
        String[] textarr = text.split(" ");
        List<String> parms = new ArrayList<>();
        parms.add(textarr[0]); // UPDATE
        parms.add(textarr[1]); // 12.7543.81

        for (int i = 2; i < textarr.length; i++) {
            if (textarr[i].matches("^([A-Za-z]+)=([\\S ]+)$")) {
                parms.add(textarr[i]);
            } else {
                parms.set(parms.size() - 1, parms.get(parms.size() - 1) + " "
                        + textarr[i]);
            }
        }
        return parms.toArray(new String[] {});
    }

    private List<ParamSingle> generateParms(String[] parmstrings) {
        List<ParamSingle> parms = new ArrayList<>();

        Pattern pv1 = Pattern.compile("^([A-Za-z]+)=([0-9]+)$");
        Pattern pv2 = Pattern.compile("^([\\S]+)=([\\S ]+) \\(([\\S ]+)\\)$");
        Pattern pv3 = Pattern.compile("^([A-Za-z]+)=\"([\\S ]+)\" \\[([\\S ]+)\\] \\(([0-9\\.]+)\\)");
        Matcher match;

        for (String parmstring : parmstrings) {
            if ((match = pv1.matcher(parmstring)).find()) {
                parms.add(new Param1Value<Integer>(match.group(1), Integer.parseInt(match.group(2))));
            } else if ((match = pv2.matcher(parmstring)).find()) {
                parms.add(new Param2Value<String,String>(match.group(1), match.group(2),match.group(3)));
            } else if ((match = pv3.matcher(parmstring)).find()) {
                parms.add(new Param3Value<String,String,Float>(match.group(1), match.group(2),match.group(3),Float.parseFloat(match.group(4))));
            }
        }

        return parms;
    }

    // ****************************************************************//
    // Structure Classes
    // ****************************************************************//

    public class Param3Value<T, T2, T3> extends Param2Value<T, T2> {
        private T3 value3;

        public Param3Value(String name, T value, T2 value2, T3 value3) {
            super(name, value, value2);
            setValue3(value3);
        }

        public T3 getValue3() {
            return this.value3;
        }

        public void setValue3(T3 value3) {
            this.value3 = value3;
        }
    }

    public class Param2Value<T, T2> extends Param1Value<T> {
        private T2 value2;

        public Param2Value(String name, T value, T2 value2) {
            super(name, value);
            setValue2(value2);
        }

        public T2 getValue2() {
            return this.value2;
        }

        public void setValue2(T2 value2) {
            this.value2 = value2;
        }
    }

    public class Param1Value<T> extends ParamSingle {
        private T value;

        public Param1Value(String name, T value) {
            super(name);
            setValue(value);
        }

        public T getValue() {
            return this.value;
        }

        public void setValue(T value) {
            this.value = value;
        }
    }

    public class ParamSingle {

        private String name;

        public ParamSingle(String name) {
            setName(name);
        }

        public String getName() {
            return this.name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
[/Div]

Test.java
[Div]
public class Test {

    final static String[] lines = new String[] {
            "UPDATE 12.7543.81 ParmA=\"dk.asterix.org\" [Denmark] (9.1) ParmB=\"de.asterix.org\" [Germany] (1.0) Called=49xxxxxxx (GBH) Calling=45xxxxxxxx (LOA) Internal=0 State=2",
            "UPDATE 12.7543.81 ParmB=\"de.asterix.org\" [Germany] (1.0) ParmA=\"dk.asterix.org\" [Denmark] (9.1) Called=49xxxxxxx (GBH) Calling=45xxxxxxxx (LOA) Internal=0 State=2",
            "UPDATE 12.7543.81 Internal=0 State=2 ParmB=\"de.asterix.org\" [Germany] (1.0) ParmA=\"dk.asterix.org\" [Denmark] (9.1) Called=49xxxxxxx (GBH) Calling=45xxxxxxxx (LOA)",
            "UPDATE 12.7543.81 Internal=0 State=2 ParmB=\"de.asterix.org\" [Germany] (1.0) Calling=90xxxxxx (LOA) ParmA=\"dk.asterix.org\" [Denmark] (9.1) Called=49xxxxxxx (GBH)" };

    @SuppressWarnings("unchecked")
    public static void main(String[] args) {

        CommandUpdate[] cus = new CommandUpdate[] {
                new CommandUpdate(lines[0]), new CommandUpdate(lines[1]),
                new CommandUpdate(lines[2]), new CommandUpdate(lines[3]) };

        for (CommandUpdate cu : cus) {
            for (CommandUpdate.ParamSingle ps : cu.getParms()) {
                if (ps instanceof CommandUpdate.Param3Value) {
                    CommandUpdate.Param3Value<String,String,Float> d = (CommandUpdate.Param3Value<String,String,Float>) ps;
                    System.out.println("Data: " + d.getName() + " Value:"
                            + d.getValue() + " Value2:" + d.getValue2()
                            + " Value3:" + d.getValue3());
                } else if (ps instanceof CommandUpdate.Param2Value) {
                    CommandUpdate.Param2Value<String,String> d = (CommandUpdate.Param2Value<String, String>) ps;
                    System.out.println("Data: " + d.getName() + " Value:"
                            + d.getValue() + " Value2:" + d.getValue2());
                } else if (ps instanceof CommandUpdate.Param1Value) {
                    CommandUpdate.Param1Value<Integer> d = (CommandUpdate.Param1Value<Integer>) ps;
                    System.out.println("Data: " + d.getName() + " Value:"
                            + d.getValue());
                }
            }
        }
    }
}
[/Div]
Avatar billede arne_v Ekspert
09. august 2013 - 03:28 #1
Jeg tror ikke at koden kan laves super simpel.

Jeg ville lave den lidt anderledes, men smag og behag.
Avatar billede arne_v Ekspert
09. august 2013 - 03:29 #2

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

public class CommandUpdate {
    private List<Param> params;
    private static Pattern parampat = Pattern.compile("([A-Za-z]+)=(.+?)(?=(([ ]*[A-Za-z]+=)|$))");
    private static Pattern valueI = Pattern.compile("(\\d+)");
    private static Pattern valueSS = Pattern.compile("(\\S+) \\((\\S+)\\)");
    private static Pattern valueSSD = Pattern.compile("\"(\\S+)\" \\[(\\S+)\\] \\(([\\d.]+)\\)");
    private Param createParam(String name, String allvalues) {
        Matcher m1, m2, m3;
        if((m1 = valueI.matcher(allvalues)).matches()) {
            return new Param1Value<Integer>(name, Integer.parseInt(m1.group(1)));
        } else if((m2 = valueSS.matcher(allvalues)).matches()) {
            return new Param2Value<String, String>(name, m2.group(1), m2.group(2));
        } else if((m3 = valueSSD.matcher(allvalues)).matches()) {
            return new Param3Value<String, String, Double>(name, m3.group(1), m3.group(2), Double.parseDouble(m3.group(3)));
        } else {
            throw new RuntimeException("Unknown parameter type: |" + allvalues + "|");
        }
    }
    public CommandUpdate(String commandstring) {
        params = new ArrayList<Param>();
        Matcher m = parampat.matcher(commandstring);
        while(m.find()) {
            params.add(createParam(m.group(1), m.group(2)));
        }
    }
    public List<Param> getParams() {
        return params;
    }
    public static class Param3Value<T1, T2, T3> extends Param {
        private T1 value1;
        private T2 value2;
        private T3 value3;
        public Param3Value(String name, T1 value1, T2 value2, T3 value3) {
            super(name);
            this.value1 = value1;
            this.value2 = value2;
            this.value3 = value3;
        }
        public T1 getValue1() {
            return value1;
        }
        public T2 getValue2() {
            return value2;
        }
        public T3 getValue3() {
            return value3;
        }
        public String getAllValues() {
            return "{ " + value1 + ", " + value2 + ", " + value3 + " }";
        }
    }
    public static class Param2Value<T1, T2> extends Param {
        private T1 value1;
        private T2 value2;
        public Param2Value(String name, T1 value1, T2 value2) {
            super(name);
            this.value1 = value1;
            this.value2 = value2;
        }
        public T1 getValue1() {
            return value1;
        }
        public T2 getValue2() {
            return value2;
        }
        public String getAllValues() {
            return "{ " + value1 + ", " + value2 + " }";
        }
    }
    public static class Param1Value<T1> extends Param {
        private T1 value1;
        public Param1Value(String name, T1 value1) {
            super(name);
            this.value1 = value1;
        }
        public T1 getValue1() {
            return value1;
        }
        public String getAllValues() {
            return "{ " + value1 + " }";
        }
    }
    public static abstract class Param {
        private String name;
        public Param(String name) {
            this.name = name;
        }
        public String getName() {
            return name;
        }
        public abstract String getAllValues();
        @Override
        public String toString() {
            return "{ name=" + name + ", value=" + getAllValues() + " }";
        }
    }
}



public class Test {
    private static final String[] lines = new String[] {
        "UPDATE 12.7543.81 ParmA=\"dk.asterix.org\" [Denmark] (9.1) ParmB=\"de.asterix.org\" [Germany] (1.0) Called=49xxxxxxx (GBH) Calling=45xxxxxxxx (LOA) Internal=0 State=2",
        "UPDATE 12.7543.81 ParmB=\"de.asterix.org\" [Germany] (1.0) ParmA=\"dk.asterix.org\" [Denmark] (9.1) Called=49xxxxxxx (GBH) Calling=45xxxxxxxx (LOA) Internal=0 State=2",
        "UPDATE 12.7543.81 Internal=0 State=2 ParmB=\"de.asterix.org\" [Germany] (1.0) ParmA=\"dk.asterix.org\" [Denmark] (9.1) Called=49xxxxxxx (GBH) Calling=45xxxxxxxx (LOA)",
        "UPDATE 12.7543.81 Internal=0 State=2 ParmB=\"de.asterix.org\" [Germany] (1.0) Calling=90xxxxxx (LOA) ParmA=\"dk.asterix.org\" [Denmark] (9.1) Called=49xxxxxxx (GBH)"
    };
    public static void main(String[] args) {
        for (String line : lines) {
            System.out.println(line);
            CommandUpdate cu = new CommandUpdate(line);
            for (CommandUpdate.Param p : cu.getParams()) {
                System.out.println(p);
            }
        }
    }
}
Avatar billede Droa Seniormester
09. august 2013 - 08:16 #3
Din kode er helt sikkert min favorit, nu er jeg ikke den bedste til regex, da jeg næsten aldrig har brugt det førhen.
men kan godt se ideen i dine ændringer.

mine bekymringer ligger stadig i at lave de underliggende struktur classer, da jeg gerne skulle have nem adgang til værdierne.
ikke mange af de tekstfalder har samme formatering, og data'erne har det med at stå på mange forskellige måder, efter hvad det er for nogen data man udtrækker.

så ville de istedet være en ide at lave CommandUpdate medlem af et interfae kaldet Command, og påsætte Paramdataerne direkte som variabler på CommandUpdate?
eller ville det være en form for dårlig kodning?
Avatar billede MadsHaupt Juniormester
09. august 2013 - 17:56 #4
Jeg bruger kun et interface når jeg for eksempel skal have nole skjulte funktioner i klassen, som bliver synlige når man konverterer det til et interface, eller hvis man for eksempel skal have nole funktioner det hedder nået andet når man konverterer det til et interface, eller hvis man for eksempel skal bruge funktionerne i flere klasser.
Avatar billede arne_v Ekspert
12. august 2013 - 04:46 #5
1) Muligvis skal der klistres ^$ paa nogle af mine regex.

2) Jeg kan ikke se at det ville vaere nemmere med properties paa command klassen fremfor Param instanser - tvaertimod.

3) Den klassiske maade at faa fuld fleksibilitet mod at give afkald paa type sikkerhed er at bruge en Map<> fremfor properties.

4) Men maaske ville en abstrakt command basis klasse med en sub klasser for hver command hvor subklasserne har direkte properties vaere et muligt design.
Avatar billede Droa Seniormester
12. august 2013 - 11:00 #6
jeg er fuldt inde for generiske klasser, men som min lære altid sagde "KIS" Keep it simple...
og nu står jeg måske der os skal vælge dynamik fra, for at holde det simpelt.. men jeg vil os gerne holde mine regex statements sammen.. og der er det nok dumt at lave extended klasser.

jeg er i tvivl..
Avatar billede arne_v Ekspert
13. august 2013 - 03:25 #7
Du kunne dynamisk loade en instans af alle factory klasser, hvor en factory klasse har to metoder:
- isInputInMatchingFormat
- createCommand
Avatar billede Droa Seniormester
13. august 2013 - 11:40 #8
haha.. det var lige det jeg sad og var gået igang med :D
Jeg har så valgt at lave det i et observer design, da man umuligt kan tjekke om der er overliggende factories. på den måde kan man køre flere factories pr Streng :)

men mange tak for ideen.. kan se den kom klokken 3 i nat, og jeg fik den selv først i morges :)

hvis du ligger et svar, så kommer der nogen point din vej :)
Avatar billede arne_v Ekspert
13. august 2013 - 14:06 #9
Nu er jeg jo 6 timer bagefter dansk tid ....
Avatar billede arne_v Ekspert
13. august 2013 - 19:16 #10
svar
Avatar billede arne_v Ekspert
13. august 2013 - 21:31 #11
og jeg forstaar ikke helt den med observer
Avatar billede arne_v Ekspert
14. august 2013 - 04:24 #12
Teknikken i #7 er ret simpel med DI f.eks. Spring.
Avatar billede Droa Seniormester
14. august 2013 - 09:25 #13
mit design har en Listener, som kigger alle indkommende strenge igennem.

det bliver tjekket med methoden boolean canHandle(Packet pck)

over alle registreret handle interfaces, hvis der ikke er nogen der retunere True, intet bliver gjort, men hvis flere retunere True, alle bliver behandlet.

på den måde kan jeg både have 0 og mange True's i canHandle
Avatar billede arne_v Ekspert
15. august 2013 - 04:04 #14
CommandUpdate.java:


package dyndi;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import dyndi.params.Param;
import dyndi.params.ParamFactory;

@Component
public class CommandUpdate {
    private static Pattern parampat = Pattern.compile("([A-Za-z]+)=(.+?)(?=(([ ]*[A-Za-z]+=)|$))");
    @Autowired
    private ParamFactory[] pflist;
    private List<Param> params;
    public void init(String commandstring) {
        params = new ArrayList<Param>();
        Matcher m = parampat.matcher(commandstring);
        while(m.find()) {
            for(ParamFactory pf : pflist) {
                pf.init(m.group(2));
                if(pf.isMatch()) {
                    params.add(pf.createParam(m.group(1)));
                }
            }
        }
    }
    public List<Param> getParams() {
        return params;
    }
}


Param.java:


package dyndi.params;

public abstract class Param {
    private String name;
    public Param(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public abstract String getAllValues();
    @Override
    public String toString() {
        return "{ name=" + name + ", value=" + getAllValues() + " }";
    }
}


Param1Value.java:


package dyndi.params;


public class Param1Value<T1> extends Param {
    private T1 value1;
    public Param1Value(String name, T1 value1) {
        super(name);
        this.value1 = value1;
    }
    public T1 getValue1() {
        return value1;
    }
    public String getAllValues() {
        return "{ " + value1 + " }";
    }
}


Param2Value.java:


package dyndi.params;


public class Param2Value<T1, T2> extends Param {
    private T1 value1;
    private T2 value2;
    public Param2Value(String name, T1 value1, T2 value2) {
        super(name);
        this.value1 = value1;
        this.value2 = value2;
    }
    public T1 getValue1() {
        return value1;
    }
    public T2 getValue2() {
        return value2;
    }
    public String getAllValues() {
        return "{ " + value1 + ", " + value2 + " }";
    }
}


Param3Value.java:


package dyndi.params;


public class Param3Value<T1, T2, T3> extends Param {
    private T1 value1;
    private T2 value2;
    private T3 value3;
    public Param3Value(String name, T1 value1, T2 value2, T3 value3) {
        super(name);
        this.value1 = value1;
        this.value2 = value2;
        this.value3 = value3;
    }
    public T1 getValue1() {
        return value1;
    }
    public T2 getValue2() {
        return value2;
    }
    public T3 getValue3() {
        return value3;
    }
    public String getAllValues() {
        return "{ " + value1 + ", " + value2 + ", " + value3 + " }";
    }
}


ParamFactory.java:


package dyndi.params;

public interface ParamFactory {
    public void init(String allvalues);
    public boolean isMatch();
    public Param createParam(String name);
}


ParamIFactory.java:


package dyndi.params;

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

import org.springframework.stereotype.Component;

@Component
public class ParamIFactory implements ParamFactory {
    private static Pattern valueI = Pattern.compile("(\\d+)");
    private Matcher m;
    public void init(String allvalues) {
        m = valueI.matcher(allvalues);
    }
    public boolean isMatch() {
        return m.matches();
    }
    public Param createParam(String name) {
        return new Param1Value<Integer>(name, Integer.parseInt(m.group(1)));
    }
}


ParamSSDFactory.java:


package dyndi.params;

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

import org.springframework.stereotype.Component;

@Component
public class ParamSSDFactory implements ParamFactory {
    private static Pattern valueSSD = Pattern.compile("\"(\\S+)\" \\[(\\S+)\\] \\(([\\d.]+)\\)");
    private Matcher m;
    public void init(String allvalues) {
        m = valueSSD.matcher(allvalues);
    }
    public boolean isMatch() {
        return m.matches();
    }
    public Param createParam(String name) {
        return new Param3Value<String, String, Double>(name, m.group(1), m.group(2), Double.parseDouble(m.group(3)));
    }
}


ParamSSFactory.java:


package dyndi.params;

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

import org.springframework.stereotype.Component;

@Component
public class ParamSSFactory implements ParamFactory {
    private static Pattern valueSS = Pattern.compile("(\\S+) \\((\\S+)\\)");
    private Matcher m;
    public void init(String allvalues) {
        m = valueSS.matcher(allvalues);
    }
    public boolean isMatch() {
        return m.matches();
    }
    public Param createParam(String name) {
        return new Param2Value<String, String>(name, m.group(1), m.group(2));
    }
}


Test.java:


package dyndi;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import dyndi.params.Param;

public class Test {
    private static final String[] lines = new String[] {
        "UPDATE 12.7543.81 ParmA=\"dk.asterix.org\" [Denmark] (9.1) ParmB=\"de.asterix.org\" [Germany] (1.0) Called=49xxxxxxx (GBH) Calling=45xxxxxxxx (LOA) Internal=0 State=2",
        "UPDATE 12.7543.81 ParmB=\"de.asterix.org\" [Germany] (1.0) ParmA=\"dk.asterix.org\" [Denmark] (9.1) Called=49xxxxxxx (GBH) Calling=45xxxxxxxx (LOA) Internal=0 State=2",
        "UPDATE 12.7543.81 Internal=0 State=2 ParmB=\"de.asterix.org\" [Germany] (1.0) ParmA=\"dk.asterix.org\" [Denmark] (9.1) Called=49xxxxxxx (GBH) Calling=45xxxxxxxx (LOA)",
        "UPDATE 12.7543.81 Internal=0 State=2 ParmB=\"de.asterix.org\" [Germany] (1.0) Calling=90xxxxxx (LOA) ParmA=\"dk.asterix.org\" [Denmark] (9.1) Called=49xxxxxxx (GBH)"
    };
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("config.xml");
        for (String line : lines) {
            System.out.println(line);
            CommandUpdate cu = ctx.getBean(CommandUpdate.class);
            cu.init(line);
            for (Param p : cu.getParams()) {
                System.out.println(p);
            }
        }
    }
}
Avatar billede Droa Seniormester
15. august 2013 - 09:51 #15
det er nogenlunde det samme jeg fik lavet, ud over jeg så returnere et array istedet for kun et element, da mit factory kan bruge flere klasser på samme data, som så os betyder flere resultater :)
Avatar billede arne_v Ekspert
15. august 2013 - 14:57 #16
En af fordelene ved DI/Spring er at naar man skal tilfoeje et ny patter, saa laver man params og factory klasse, builder, pakker i en jar file og tilfoejer jar fil til classpath. Ingen rettelser i resten af programmet.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester