java-forum.org - Java programmieren aus Leidenschaft

Zurück   java-forum.org - Java programmieren aus Leidenschaft > Blogs > Antoras

Bewerten

Dateiverarbeitung im Dateisystem und in Archiven

"Dateiverarbeitung im Dateisystem und in Archiven" bei Mister Wong speichern "Dateiverarbeitung im Dateisystem und in Archiven" bei YiGG.de speichern "Dateiverarbeitung im Dateisystem und in Archiven" bei Google speichern "Dateiverarbeitung im Dateisystem und in Archiven" bei del.icio.us speichern
Veröffentlicht: 03.04.2011 um 14:24 von Antoras
Aktualisiert: 03.04.2011 um 20:02 von Antoras
Stichworte archiv , dateiverarbeitung , file , jar

Da hier im Forum immer wieder Fragen zur Dateiverarbeitung auftauchen, bei denen die User vor allem Probleme mit dem Setzen des richtigen Pfades haben, hab ich mir gedacht, dass ich mal einen kleinen Beitrag dazu verfasse um ein wenig Licht ins Dunkel zu bringen.

Zuerst einmal ein paar Worte zu der richtigen Adressierung einer Datei. Es gibt verschiedene Symbole, die angeben von welcher Stelle an im Dateisystem gesucht werden soll. Die Wichtigsten sind:
SymbolBedeutung
/Root-Vrzeichnis
./momentanes Verzeichnis
..vorheriges Verzeichnis

Dateiverarbeitung im Dateisystem
Mit der Klasse File kann man Dateien referenzieren, die nicht in einem Archiv gespeichert sind. So referenziert new File("file.txt"); auf eine Datei im momentanen Verzeichnis - das momentane Verzeichnis ist hierbei das Verzeichnis, von dem aus das Programm aufgerufen wurde.
Wird einem Dateinamen keines der oben genannten Symbole voraus gestellt, wird automatisch im momentanen Verzeichnis gesucht.

Um den Inhalt einer Textdatei zu lesen findet sich immer wieder die folgende Methode:
Java Code: Quelltext in neuem Fenster öffnen
1
2
3
4
5
6
7
8
List<String> readFile(final String file) throws IOException {
  final BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
  final List<String> lines = new ArrayList<String>();
  for (String line; (line = in.readLine()) != null;) {
    lines.add(line);
  }
  return lines;
}
Alternativ kann man hier statt einem String auch ein File an die Methode übergeben. Schöner finde ich es aber wenn auf einen Scanner zurückgegriffen wird:
Java Code: Quelltext in neuem Fenster öffnen
1
2
3
List<String> readFile(final String file) throws IOException {
  return Arrays.asList(new Scanner(new FileInputStream(file)).useDelimiter("\0").next().split("\n"));
}

Dateiverarbeitung in einem Archiv
In Archiven kann nicht mit der Klasse File gesucht werden, schließlich haben wir ja nur eine Datei (das Archiv) und wir müssen dessen gepackten Inhalt durchsuchen. Das JDK stellt dafür Methoden wie Class#getResource, Class#getResourceAsStream oder ClassLoader#getResourceAsStream oder ClassLoader#getSystemResource bereit.

Falls wir also beispielsweise unser Programm als JAR exportiert haben können wir mit diesen beiden Methoden auf die dort enthaltenen Dateien zugreifen.
Java Code: Quelltext in neuem Fenster öffnen
1
2
3
4
5
6
7
public class Main {
  public static void main(final String... args) {
    Main.class.getResourceAsStream("/file.txt");
    // oder
    Main.class.getClassLoader().getResourceAsStream("file.txt");
  }
}
Dieser Code würde uns Zugriff auf die Datei file.txt gewähren, die innerhalb des JAR aufzufinden ist. Besonderes Augenmerk ist in der ersten Methode auf den Slash vor dem Dateinamen zu richten. Diese Methode sucht relativ zur Klasse nach der Ressource, d.h. wenn wir an ein höher liegendes Verzeichnis kommen wollen, müssen wir entweder vom Root-Verzeichnis aus suchen oder mit den zwei aufeinander folgenden Punkten durch die Verzeichnishierarchie navigieren (z.B. "../file.txt"). Das Root-Verzeichnis ist hier übrigens das Archiv selbst und nicht ein Verzeichnis im Dateisystem.
Die zweite Methode, die nicht von Class sondern von ClassLoader bereitgestellt wird nimmt nur absolute Pfade entgegen, d.h. es macht keinen Unterschied ob man einen Slash voranstellt, da immer vom Root-Verzeichnis des Archivs aus gesucht wird. Wenn man nicht durcheinander kommen möchte, ist es wohl am einfachsten wenn man alle Pfade relativ zum Root-Verzeichnis der Archivs angibt.

Mit der Scanner-Klasse wird es einem wieder einfach gemacht den Inhalt einer Textdatei zu lesen:
Java Code: Quelltext in neuem Fenster öffnen
1
2
3
List<String> readArchiveFile(final String fileName) {
  return Arrays.asList(new Scanner(Main.class.getClassLoader().getResourceAsStream(fileName)).useDelimiter("\0").next().split("\n"));
}

