Avatar billede jespersahner Nybegynder
12. november 2004 - 18:31 Der er 23 kommentarer og
1 løsning

Header-information gemt i XML-fil

Jeg har en applikation, hvor jeg læser fra en binær fil, jf.

import java.io.*;

public class Laesfil {
   
    public static void main(String[] args) throws IOException {
        File f=new File("f:/Java/Projects/Diverse/fil.dat");
        DataInputStream is = new DataInputStream(new FileInputStream(f));
        int i;
        double d;
        String s;
        i=is.readInt();
        d=is.readDouble();
        b=is.readByte();
        s=is.readUTF();
        is.close();
    }
   
}

Jeg kunne godt tænke mig at gemme header-informationen, dvs. information om felternes navne og type i en XML-fil og derefter bruge denne XML-fil til at styre indlæsningen.

Er der en, der kan angive et simpelt eksempel på dette?
Avatar billede arne_v Ekspert
12. november 2004 - 20:27 #1
Du er ikke særlig præcis me dhensyn til hvordan det skal fungere, men
her er et eksempel til inspiration.
Avatar billede arne_v Ekspert
12. november 2004 - 20:27 #2
<allfields>
  <field name="a" type="int"/>
  <field name="b" type="double"/>
</allfields>
Avatar billede arne_v Ekspert
12. november 2004 - 20:27 #3
import java.io.*;
import java.util.*;

import javax.xml.parsers.*;

import org.w3c.dom.*;
import org.xml.sax.*;

public class XmlBinFilCtrl {
    public static final int FIELD_BYTE = 1;
    public static final int FIELD_SHORT = 2;
    public static final int FIELD_INT = 3;
    public static final int FIELD_LONG = 4;
    public static final int FIELD_FLOAT = 5;
    public static final int FIELD_DOUBLE = 6;
    public static int parseType(String s) {
        if(s.equals("byte")) return FIELD_BYTE;
        if(s.equals("short")) return FIELD_SHORT;
        if(s.equals("int")) return FIELD_INT;
        if(s.equals("long")) return FIELD_LONG;
        if(s.equals("float")) return FIELD_FLOAT;
        if(s.equals("double")) return FIELD_DOUBLE;
        return -1;
    }
    public static Field[] readConfig(String xmlctrl) throws SAXException, IOException, ParserConfigurationException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(new File(xmlctrl));
        NodeList elements = doc.getElementsByTagName("field");
        Field[] res = new Field[elements.getLength()];
        int[] types = new int[elements.getLength()];
        for (int i = 0; i < elements.getLength(); i++) {
            res[i] = new Field();
            res[i].setName(elements.item(i).getAttributes().getNamedItem("name").getNodeValue());
            res[i].setType(parseType(elements.item(i).getAttributes().getNamedItem("type").getNodeValue()));
        }
        return res;
    }
    public static Map readData(DataInputStream dis, Field[] fields) throws IOException {
        Map res = new HashMap();
        for(int i = 0; i < fields.length; i++) {
            String name = fields[i].getName();
            switch(fields[i].getType()) {
            case FIELD_BYTE:
                res.put(name, new Byte(dis.readByte()));
                break;
            case FIELD_SHORT:
                res.put(name, new Short(dis.readShort()));
                break;
            case FIELD_INT:
                res.put(name, new Integer(dis.readInt()));
                break;
            case FIELD_LONG:
                res.put(name, new Long(dis.readLong()));
                break;
            case FIELD_FLOAT:
                res.put(name, new Float(dis.readFloat()));
                break;
            case FIELD_DOUBLE:
                res.put(name, new Double(dis.readDouble()));
                break;
            }
        }
        return res;
    }
    public static void writeData(Map data, DataOutputStream dos, Field[] fields) throws IOException {
        for(int i = 0; i < fields.length; i++) {
            String name = fields[i].getName();
            switch(fields[i].getType()) {
            case FIELD_BYTE:
                dos.writeByte(((Byte)data.get(name)).byteValue());
                break;
            case FIELD_SHORT:
                dos.writeShort(((Short)data.get(name)).shortValue());
                break;
            case FIELD_INT:
                dos.writeInt(((Integer)data.get(name)).intValue());
                break;
            case FIELD_LONG:
                dos.writeLong(((Long)data.get(name)).longValue());
                break;
            case FIELD_FLOAT:
                dos.writeFloat(((Float)data.get(name)).floatValue());
                break;
            case FIELD_DOUBLE:
                dos.writeDouble(((Double)data.get(name)).doubleValue());
                break;
            }
        }
    }
    public static void main(String[] args) throws Exception {
        Field[] f = readConfig("C:\\ctrl.xml");
        DataOutputStream dos = new DataOutputStream(new FileOutputStream("C:\\bin.dat"));
        Map hm1 = new HashMap();
        hm1.put("a", new Integer(123));
        hm1.put("b", new Double(123.456));
        writeData(hm1, dos, f);
        Map hm2 = new HashMap();
        hm2.put("a", new Integer(321));
        hm2.put("b", new Double(654.321));
        writeData(hm2, dos, f);
        dos.close();
        DataInputStream dis = new DataInputStream(new FileInputStream("C:\\bin.dat"));
        Map hm3 = readData(dis, f);
        System.out.println(hm3);
        Map hm4 = readData(dis, f);
        System.out.println(hm4);
        dis.close();
    }
}

