Programmieren lernen in Visual Basic.NET von Walter Doberenz, Thomas Kowalski 1. Auflage Hanser München 2003 Verlag C.H. Beck im Internet: www.beck.de ISBN 978 3 446 22026 3 Zu Inhaltsverzeichnis schnell und portofrei erhältlich bei beck-shop.de DIE FACHBUCHHANDLUNG
CARL HANSER VERLAG Walter Doberenz, Thomas Kowalski Programmieren lernen in Visual Basic.NET 3-446-22026-7 www.hanser.de
8.1 Operationen mit Verzeichnissen und Dateien 205 Zu den wichtigsten Aufgaben des Programmierers zählt es, Daten auf der Festplatte (oder Diskette, CD,...) permanent abzuspeichern bzw. von dort zu laden. Das.NET-Framework stellt dafür im System.IO-Namensraum eine Anzahl leistungsfähiger Klassen zur Verfügung, die diese Aufgabe vereinfachen sollen. Diese Lektion vermittelt Ihnen die dazu erforderlichen Grundkenntnisse. 8.1 Operationen mit Verzeichnissen und Dateien Bevor wir uns dem Lesen und Schreiben von Dateien zuwenden, betrachten wir zunächst die Arbeit auf Verzeichnisebene, womit das Erstellen, Löschen, Kopieren, Verschieben, Umbenennen, Durchsuchen und Überwachen von Verzeichnissen und Dateien gemeint ist. 8.1.1 Klassen für Verzeichnis- und Dateioperationen Für Manipulationen mit Verzeichnissen und Dateien gibt es neben der Path-Klasse die Pärchen Directory/DirectoryInfo und File/FileInfo, die sich vor allem hinsichtlich ihrer Instanziierbarkeit unterscheiden (statische oder Instanzen-Methoden, siehe 8.1.10). Hinweis: Alle fünf Klassen befinden sich im System.IO-Namensraum. Klasse Directory DirectoryInfo Path File FileInfo FileSystemWatcher... die statischen Methoden erlauben das Erstellen, Verschieben und Benennen von Verzeichnissen und Unterverzeichnissen.... ähnelt der Directory-Klasse, enthält aber nur Instanz-Methoden.... die statischen Methoden erlauben die plattformübergreifende Arbeit mit Verzeichnissen.... die statischen Methoden erlauben das Erzeugen, Kopieren, Löschen, Verschieben und Öffnen von Dateien.... ähnelt der File-Klasse, enthält aber nur Instanz-Methoden.... löst Ereignisse zum Überwachen des Dateisystems aus. Hinweis: Methoden der Klassen File und FileInfo liefern auch die Voraussetzungen für den Schreib- und Lesezugriff auf Dateien (siehe 8.2). 8.1.2 Verzeichnisse erzeugen und löschen Mit Directory-Klasse Die einfachsten Möglichkeiten zum Erzeugen und Löschen von Verzeichnissen bieten die statischen Methoden CreateDirectory() und Delete() der Directory-Klasse. Beispiel: Ein Verzeichnis erzeugen und anschließend wieder löschen Dim pfad As String = "c:\temp"
206 LEKTION 8: Dateien und Streams Directory.CreateDirectory(pfad)... Directory.Delete(pfad, True) ' falls Verzeichnis bereits vorhanden, passiert nichts! ' löscht auch vorhandene Unterverzeichnisse und Dateien Mit DirectoryInfo-Klasse Das gleiche Ziel, allerdings etwas umständlicher, erreicht man mit der Create-Methode der DirectoryInfo-Klasse, wobei mittels CreateSubdirectory auch das Hinzufügen von Unterverzeichnissen möglich ist. Beispiel: Ein Verzeichnis und ein Unterverzeichnis anlegen und wieder löschen Dim di As New DirectoryInfo("c:\temp") di.create() di.createsubdirectory("temp1")... di.delete(true) ' löscht inklusive vorhandener Unterverzeichnisse und Dateien Hinweis: Der parameterlose Aufruf von Delete() funktioniert nur, wenn das Verzeichnis leer ist! 8.1.3 Verzeichnisse verschieben und umbenennen Für diese Aufgaben verwenden Sie am besten die Move-Methode der statischen Directory-Klasse. Beispiel: Das Verzeichnis c:\tempx wird nach c:\beispiele verschoben und umbenannt in tempy. Directory.Move("c:\tempX", "c:\beispiele\tempy") 8.1.4 Aktuelles Verzeichnis ermitteln bzw. festlegen Verwenden Sie dazu die GetCurrentDirectory- bzw. SetCurrentDirectory-Methode der (statischen) Directory-Klasse. Beispiel: Festlegen und Anzeigen des aktuellen Arbeitsverzeichnisses Directory.SetCurrentDirectory("c:\test") Label1.Text = Directory.GetCurrentDirectory() ' zeigt "c:\test" Hinweis: Wenn der Dateiname ohne Pfad angegeben wird, bezieht sich die Datei automatisch auf das Projekt- bzw. Arbeitsverzeichnis. Beispiel: Im Projektverzeichnis wird ein Verzeichnis \temp angelegt. Directory.CreateDirectory("temp")
8.1 Operationen mit Verzeichnissen und Dateien 207 8.1.5 Unterverzeichnisse ermitteln Um alle Unterverzeichnisse zu ermitteln, verwenden Sie die GetDirectories-Methode der Directory- Info-Klasse. Beispiel: Es werden alle Unterverzeichnisse von c:\ in einer ListBox angezeigt. Dim mydir As New DirectoryInfo("c:\") ' neues DirectoryInfo-Objekt Dim mydirs() As DirectoryInfo ' Array zum Speichern der Unterverzeichnisse anlegen mydirs = mydir.getdirectories() ' Unterverzeichnisse ermitteln und im Array ablegen Dim i As Integer For i = 0 To mydirs.length - 1 ' alle Unterverzeichnisse durchlaufen... ListBox1.Items.Add(myDirs(i).Name) '... und Verzeichnisnamen zur ListBox hinzufügen Next i Übrigens: Mit der GetFiles-Methode ermitteln Sie alle in einem Verzeichnis enthaltenen Dateien. Hinweis: Das komplette Beispiel finden Sie auf der Buch-CD. 8.1.6 Anwenden der Path-Klasse Die (statischen) Methoden der Path-Klasse sollen anhand eines Beispiels demonstriert werden. Beispiel: Ausgabe von Dateiinfos in einer ListBox Dim verz As String = "c:\test\info.txt" With ListBox1.Items.Add("Verzeichnis : " & Path.GetDirectoryName(verz)).Add("Dateiname : " & Path.GetFileName(verz)).Add("Dateiname ohne Extension : " & Path.GetFileNameWithoutExtension(verz)).Add("Dateiextension : " & Path.GetExtension(verz)).Add("Rootverzeichnis : " & Path.GetPathRoot(verz)).Add("Temporäres Verzeichnis : " & Path.GetTempPath()).Add("Neues Tempfile : " & Path.GetTempFileName()) End With Doch Vorsicht ist geboten: Die meisten Member der Path-Klasse wirken nicht mit dem Dateisystem zusammen und überprüfen deshalb nicht, ob die durch eine Pfadzeichenfolge angegebene Datei tatsächlich vorhanden ist. Hinweis: Das Beispiel finden Sie auf der Buch-CD!
208 LEKTION 8: Dateien und Streams 8.1.7 Dateien kopieren, verschieben und umbenennen Kopieren und verschieben Am einfachsten realisieren Sie diese Aufgabe mit den statischen Copy- bzw. Move-Methoden der File-Klasse. Beispiel: Datei kopieren und anschließend verschieben Dim sourcepath As String = "c:\sample.txt" Dim destpath As String = "c:\sample1.txt" Dim movepath As String = "c:\temp\sample1.txt" File.Copy(sourcePath, destpath) File.Move(sourcePath, movepath) Falls Sie lieber mit Instanzen arbeiten, können Sie die Methoden CopyTo und MoveTo der Klassen FileInfo bzw. DirectoryInfo verwenden. Hinweis: Auch die Directory-Klasse verfügt über eine Move-Methode zum Verschieben von Verzeichnissen. Umbenennen Das.NET Framework bietet keine Möglichkeit zum direkten Umbenennen einer Datei, da die Name-Eigenschaft der FileInfo-Klasse schreibgeschützt ist und eine Rename-Methode fehlt. Verwenden Sie zum Umbenennen also die Methoden Move der Klasse File bzw. MoveTo der Klasse FileInfo. Beispiel: Umbenennen der Datei info.txt in info_1.txt Dim fi As New FileInfo("c:\test\info.txt") fi.moveto("c:\test\info_1.txt") 8.1.8 Dateiattribute feststellen FileAttributes-Enumeration Die verschiedenen Attribute für Dateien und Verzeichnisse sind in der FileAttribute-Enumeration anzutreffen. Die Tabelle zeigt die wichtigsten: Mitglied Archive Compressed Directory Encrypted... entspricht dem Archiv-Status der Datei, wie er oft zum Markieren einer zu löschenden oder einer Backup-Datei verwendet wird.... entspricht einer gepackten Datei.... zeigt an, dass die Datei in Wirklichkeit ein Verzeichnis ist.... die Datei ist verschlüsselt.
8.1 Operationen mit Verzeichnissen und Dateien 209 Mitglied Hidden Normal ReadOnly System Temporary... die Datei ist versteckt und demzufolge in einem Verzeichnis unsichtbar.... es wurden keine Datei-Attribute gesetzt.... die Datei kann nicht verändert, sondern nur gelesen werden.... die Datei gehört zum Betriebssystem oder wird exklusiv von diesem benutzt.... die Datei ist temporär, d.h., sie wird vom Programm bei Bedarf angelegt und wieder gelöscht. Wichtige Eigenschaften und Methoden Um die Dateiattribute zu ermitteln, kann man entweder auf die Eigenschaften der FileInfo-Klasse oder aber auch auf die entsprechenden (statischen) Methoden der File-Klasse zugreifen: Eigenschaft FileInfo-Klasse Attributes CreationTime LastAccessTime LastWriteTime Methode File-Klasse GetAttributes() SetAttributes() GetCreationTime() SetCreationTime() GetLastAccessTime() SetLastAccessTime() GetLastWriteTime() SetLastWriteTime()... Wert basiert auf Datei-Attribute-Flags (archive, compressed, directory, hidden,...).... Datum/Zeit der Erstellung.... Datum/Zeit des letzten Zugriffs.... Datum/Zeit des letzten Schreibzugriffs. Exists Exists()... liefert True, falls Datei physikalisch existiert. Beispiel: Feststellen, ob Datei im Arbeitsverzeichnis existiert If File.Exists("Liesmich.txt") Then MessageBox.Show("Datei ist vorhanden!") oder Dim fi As New FileInfo("Liesmich.txt") If fi.exists Then MessageBox.Show("Datei ist vorhanden!") Beispiel: Anzeige des Erstellungsdatums einer Datei Label1.Text = File.GetCreationTime("Liesmich.txt").ToString Beispiel: In einer CheckBox wird angezeigt, ob es sich um eine Archiv-Datei handelt. Dim attbs As FileAttributes = File.GetAttributes("c:\beispiele\test.dat") If attbs = (attbs Or FileAttributes.Archive) Then CheckBox2.Checked = True Else CheckBox2.Checked = False End If
210 LEKTION 8: Dateien und Streams 8.1.9 Weitere Datei-Eigenschaften Die folgende Tabelle zeigt einige weitere wichtige Eigenschaften der FileInfo-Klasse. Eigenschaft FileInfo-Klasse Directory DirectoryName Extension FullName Length Name... liefert Instanz des übergeordneten Verzeichnisses.... liefert den vollständigen Dateipfad.... liefert Dateiextension (z.b. txt für Textdateien).... liefert vollständigen Dateipfad plus Dateinamen.... liefert Dateigröße in Bytes.... liefert Dateinamen. Beispiel: Der Pfad der Datei liesmich.txt wird in einem Label angezeigt. Dim fi As New FileInfo("Liesmich.txt") Label1.Text = fi.directoryname Im Zusammenhang mit der Directory-Eigenschaft der FileInfo-Klasse verdient die GetFileSystem- Infos-Methode der DirectoryInfo-Klasse besondere Beachtung. Beispiel: In einer TextBox (MultiLine = True) werden neben dem Verzeichnis einer Datei alle weiteren sich im gleichen Verzeichnis befindlichen Dateien und Unterverzeichnisse angezeigt. Dim fi As New FileInfo("Liesmich.txt") ' öffnet existierende Datei oder neue Dim di As DirectoryInfo = fi.directory ' Verzeichnis-Instanz erzeugen Dim fsi() As FileSystemInfo = di.getfilesysteminfos() ' alle Einträge ermitteln TextBox1.Text = di.fullname & Environment.NewLine ' vollständigen Verzeichnispfad anzeigen Dim info As FileSystemInfo For Each info In fsi '... dann alle weiteren Unterverzeichnisse und Dateien TextBox1.Text &= info.name & Environment.NewLine Next Hinweis: Das komplette Beispiel finden Sie auf der Buch-CD! 8.1.10 Statische oder Instanzen-Klasse? Wenn Sie mit den Methoden der statischen Klassen File, Directory und Path arbeiten, werden Sicherheitsüberprüfungen bei jedem Methodenaufruf vorgenommen, bei den Instanzen-Methoden der Klassen FileInfo und DirectoryInfo geschieht dies nur ein einziges Mal.
8.1 Operationen mit Verzeichnissen und Dateien 211 Bei statischen Methoden müssen Sie jeder Methode den Dateinamen oder den Verzeichnispfad übergeben. Das kann dann ziemlich lästig werden, wenn Sie diese Methoden öfters hintereinander aufrufen müssen. Die entsprechenden Eigenschaften der Instanzen-Klassen FileInfo und Directory- Info hingegen erlauben es Ihnen, den Datei- oder Verzeichnisnamen bereits im Konstruktor einmalig zu spezifizieren. Beispiel: Zwei alternative Möglichkeiten zum Anzeigen von Erstellungsdatum und Zeitpunkt des letzten Zugriffs auf die Datei c:\test\info.txt Label1.Text = File.GetCreationTime("c:\test\info.txt").ToString Label2.Text = File.GetLastAccessTime("c:\test\info.txt").ToString oder Dim myfile As New FileInfo("c:\test\info.txt") Label1.Text = myfile.creationtime.tostring Label2.Text = myfile.lastaccesstime.tostring Hinweis: Nicht immer werden, wie im obigen Beispiel, alle Eigenschaften/Methoden von beiden Klassen gleichermaßen angeboten, dann haben Sie nur eine Wahl. 8.1.11 Überwachung von Änderungen im Dateisystem Die Klasse FileSystemWatcher dient dem einfachen Beobachten des Dateisystems, so löst sie z.b. Ereignisse aus, wenn Dateien oder Verzeichnisse geändert werden. Beispiel: Das Überwachen von vier Ereignissen von.txt-dateien im Verzeichnis c:\beispiele: Dim watcher As New FileSystemWatcher("C:\Beispiele") With watcher.notifyfilter = NotifyFilters.LastAccess Or NotifyFilters.FileName.Filter = "*.txt" AddHandler.Changed, AddressOf Me.OnChanged ' Datei wurde geändert AddHandler.Created, AddressOf Me.OnChanged ' Datei wurde neu hinzugefügt AddHandler.Deleted, AddressOf Me.OnChanged ' Datei wurde gelöscht AddHandler.Renamed, AddressOf Me.OnRenamed ' Datei wurde umbenannt.enableraisingevents = True ' Start der Überwachung End With Die beiden folgenden Event-Handler spezifizieren die Reaktion auf die obigen vier Ereignisse (Anzeige in einer ListBox): Public Sub OnChanged(ByVal Source As Object, ByVal e As FileSystemEventArgs) ListBox1.Items.Add("Datei: " & e.fullpath + " " & e.changetype.tostring) End Sub Public Sub OnRenamed(ByVal Source As Object, ByVal e As RenamedEventArgs) ListBox1.Items.Add("Datei: " & e.oldfullpath + " umbenannt in " & e.fullpath) End Sub
212 LEKTION 8: Dateien und Streams Hinweis: Das vollständige Programm finden Sie auf der Buch-CD. 8.2 Lesen und schreiben von Dateien In diesem Abschnitt geht es um das zentrale Anliegen der Lektion, nämlich um das Abspeichern von Daten. Die wichtigsten Dateitypen sind: Textdatei Binärdatei (Bilddateien etc.) sequenzielle Datei Random-Access-Datei Hinweis: Da.NET keine typisierten Dateien unterstützt, müssen sequenzielle und Random- Access-Dateien durch geeignete Programmiermaßnahmen auf Binärdateien zurückgeführt werden. 8.2.1 Übersicht Dateien und Streams Während man die in einer Datei gespeicherten Informationen auch als persistente Daten bezeichnet, arbeitet das Programm mit temporären bzw. transienten Daten. Wie Sie der folgenden Abbildung entnehmen, gewährleisten Streams quasi als "Verbindungskanäle" die Kommunikation zwischen Datei und Programm. Programm temporäre Daten (Arbeitsspeicher) Read Stream Write Stream Datei persistente Daten (Festplatte)
8.2 Lesen und schreiben von Dateien 213 Klassen Auch für Datei- und Stream-Operationen werden zunächst die Klassen File bzw. FileInfo benötigt (siehe Abschnitt 8.1). Die folgende Tabelle zeigt die weiteren wichtigen Klassen. Klasse FileStream StreamReader StreamWriter StringReader StringWriter BinaryReader BinaryWriter BinaryFormatter... erlaubt, basierend auf einer Datei, das Erstellen einer Stream-Instanz.... implementiert ein TextReader-Objekt, welches Zeichen von einem Byte- Stream in einer bestimmten Kodierung liest.... implementiert ein TextWriter-Objekt, welches Zeichen in einen Stream in einer speziellen Kodierung liest.... implementiert ein TextReader-Objekt, das Daten von einem String liest.... implementiert ein TextWriter-Objekt, das Daten in einen String schreibt. Die Daten werden in einer darunter liegenden StringBuilder-Klasse gespeichert.... erlaubt das binäre Lesen von Dateien... erlaubt das binäre Schreiben in Dateien.... kann Objekte in einen Stream serialisieren bzw. von dort deserialisieren. Erzeugen einer Stream-Instanz Voraussetzung für jeglichen Dateizugriff ist das Vorhandensein eines Stream-Objekts (siehe obige Abbildung). Letzteres kann entweder über die Open-Methode eines FileInfo-Objekts oder der (statischen) File-Klasse erzeugt werden. Beispiel: Die (im Arbeitsverzeichnis befindliche) Datei temp.txt soll für den exklusiven Schreib-/ Lesezugriff geöffnet werden. Falls sie nicht vorhanden ist, wird sie neu erzeugt. Dim fi As New FileInfo("temp.txt") Dim fs As FileStream = fi.open(filemode.openorcreate, FileAccess.ReadWrite, FileShare.None) oder Dim fs As FileStream = File.Open("temp.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, _ FileShare.None) Zur Bedeutung der einzelnen Parameter der Open-Methode kommen wir im folgenden Abschnitt. 8.2.2 Dateiparameter In den Methoden bzw. Konstruktoren der Klassen File, FileInfo und FileStream werden bestimmte Parameter übergeben, die in Aufzählungstypen (Enumerationen) gekapselt sind. FileAccess Diese Enumeration bezeichnet den Zugriffslevel auf eine Datei.
214 LEKTION 8: Dateien und Streams Mitglied Read ReadWrite Write... erlaubt Lesezugriff... erlaubt Lese- und Schreibzugriff... erlaubt Schreibzugriff Die FileMode-Enumeration Diese Enumeration bestimmt den Öffnungsmodus einer Datei. Mitglied Append Create Open OpenOrCreate Truncate Eine existierende Datei wird geöffnet und der Dateizeiger an das Ende bewegt, oder eine neue Datei wird erstellt ( FileAccess.Write ist erforderlich, Leseversuche schlagen fehl). Eine neue Datei wird erzeugt. Falls die Datei bereits existiert, wird sie überschrieben. Eine existierende Datei wird geöffnet. Falls die Datei existiert, wird sie geöffnet, andernfalls wird sie neu erzeugt. Eine existierende Datei wird geöffnet und die Dateigröße auf null Bytes beschnitten. Die FileShare-Enumeration Diese Enumeration verwenden Sie, um festzulegen, ob auf eine Datei gleichzeitig von mehreren Prozessen aus zugegriffen werden kann. Mitglied None Read ReadWrite Write Die Datei ist für den gleichzeitigen Zugriff gesperrt. Alle weiteren Anforderungen zum Öffnen werden abgelehnt, es sei denn, die Datei ist geschlossen. Auch andere Benutzer bzw. Prozesse dürfen die Datei lesen. Versuche zum Schreiben bzw. Abspeichern schlagen fehl. Die Datei kann von mehreren Benutzern bzw. Prozessen sowohl zum Lesen als auch zum Schreiben geöffnet werden (problematisch, da der letzte Benutzer auch die Änderungen anderer Benutzer abspeichert). Die Datei ist für den gleichzeitigen Schreibzugriff geöffnet. In Kombination mit Read kann das den ReadWrite-Parameter ersetzen. Beispiel: Mit dem folgenden FileStream-Konstruktor wird eine vorhandene Datei geöffnet und weiteren Benutzern der schreibgeschützte Zugriff gewährt (FileShare.Read). Dim mystream As New FileStream("c:\test.txt", FileMode.Open, FileAccess.Read, FileShare.Read)