Zeilen einer Textdatei in Objekte einlesen

Angenommen ich habe eine Textdatei folgenden Formats (exportiert aus einer Access-Abfrage):

Ort Bundesland Bürgermeister
Berlin Berlin Wowi
Hamburg Hamburg Scholz
München Bayern Horst
Hannover Niedersachsen Horstine

Wie kann ich diese einlesen, so dass für jeden Ort ein Objekt mit den Eigenschaften Ort, Bundesland und Bürgermeister angelegt wird?

Zeilenweise einlesen und bei Leerzeichen splitten.

Etwas problematisch wird das ganze wenn die Orte oder Namen Leerzeichen besitzen, die müssten dann beim Exportieren escaped oder “gekapselt” werden (wie das auch bei CSV der Fall ist).

Das ist ein klassisches CSV-Format mit einem Leerzeichen als Trennzeichen. Da das so simpel ist würde ich nicht einmal meine Zeit damit verschwenden mich in eine CSV-Lib reinzulesen sondern tatsächlich jede Zeile einzeln im File durchzugehen.

Dann brauchst du 'ne kleine Entity-Klasse

public class Mayor {
  private String name;
  private String state;
  private String city;

  // constructor, getter/setter hier einfügen
} 

Und dann gehst du die Zeilen in einer Schleife durch:

List<Mayor> mayors = new ArrayList<Mayor>();
BufferedReader br = new BufferedReader(new FileReader("file.dat"));
    try {

        while ((line = br.readLine()) != null) {
            String[] parts = line.split(" ");
            mayors.add(new Mayor(parts[0], parts[1], parts[2]);
        }
    } finally {
        br.close();
    }

Hier ein kurzes Beispiel, angenommen du hast eine klasse Ort mit den 3 Feldern und passenden Settern und Gettern.

try {
	BufferedReader in = new BufferedReader(new FileReader("text.txt"));
	String zeile = null;
        in.readLine();
	while ((zeile = in.readLine()) != null) {
		String[] parts=zeile.split(" ");
		Orte ort=new Ort();
		ort.setOrt(parts[0]);
		ort.setBundesland(parts[1]);
		ort.serBuergermeister(parts[2]);
		orte.add(ort);
	}
} catch (IOException e) {
	e.printStackTrace();
}```
Das Ergebniss ist eine Liste mit den Ort Objekten

Und wenn statt Leerzeichen Tabs verwendet wurden, weil z.B. Städte Leerzeichen enthalten?

split[" "]?

Gracias :slight_smile:

zeile.split() splitted auch bei allen whitespaces. Wenn nur der Name Bürgermeister Leerzeichen enthält, dann zuerst Ort (part[0]), Bundesland (part[1]) und alle übrigen Teile (part[2]+…+part[n-1]) konkatenieren.
Eine Liste mit Objekten ist gut. LinkedList beißt sich gegenüber ArrayList nicht.

[QUOTE=Unregistered]zeile.split() splitted auch bei allen whitespaces. Wenn nur der Name Bürgermeister Leerzeichen enthält, dann zuerst Ort (part[0]), Bundesland (part[1]) und alle übrigen Teile (part[2]+…+part[n-1]) konkatenieren.
Eine Liste mit Objekten ist gut. LinkedList beißt sich gegenüber ArrayList nicht.[/QUOTE]
Unter der Annahme das der Ort kein Leerzeichen enthält, kann das funktionieren, aber bei dem Input
Bad Homburg Hessen Max Mustermann würde dann in deinem Objekt stehen
Ort Bad
Bundesland Homburg
Bürgermeister Hessen Max Mustermann
wenn das Trennzeichen ein Tabulator ist, und man am Tab trennt sieht es so aus
Ort Bad Homburg
Bundesland Hessen
Bürgermeister Max Mustermann

äh ja, wenn die essentielle Annahme nicht erfüllt ist, wird es wohl schiefgehen :wink:

ich verringere den Spam-Charakter meines Postings mit Hinweis dass ich Thema auf Gelöst setze

edit nach unten:
ok, dann nun täglicher Druck, endlich das Thema fortzuführen :wink:
ne kein Problem, im Zweifel muss nur mein Ordnungstick leiden

[QUOTE=SlaterB]
ich verringere den Spam-Charakter meines Postings mit Hinweis dass ich Thema auf Gelöst setze[/QUOTE]

Neeee, da kommen mit Sicherheit noch weiterführende Fragen :wink:

Seit ich ein wenig mehr mit Sprachen rumhantiere, die sich wie dynamisch Typisierte anfühlen finde ich Maps sehr cool.

List<Map<String, String>> result = new ArrayList<Map<String,String>>();
BufferedReader br = new BufferedReader(new FileReader("file.dat"));
try {
  if ((keyline = br.readLine()) != null) {    
    String[] keys = keyline.split(" "); 
    while ((line = br.readLine()) != null) {
      Map<String, String> entry = new HashMap<String, String>();
      String[] parts = line.split(" ");
      for(int i = 0; i<parts.length; i++){
        entry.put(keys**, parts**);
      }
      result.add(new Mayor(parts[0], parts[1], parts[2]);
    }
  }
} finally {
  br.close();
}```

Der Vorteil ist, daß man sich so das erstellen einer Klasse ersparen kann.

Wenn man sich z.B. ein Userinterface dazubaut und das Programm startet. Dann könnte man eine beliebige Datei starten und dann so abfragen machen wie in welchem "Ort" ist "Wowi" "Bürgermeister".

```String searchKey = "Ort";
String searchValue = "Wowi";
String resultKey = "Bürgermeister";

List<String> results = new ArrayList<String>();
for(Map<String, String> entry: LOADED_ENTRIES) {
  if(entry.get(searchKey).equals(searchValue)){
    results.add(entry.get(resultKey));
  }
}
return results;```

Und im nächsten Schritt, wählt man eine Datei über Fußball aus und fragt, in welchen Jahren hat Hansa in der Bundesliga gespielt;)