class Field {
    private String name;
    private int type;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getType() {
        return type;
    }
    public void setType(int type) {
        this.type = type;
    }
}
Avatar billede arne_v Ekspert
12. november 2004 - 20:28 #4
{a=123, b=123.456}
{a=321, b=654.321}
Avatar billede arne_v Ekspert
12. november 2004 - 20:28 #5
og et svar
Avatar billede jespersahner Nybegynder
13. november 2004 - 08:53 #6
->arne_v: Det ser fint ud.

Jeg kender intet til XML men har læst dine artikler. Bør man i dette tilfælde bruge SAX eller W3C DOM? I mit tilfælde har man nok ikke som sådan brug for at kunne spole frem og tilbage, idet jeg læser header'en ind og ikke andet.
Avatar billede arne_v Ekspert
13. november 2004 - 10:50 #7
Det har du faktisk ret i. Og man kunne sagtens have parset med SAX.

Men jeg tror at DOM kræver færre linier kode her. Og der er ikke noget performance
problem som peger på SAX.
Avatar billede jespersahner Nybegynder
13. november 2004 - 12:39 #8
->arne_v: Et enkelt tillægsspm.: Når der er læst ind igen fra filen og gemt i hm3 og hm4, hvordan opretter jeg så (dynamisk) på baggrund heraf f.eks. relevante arrays:
int[] a=new int[100];
double[] b=new double[100];
osv.
- som jeg kan arbejde videre på?
Avatar billede arne_v Ekspert
13. november 2004 - 13:06 #9
Du kan omskrive readData så den læser ind i dine 2 arrays i.s.f. at returnere en Map.

Eller du kan hente ud af map og over i array bagefter.

a[ix] = ((Integer)hm3.get("a")).intValue();
b[ix] = ((Double)hm3.get("b")).doubleValue();
Avatar billede arne_v Ekspert
13. november 2004 - 13:06 #10
Hvis du er på Java 1.5/5.0 kan du spare en masse type cast ...
Avatar billede jespersahner Nybegynder
13. november 2004 - 13:10 #11
->arne_v: Forudsætter det ikke, at jeg kender navnene? - og det gør jeg først, når jeg læser dem fra header-filen.
Avatar billede jespersahner Nybegynder
13. november 2004 - 13:40 #12
->arne_v: Ville det i virkeligheden være nemmere at gemme header-informationen i et objekt, som derefter serialiseres/deserialiseres vha. (ObjectOutputStream og ObjectInputStream) i stedet for at anvende XML?
Avatar billede arne_v Ekspert
13. november 2004 - 16:04 #13
re 13:10>

Jo - det var derfor jeg valgte en map hvor man ikke behøver kende navnene på forhånd.

re 13:40>

Sagtens hvis informationen kun skal læses/skrive af programmet. Men hvis man gerne
vil kunne læse beskrivelse og måske endda rette i den med en editor, så er XML godt.
Avatar billede jespersahner Nybegynder
13. november 2004 - 17:17 #14
->arne_v
re 13:10> Men hvordan får jeg oprettet variabler svarende til indeholdet af Map'en/headeren? Kan jeg overhovedet gøre dette på run-time på en nem måde??

Jeg forestiller mig, at jeg ved brug af header-informationen indlæser en "record" fra en data-fil, hvor indholdet puttes i variabler som angivet i headeren, idet jeg derved kan arbejde videre på disse variabler i min Java-kode.