Dynamisches Laden von Dateien
Falls man nicht schon beim Entwickeln weiß welche Dateien das Programm später benötigt kann man auch keine Namen festlegen mit denen sich die Dateien referenzieren ließen. Dann muss man in den sauren Apfel beißen und sich die benötigten Dateien eben selbst zur Laufzeit zusammensuchen.
Mit new File("./").listFiles(); kann man sich alle Dateien eines Verzeichnisses ausgeben lassen.

Einige Methoden, die File bereit stellt, werden in dem FAQ Artikel Verzeichnisse durchsuchen/bearbeiten/auslesen beschrieben.

Möchte man Zugriff auf beliebige Dateien eines JAR erhalten bietet das JDK die Klassen JarEntry und JarFile:
Java Code: Quelltext in neuem Fenster öffnen
1
2
3
4
5
6
7
8
9
10
11
public class Main {
  public static void main(final String... args) throws IOException {
    for (final JarEntry e : Collections.list(new JarFile(jarName()).entries())) {
      // use e
    }
  }
  static String jarName() {
    final String s[] = Main.class.getProtectionDomain().getCodeSource().getLocation().toString().split("/");
    return s[s.length - 1];
  }
}

Entwickelst du noch oder deployst du schon?
Ein kleines Schönheitsproblem hat man wenn man viel mit Dateien arbeiten muss, bei Auslieferung der Software aber immer darauf achten muss, dass auch die Pfade zu den Ressourcen richtig gesetzt bleiben. Um hier keine Fehler zu machen bietet es sich an eine Überprüfung im Programm einzubauen, die einem hier behilflich ist:
Java Code: Quelltext in neuem Fenster öffnen
1
2
3
boolean isExecutedInArchive() {
  return Main.class.getProtectionDomain().getCodeSource().getLocation().toString().endsWith(".jar");
}
Beim Entwickeln endet der gefundene Pfad mit dem Verzeichnis in dem der Bytecode liegt, z.B. bin . Das ganze noch in eine Klasse verpackt braucht es einen dann nicht mehr zu kümmern ob man das Programm noch mit der IDE ausführt oder ob es schon vom Endbenutzter benutzt wird:
Java Code: Quelltext in neuem Fenster öffnen
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class FileLoader {
  public static void main(final String... args) {
    final String name = "fileloader.txt";
    // Funktioniert sowohl in der IDE als auch in einem JAR
    for (final String s : read(name)) {
        System.out.println(s);
    }
  }
 
  public static List<String> read(final String fileName) {
    return isExecutedInArchive() ? readArchiveFile(fileName) : readFile(fileName);
  }
 
  public static List<String> read(final File file) {
    return isExecutedInArchive() ? readArchiveFile(file.getPath()) : readFile(file);
  }
 
  public static boolean isExecutedInArchive() {
    return FileLoader.class.getProtectionDomain().getCodeSource().getLocation().toString().endsWith(".jar");
  }
 
  public static List<String> readFile(final String fileName) {
    return readFile(new File(fileName));
  }
 
  public static List<String> readFile(final File file) {
    try {
        return readLines(new FileInputStream(file));
    } catch (final IOException e) {
        e.printStackTrace();
    }
    return Collections.emptyList();
  }
 
  public static List<String> readArchiveFile(final String fileName) {
    return readLines(FileLoader.class.getClassLoader().getResourceAsStream(fileName));
  }
 
  public static List<String> readLines(final InputStream src) {
    return Arrays.asList(new Scanner(src).useDelimiter("\0").next().split("\n"));
  }
}
Kategorie: Kategorielos
Hits 1138 Kommentare 2
« Zurück     Startseite des Blogs     Nächste »
Kommentare 2

Kommentare

  1. Alter Kommentar
    Zitat:
    Wollen wir eine Datei in einem Archiv referenzieren muss unbedingt vom Root-Verzeichnis aus gesucht werden
    Mhm, das stimmt so aber nicht ganz oder? Es gibt da jeweils Unterschiede, je nachdem welche Methode benutzt wird. In deinem Beispiel (Class.getResourceAsStream) sagt der führende Slash aus, dass der Pfad absolut angegeben wird. Man kann den Slash aber auch weglassen, dann wird relativ zu der Klasse auf der man die Methode aufruft gesucht.
    Beim Classloader.getResourceAsStream müssen alle Angaben als absolut angegeben werden (der führende Slash ist auch nicht notwendig!)
    Eine gute Übersicht befindet sich wie ich finde hier: Smartly load your properties - JavaWorld
    permalink
    Veröffentlicht: 03.04.2011 um 16:59 von eRaaaa eRaaaa ist offline
  2. Alter Kommentar
    Benutzerbild von Antoras
    Stimmt, das ist schlecht formuliert. Ich bin davon ausgegangen, dass zu ladende Ressourcen immer im Hauptverzeichnis abgelegt werden, wovon ich aber nicht ausgehen darf. Ich hab den entsprechenden Abschnitt mal neu formuliert. Vielen Dank für den Hinweis!
    permalink
    Veröffentlicht: 03.04.2011 um 20:05 von Antoras Antoras ist offline
 

Alle Zeitangaben in WEZ +1. Es ist jetzt 08:30 Uhr.


Powered by vBulletin® Version 3.8.6 (Deutsch)
Copyright ©2000 - 2013, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.3.2
Thanks for Smilies by smilies.4-user.de