Und zum Einlesen noch eine Frage. Sind die Dateien sehr groß, bzw. wie groß sind diese?
Bei sehr großen Dateien kann es manchmal hilfreich sein, Iteratoren statt Listen zu verwenden, gerade auch wenn die Daten weiterverarbeitet werden sollen.

Ich sehe da den Vorteil nicht. Was machst du wenn du nach “Wowi” suchst? eine 2. Map? Da kommst du schon extrem ins schleudern bei 3 Werten. Da kannst du ein Objekt deutlich komfortabler durchsuchen.

Der Vorteil einer Map, kann daraus bestehen, dass dynamisch auf verschiedene Datentypen reagiert werden kann.

[SPOILER]```import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;

public class CSVReader {

public static Iterator<Map<String, String>> getIterator() {
    final Scanner scanner = new Scanner(System.in);
    if (scanner.hasNextLine()) {
        final String[] columns = scanner.nextLine().split("	");

        return new Iterator<Map<String, String>>() {
            @Override
            public boolean hasNext() {
                return scanner.hasNextLine();
            }

            @Override
            public Map<String, String> next() {
                String[] line = scanner.nextLine().split("	");
                Map<String, String> entry = new HashMap<>();
                for (int i = 0; i < columns.length; i++) {
                    entry.put(columns**, line**);
                }
                return entry;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    } else {
        return new Iterator<Map<String, String>>() {
            @Override
            public boolean hasNext() {
                return false;
            }

            @Override
            public Map<String, String> next() {
                throw new NullPointerException();
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }
}

public static String toJson(Iterator<Map<String, String>> csvIterator) {
    StringBuilder builder = new StringBuilder("{\"result\":[

“);
while (csvIterator.hasNext()) {
Map<String, String> entry = csvIterator.next();
builder.append(”{");
boolean printComma = false;
for (String key : entry.keySet()) {
if (printComma) {
builder.append(", “);
}
printComma = true;
builder.append(String.format(”"%s":"%s"", key, entry.get(key)));
}
builder.append("}");
if (csvIterator.hasNext()) {
builder.append(",");
builder.append("
“);
}
}
return builder.append(”
]}").toString();
}

public static Iterator<Map<String, String>> filter(final String key, final String value, final Iterator<Map<String, String>> it) {
    return new Iterator<Map<String, String>>() {
        private Map<String, String> current = null;

        @Override
        public boolean hasNext() {
            while (current == null && it.hasNext()) {
                current = it.next();
                if (current.get(key).equals(value)) {
                    return true;
                } else {
                    current = null;
                }
            }
            return current != null && current.get(key).equals(value);
        }

        @Override
        public Map<String, String> next() {
            if (current != null) {
                Map<String, String> result = current;
                current = null;
                return result;
            } else {
                throw new NullPointerException();
            }
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    };
}

public static Iterator<Map<String, String>> project(final String[] keys, final Iterator<Map<String, String>> it) {
    return new Iterator<Map<String, String>>() {
        @Override
        public boolean hasNext() {
            return it.hasNext();
        }

        @Override
        public Map<String, String> next() {
            Map<String, String> entry = it.next();
            Map<String, String> result = new HashMap<>();
            for (String key : keys) {
                result.put(key, entry.get(key));
            }
            return result;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    };
}

public static void main(String[] args) throws FileNotFoundException {
    Iterator<Map<String, String>> it = getIterator();
    if (args.length == 1) {
        Scanner scanner = new Scanner(new File(args[0]));
        while (scanner.hasNextLine()) {
            String[] line = scanner.nextLine().split("	");
            switch (line[0]) {
                case "filter":
                    it = filter(line[1], line[2], it);
                    break;
                case "projection":
                    String[] pros = new String[line.length - 1];
                    System.arraycopy(line, 1, pros, 0, pros.length);
                    it = project(pros, it);
                    break;
            }
        }
    }

    String result = toJson(it);
    System.out.println(result);
}

}


Eine Implementierung mit der man Einträge filtern, auf weniger Spalten projezieren und als JSon ausgeben kann.

Beispiel des TO:
Input
[SPOILER]

Ort Bundesland Bürgermeister
Berlin Berlin Wowi
Hamburg Hamburg Scholz
München Bayern Horst
Hannover Niedersachsen Horstine


[/SPOILER]

Rezept

filter Ort Berlin
projection Bürgermeister Bundesland



Aufruf

cat test.dat | java CSVReader test.recipe



Ausgabe

{“result”:[
{“Bürgermeister”:“Wowi”, “Bundesland”:“Berlin”}
]}



Fussball-Beispiel
Input
[SPOILER]

Saison Meister
1998/99 FC Bayern München
99/2000 FC Bayern München
2000/01 FC Bayern München
2001/02 Borussia Dortmund
2002/03 FC Bayern München
2003/04 Werder Bremen
2004/05 FC Bayern München
2005/06 FC Bayern München
2006/07 VfB Stuttgart
2007/08 FC Bayern München
2008/09 VfL Wolfsburg
2009/10 FC Bayern München
2010/11 Borussia Dortmund
2011/12 Borussia Dortmund
2012/13 FC Bayern München


[/SPOILER]

Rezept

filter Meister FC Bayern München
projection Saison




Aufruf

cat fussball.dat | java CSVReader fussball.recipe



Ausgabe

{“result”:[
{“Saison”:“1998/99”},
{“Saison”:“99/2000”},
{“Saison”:“2000/01”},
{“Saison”:“2002/03”},
{“Saison”:“2004/05”},
{“Saison”:“2005/06”},
{“Saison”:“2007/08”},
{“Saison”:“2009/10”},
{“Saison”:“2012/13”}
]}



Die Dinge, also filter, projektion etc. die in recipe definiert werden, kann man auch gut in eine GUI verpacken. Damit kann man dann nahezu beliebige CSV Dateien auswählen und analysieren ohne extra Klassen zu erstellen und kompilieren zu müssen. 

Das kann man schon praktisch finden?

[QUOTE=TheUnregistered]Unter der Annahme das der Ort kein Leerzeichen enthält, kann das funktionieren, aber bei dem Input
Bad Homburg Hessen Max Mustermann würde dann in deinem Objekt stehen
Ort Bad
Bundesland Homburg
Bürgermeister Hessen Max Mustermann
wenn das Trennzeichen ein Tabulator ist, und man am Tab trennt sieht es so aus
Ort Bad Homburg
Bundesland Hessen
Bürgermeister Max Mustermann[/QUOTE]

Das stimmt natürlich. Aber es gebe noch eine 3. Möglichkeit (neben Tabs und dem Zusammenfügen längerer Namen/Bezeichnungen): Man hätte z.B. eine googel datenbank mit Städtenamen. Wenn niemand so heißt wie eine Stadt (oder umgekehrt), dann sollte es keine Schwierigkeiten geben. In den Staaten heißen ja sehr viele mit Vornamen so wie eine Stadt, denke ich mal. Vielee Grüßee

dann nehm eine nicht oop sprache wenn es dich so stoert.

was sich mit Klassen/Interfaces etc genauso machen laesst und das logischer, klarer und strukturiert

Angenommen ich habe jetzt folgende Variante von schlingel verwendet:

BufferedReader br = new BufferedReader(new FileReader("file.dat"));
    try {
 
        while ((line = br.readLine()) != null) {
            String[] parts = line.split(" ");
            mayors.add(new Mayor(parts[0], parts[1], parts[2]);
        }
    } finally {
        br.close();
    }```