Sig lige til, hvis jeg skal oprette et nyt spm.
Avatar billede arne_v Ekspert
13. november 2004 - 17:41 #15
Man kunne godt sætte attributter på beans runtime.

Men umiddelbart tror jeg f.eks. at en HashMap af ArrayList ville give
det bedste resultat.

Men meget afhænger af hvordan du skal bruge data.
Avatar billede jespersahner Nybegynder
13. november 2004 - 18:16 #16
->arne_v: Hvordan ville du i det konkrete tilfælde oprette variablene a og b ud fra Map'en (altså uden at kende navnene a og b på forhånd)? Det er vel ikke ligetil (?)
Avatar billede arne_v Ekspert
13. november 2004 - 20:55 #17
Nej. Men selv om man kunne - hvad så ? Der var jo ingen kode som brugte de variable !

Hvordan skal resten af din kode bruge data ?
Avatar billede jespersahner Nybegynder
14. november 2004 - 02:12 #18
->arne_v: Jeg forestiller mig noget i lighed med et database-opslag, hvor man indlæser (alle) felter fra en tabel, og hvor man så kan arbejde videre på de indlæste variable. I dette tilfælde er variablernes navne (og typer) gemt i en header, som styrer indlæsningen. I "halen" på indlæsningen vil jeg så gerne kunne arbejde videre på de indlæste variable og måske skrive tilbage i en ny fil. Jeg er derfor nødt til at sikre mig, variablerne oprettes i koden som angivet i headeren.
Avatar billede arne_v Ekspert
14. november 2004 - 13:50 #19
Jeg tro stadig på ideen med HashMap af ArrayList !
Avatar billede arne_v Ekspert
14. november 2004 - 14:08 #20
Jeg prøver lige at lave et eksempel.
Avatar billede arne_v Ekspert
14. november 2004 - 14:56 #21
import java.io.*;
import java.util.*;

import javax.xml.parsers.*;

import org.w3c.dom.*;
import org.xml.sax.*;

public class XmlBinFilCtrlExt {
    public static final int FIELD_BYTE = 1;
    public static final int FIELD_SHORT = 2;
    public static final int FIELD_INT = 3;
    public static final int FIELD_LONG = 4;
    public static final int FIELD_FLOAT = 5;
    public static final int FIELD_DOUBLE = 6;
    public static int parseType(String s) {
        if(s.equals("byte")) return FIELD_BYTE;
        if(s.equals("short")) return FIELD_SHORT;
        if(s.equals("int")) return FIELD_INT;
        if(s.equals("long")) return FIELD_LONG;
        if(s.equals("float")) return FIELD_FLOAT;
        if(s.equals("double")) return FIELD_DOUBLE;
        return -1;
    }
    public static FieldDescr[] readConfig(String xmlctrl) throws SAXException, IOException, ParserConfigurationException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(new File(xmlctrl));
        NodeList elements = doc.getElementsByTagName("field");
        FieldDescr[] res = new FieldDescr[elements.getLength()];
        int[] types = new int[elements.getLength()];
        for (int i = 0; i < elements.getLength(); i++) {
            res[i] = new FieldDescr();
            res[i].setName(elements.item(i).getAttributes().getNamedItem("name").getNodeValue());
            res[i].setType(parseType(elements.item(i).getAttributes().getNamedItem("type").getNodeValue()));
        }
        return res;
    }
    public static void xput(Map data, String name, Object o) {
        ArrayList al = (ArrayList)data.get(name);
        if(al == null) {
            al = new ArrayList();
            data.put(name, al);
        }
        al.add(o);
    }
    public static Map readData(int n, DataInputStream dis, FieldDescr[] fields) throws IOException {
        Map res = new HashMap();
        for (int j = 0; j < n; j++) {
            for (int i = 0; i < fields.length; i++) {
                String name = fields[i].getName();
                switch (fields[i].getType()) {
                case FIELD_BYTE:
                    xput(res, name, new Byte(dis.readByte()));
                    break;
                case FIELD_SHORT:
                    xput(res, name, new Short(dis.readShort()));
                    break;
                case FIELD_INT:
                    xput(res, name, new Integer(dis.readInt()));
                    break;
                case FIELD_LONG:
                    xput(res, name, new Long(dis.readLong()));
                    break;
                case FIELD_FLOAT:
                    xput(res, name, new Float(dis.readFloat()));
                    break;
                case FIELD_DOUBLE:
                    xput(res, name, new Double(dis.readDouble()));
                    break;
                }
            }
        }
        return res;
    }
    private static Object xget(Map data, String name, int ix) {
        return ((ArrayList)data.get(name)).get(ix);
    }
    public static void writeData(Map data, int n, DataOutputStream dos, FieldDescr[] fields) throws IOException {
        for(int j = 0; j < n; j++) {
            for (int i = 0; i < fields.length; i++) {
                String name = fields[i].getName();
                switch (fields[i].getType()) {
                case FIELD_BYTE:
                    dos.writeByte(((Byte) xget(data, name, j)).byteValue());
                    break;
                case FIELD_SHORT:
                    dos.writeShort(((Short) xget(data, name, j)).shortValue());
                    break;
                case FIELD_INT:
                    dos.writeInt(((Integer) xget(data, name, j)).intValue());
                    break;
                case FIELD_LONG:
                    dos.writeLong(((Long) xget(data, name, j)).longValue());
                    break;
                case FIELD_FLOAT:
                    dos.writeFloat(((Float) xget(data, name, j)).floatValue());
                    break;
                case FIELD_DOUBLE:
                    dos.writeDouble(((Double) xget(data, name, j)).doubleValue());
                    break;
                }
            }
        }
    }
    public static void main(String[] args) throws Exception {
        FieldDescr[] f = readConfig("C:\\ctrl.xml");
        DataOutputStream dos = new DataOutputStream(new FileOutputStream("C:\\bin.dat"));
        ArrayList a1 = new ArrayList();
        a1.add(new Integer(123));
        a1.add(new Integer(321));
        ArrayList b1 = new ArrayList();
        b1.add(new Double(123.456));
        b1.add(new Double(654.321));
        Map hm1 = new HashMap();
        hm1.put("a", a1);
        hm1.put("b", b1);
        System.out.println(hm1);
        writeData(hm1, 2, dos, f);
        dos.close();
        DataInputStream dis = new DataInputStream(new FileInputStream("C:\\bin.dat"));
        Map hm2 = readData(2, dis, f);
        System.out.println(hm2);
        dis.close();
    }
}

class FieldDescr {
    private String name;
    private int type;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getType() {
        return type;
    }
    public void setType(int type) {
        this.type = type;
    }
}
Avatar billede jespersahner Nybegynder
15. november 2004 - 01:30 #22
->arne_v: Jeg tror jeg forstår, hvor du vil hen. Når du er færdig med indlæsningen, har du en HashMap, hvor nøgleværdierne er navnene på variablerne og indholdet er værdierne med de rigtige typer.

Men hvis man skal arbejde videre med indholdet, udestår det stadig at overføre indholdet til primitive (array-)typer med de rigtige navne, f.eks. svarende til:
        ArrayList xa=(ArrayList)hm2.get("a");
        ArrayList xb=(ArrayList)hm2.get("b");
        int dim=xa.size();
        int[] a=new int[dim];
        double[] b=new double[dim];
        for (int i=0;i<dim;i++) {
            a[i]=((Integer)xa.get(i)).intValue();
            b[i]=((Double)xb.get(i)).doubleValue();
            System.out.println(a[i]);
            System.out.println(b[i]);
        }

Nu kan man så arbejde videre på a[] og b[] som primitive (array-)typer.

Men, men..jeg kan jo kun udstede erklæringerne
        int[] a=new int[dim];
        double[] b=new double[dim];
når jeg kender typerne af a og b, og det gør jeg først på runtime! Jeg kan med andre ord ikke udstede erklæringerne vedr. a og b dynamisk ud fra header-oplysningerne, idet de ikke er kendt på compile-time.

Er der noget jeg overser her?
Avatar billede arne_v Ekspert
15. november 2004 - 07:26 #23
Min pointe er at du slet ikke skal hente ud af den HashMap og over i arrays.

Du skal lade resten af din kode arbejde direkte på HashMap med ArrayList's !

En HashMap er jo netop en samling variable navngivet på runtime.

Alle de der afskyelige casts kan undgåes hvis du bruger Java 1.5/5.0 (det har jeg
bare ikke antaget).
Avatar billede jespersahner Nybegynder
15. november 2004 - 11:30 #24
->arne_v: Ok. Jeg takker for stor vedholdenhed og tålmodighed vedr. dette spm.!
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