Nun möchte ich das ganze in die neue ArrayList "Knoten" umschreiben, wobei für jeden Ort ein neuer Knoten angelegt werden soll.
Das Format, dass dabei herauskommt, soll folgendermaßen aussehen (grob):

Ort [label="Bundesland"] (alles in einen String)

Wie schreibe ich das jetzt aus der ersten ArrayList raus und in die neue ArrayList für Knoten rein?

eine Liste per Schleife durchlaufen sollte als Grundfunktion bekannt sein,

die Liste mayors durchlaufen (halbwegs vergleichbar mit bisherigen Durchlauf),
neue Objekte welcher Form auch immer anlegen (halbwegs vergleichbar mit Mayor-Erzeugung),
und in eine neue Liste einfügen (ziemlich ähnlich)?

einen String zusammenbauen ist davon unabhängig eine Teilaufgabe, die man vorher an einem Beispiel angehen kann,
sollte aber doch nicht so schwer sein

Ich habe jetzt folgendes:

		
for (int counter = 0; counter < mayors.size(); counter--) {
	knoten.add(new Knoten(???));
}```


```public class Knoten {

	String knoten;
	
	Knoten(String knoten) {
		this.knoten = knoten;
	}
}```

Nur weiß ich nicht, wie ich die markierte Stelle mit den ??? fülle.

Kann nicht klappen, schau dir mal deine for-schleife an. - Oder noch besser: mach eine foreach draus.

Dann könntest du die toString-Methode von Mayor überschreiben - oder du spendierst der Klasse eine Methode die dir einen String zurückgibt (den musst du eben selber zusammenbauen). Zum Schluss hast du dann etwa sowas: Knoten knoten = new Knoten(mayor.toInfoString());