Praxismodul 6 - Webarchiv ETHZ / Webarchive ETH

Werbung
Institut für Computational Science
Prof. Dr. H. Hinterberger
Praxismodul 6
Dateien verarbeiten
Dateien verarbeiten
2
Aufbau
A. Einführung
B. Tutorial
C. Selbständige Aufgabe
D. Abgabebedingungen
Inhalt
1. Der Datentyp Textfile ....................................................................................................................................................... 3
2. Fehlerbehandlung...............................................................................................................................................................6
3. Programmentwurf............................................................................................................................................................. 7
4. Hilfsmittel für den Programmentwurf......................................................................................................................9
5. Angewandter Programmentwurf...............................................................................................................................12
Phase 1 – Analyse.....................................................................................................................................................................15
Phase 2 – Modellierung ....................................................................................................................................................... 18
Phase 3 – Entwurf ................................................................................................................................................................. 20
Phase 4 – Implementation.................................................................................................................................................24
Phase 5 – Verifikation.......................................................................................................................................................... 30
6. Neue Sprachelemente .....................................................................................................................................................31
Dateien verarbeiten
3
A. Einführung
Damit die von einem Programm erzeugten oder verarbeiteten Daten auch noch zur Verfügung
stehen nachdem das Programm terminiert, müssen diese in Dateien auf einem permanenten
Speichermedium gesichert werden.
1. Der Datentyp Textfile
Für Daten, die in Dateien auf Speichermedien wie Festplatten, Disketten, Magnetbänder, CD-ROM,
usw. permanent gespeichert sind, hat sich der Begriff "file" eingebürgert.
Pascal unterscheidet drei File-Typen: typisierte, untypisierte Files und Textfiles. Typisierte und untypisierte Files werden im Praxismodul 7 behandelt. Dieses Praxismodul bietet einen Einstieg in die
Verarbeitung von Dateien auf der Basis von Textfiles.
Files deklarieren
Mit dem von Pascal vorgesehenen Standard-Filetyp Textfile werden Files deklariert, welche Textzeichen enthalten, die zeilenweise angeordnet sind. Jede Zeile schliesst dabei mit einem Zeilenende-Zeichen ab.
Um mit einem File arbeiten zu können, muss dieses in der Variablendeklaration mit einem Namen
bezeichnet werden:
Var
Textzeichen: Textfile;
Diese Filevariable (Textzeichen) ist vom Typ Textfile, d.h. alle für Textfiles erlaubten Operationen können auf sie angewandt werden.
Die Daten einer Dateivariablen sind im Hauptspeicher abgelegt, sie sind also nicht permanent.
Damit diese Daten auf einem anderen Speichermedium permanent gespeichert werden können,
muss die Filevariable einer externen Datei zugeordnet werden. Dies geschieht mit Hilfe eines Aufrufs der Prozedur AssignFile. Wenn wir beispielsweise eine Textdatei erzeugen oder mit einer
bestehenden Datei arbeiten möchten, schreiben wir:
Filevariable
Dateiname
AssignFile(Textzeichen, 'C:\Meintext.txt')
oder
AssignFile(Textzeichen, OpenDialog1.FileName)
falls wir einen Dateinamen mit der Dialogkomponente von Delphi, welche das WindowsStandarddialogfeld "Öffnen" erzeugt, auswählen.
Dateien verarbeiten
4
Files öffnen
Nach der Zuordnung der Filevariablen zur externen Datei muss die Filevariable "geöffnet", das
heisst für die Eingabe oder Ausgabe vorbereitet werden. Bereits bestehende Files werden mit der
Prozedur Reset geöffnet:
Reset(Textzeichen);
Eine Textdatei ist nach dem Öffnen mit Reset schreibgeschützt. Eine bereits geöffnete Datei wird
mit Reset geschlossen und wieder geöffnet. Jede, auf Reset folgende Operation, "sieht" den
Anfang des geöffneten Files.
Neue Files können mit der Prozedur Rewrite erzeugt und gleichzeitig geöffnet werden:
Rewrite(Textzeichen)
Falls bereits eine gleichnamige Datei vorhanden ist, wird der Inhalt dieser Datei gelöscht. Jede, auf
Rewrite folgende Operation, "sieht" den Anfang des geöffneten Files.
Im Gegensatz zu Rewrite öffnet die Prozedur Append ein existierendes Textfile so, dass am Ende
des Files neuer Text hinzugefügt werden kann:
Append(Textzeichen)
Die Prozedur Append kann nur auf Textfiles angewandt werden.
Files schliessen
Wenn ein Programm die Bearbeitung eines Files beendet hat, muss dieses mit Hilfe der Standardprozedur CloseFile geschlossen werden:
CloseFile(Textzeichen);
Hierbei wird die zugehörige Datei aktualisiert, d.h. eventuell noch ausstehende Datentransfers
werden ausgeführt. Die Filevariable kann anschliessend einer anderen Datei zugeordnet oder die
gerade geschlossene Datei kann durch ein anderes Programm verwendet werden.
Textfiles lesen
Angenommen, unsere im Verzeichnis C:\ gespeicherte Datei Meintext.txt enthält Text, mit Zeilen,
die nicht mehr als 127 Zeichen enthalten. Das folgende Codesegment liest die erste Zeile dieses
Textes in die Stringvariable Zeile:
Var
Textzeichen: Textfile;
Zeile: String;
Zeichen: Char;
begin
AssignFile(Textzeichen, 'C:\Meintext.txt');
Reset(Textzeichen);
Read(Textzeichen, Zeile);
Read(Textzeichen);
Dateien verarbeiten
5
Das erste Read liest die erste Zeile der Datei, das zweite Read das Zeilenende-Zeichen (speichert
dieses aber nicht in einer Variablen des Programms). Damit das Zeilenende-Zeichen nicht separat
gelesen werden muss, stellt Pascal die Standardprozedur Readln (Abk. für Read line) zur Verfügung. Diese erlaubt es, die letzten zwei Programmzeilen in obigem Beispiel wie folgt zusammen
zu fassen:
Readln(Textzeichen, Zeile);
Die Prozedur Readln liest also eine Zeile Text bis ans Ende und bewirkt, dass der nächste Aufruf
von Read oder Readln die nächste Zeile lesen wird. Falls keine nächste Zeile vorhanden ist, führt
ein solches Read zu einem E/A-Fehler.
Beim Einsatz von Readln muss darauf geachtet werden, dass nicht über Text gesprungen wird,
den man eigentlich hätte lesen wollen, denn die Prozedur liest soviel wie in der Variablen, in die
gelesen wird, Platz vorhanden ist und ignoriert den Rest bis zum Zeilenende.
Textfiles haben die nützliche Eigenschaften, dass wir mit der Funktion Eoln (Abk. für "End of line")
Zeilenumbrüche erkennen können. Sie erlauben auch das Lesen und Schreiben von Zeichen, die
nicht vom Typ Char sind. Die folgende Anweisung liest alle Zeichen einer Zeile einzeln bis ans Ende
der Zeile:
while not Eoln(Textzeichen) do Read(Textzeichen, Zeichen);
Wenn ein Programm versucht über das Ende eines Files hinaus zu lesen, entsteht ein E/A-Fehler,
der das Programm zum Abbruch bringt. Mit der Funktion EOF (End Of File) können wir prüfen, ob
wir am Ende eines Files angekommen sind, denn EOF liefert den Wahrheitswert "TRUE" sobald
dies der Fall ist. Die vorsichtige Programmiererin prüft deshalb auf "End Of File" vor jeder ReadAnweisung:
if not EOF(Textzeichen) then Read(Textzeichen, Zeichen);
EOF kann natürlich auch als Bedingung eingesetzt werden, wenn Files innerhalb einer Schleife
bearbeitet werden:
while not EOF do . . .
Textfiles schreiben
Um ein Textfile fürs Schreiben vorzubereiten gibt es zwei Möglichkeiten: Die Prozedur Rewrite
und die Prozedur Append. Um eine zusätzliche Zeile mit dem Text "ETH Zuerich" ans Ende unserer
Datei Meintext.txt anzufügen, schreiben wir:
Zeile:= 'ETH Zuerich';
Append(Textzeichen);
Writeln(Textzeichen, Zeile);
Hätten wir
Write(Textzeichen, Zeile);
Dateien verarbeiten
6
geschrieben, wäre zwar der Text "ETH Zuerich" ans Ende der Datei geschrieben worden, aber ohne
Zeilenende-Zeichen.
Mit Reset geöffnete Textfiles sind schreibgeschützt (das heisst, sie können nur gelesen werden)
während in mit Rewrite und Append geöffnete Textfiles nur geschrieben werden kann. Typisierte und untypisierte Files (das Thema von Praxismodul7) lassen immer Lesen und Schreiben zu,
wobei es keine Rolle spielt, ob sie mit Reset oder Rewrite geöffnet wurden.
Automatische Typenkonvertierung
Textfile ist wohl der am meisten verwendete File-Typ. Deshalb stellt Pascal verschiedene spezielle
Routinen zur Verfügung damit Programmiererinnen Textfiles leichter verarbeiten können. Read
und Write beispielsweise erlauben es, dass Werte gelesen und geschrieben werden können, die
nicht vom Typ Char sind. Zwei Beispiele illustrieren diese Typenkonvertierung:
Var
Textzeichen: Textfile;
Zahl: Integer;
Wahr: Boolean;
Der Prozeduraufruf
Read(Textzeichen, Zahl)
liest eine Ziffernfolge im File Textzeichen, interpretiert diese als ganze Zahl vom Typ Integer
und speichert ihren Wert in der Variablen Zahl.
Mit der Anweisungsfolge
Wahr:= true;
Write(Textzeichen, Wahr)
wird am Ende des Files Textzeichen der String 'TRUE' angefügt.
2. Fehlerbehandlung
Pascal überprüft standardmässig ob Aufrufe von Eingabe-/Ausgabe-Prozeduren und -Funktionen
zu Problemen geführt haben (E/A-Fehler), die es verhindern, dass die Operation erfolgreich ausgeführt werden kann. Beim Auftreten eines Fehlers wird eine so genannte Exception (Fehlerbedingung, Ablaufunterbrechung) ausgelöst oder – falls die Exception-Behandlung deaktiviert ist – das
Programm abgebrochen. Die automatische Fehlerüberprüfung kann mit der Compiler-Direktive
{$I+} aktiviert und mit {$I-} deaktiviert werden.
Eine Compiler-Direktive ist ein Kommentar mit einer speziellen Syntax. In Delphi können Compiler-Direktiven überall dort eingesetzt werden, wo auch Kommentare erlaubt sind. Eine CompilerDirektive beginnt mit einem $ als erstem Zeichen nach dem Kommentarzeichen. Darauf folgt der
Dateien verarbeiten
7
Name der Direktive (bestehend aus einem oder mehren Buchstaben). Auf den Namen und die erforderlichen Parameter können Kommentare folgen.
Kommentar
Compiler-Direktive
Name
Parameter
{ $ I + }
Wenn eine Prozedur oder Funktion im {$I-} Modus kompiliert wurde, löst ein Eingabe/AusgabeFehler keine Exception aus. Man kann aber das Ergebnis einer Eingabe/Ausgabe-Operation durch
einen Aufruf der Standardfunktion IOResult selber ermitteln:
AssignFile(Textzeichen, 'C:\Meintext.txt')
{$I-}
Reset(Textzeichen);
{$I+}
if IOResult = 0 {File wurde fehlerfrei geöffnet}
then {File verarbeiten}
Rufen Sie die Funktion IOResult auch dann auf, wenn Sie der Fehler nicht interessiert, denn die
Fehleranzeige wird erst durch den Aufruf von IOResult gelöscht. Wenn Sie das nicht tun und im
Modus {$I+} arbeiten, dann schlägt der nächste Aufruf einer Eingabe/Ausgabe-Prozedur aufgrund des nicht gelöschten IOResult-Fehlerstatus fehl.
Weil Pascal-Files eine Abstraktion von tatsächlichen Dateien sind, enthält ein Programm keine Information über die physische Eigenart einer Datei. Deshalb müssen wir nicht angeben, ja nicht
einmal wissen, ob die Daten von einem Stapel Lochkarten, einem Plattenspeicher, oder einer Tastatur kommen. Es ist die Aufgabe des Betriebssystems, konkrete Dateien ihrem Programm während seiner Laufzeit zuzuordnen. Das Betriebssystem erreicht dies über Instruktionen, welche
nicht zu unserem Programm gehören.
3. Programmentwurf
Mit den im Laufe der ersten fünf E.Tutorials erarbeiteten Grundlagen sind Sie nun in der Lage umfangreichere Programme zu schreiben und können damit auch anspruchsvollere Probleme lösen.
Problemlösen und Programmentwurf sind gewissermassen zwei Seiten der gleichen Münze.
Problemlösungsmethoden
Für die Aufgabe, eine Lösung für ein Problem zu entwerfen, gibt es verschiedene Methoden, von
denen jede in bestimmten aber nicht in allen Situationen erfolgreich angewandt werden kann.
Eine davon ist, dass man versucht, das Problem rückwärts zu lösen. Wenn das Problem beispielsweise darin besteht, für eine gegebene Eingabe eine bestimmte Ausgabe zu erzeugen, könnte
Dateien verarbeiten
8
man mit der Ausgabe beginnen und versuchen, auf die gegebene Eingabe zurück zu schliessen.
Diese Vorgehensweise ist typisch für jemanden, der z.B. den Algorithmus für das Schlaufen eines
komplizierten Knotens herausfinden möchte. Man beginnt mit dem vollständigen Knoten und versucht, währenddem man ihn löst, herauszufinden wie er erzeugt wurde.
Ein anderer, allgemeiner Problemlösungsansatz ist, ein verwandtes Problem zu suchen, das entweder einfacher zu lösen ist oder das bereits gelöst wurde, um anschliessend die Lösung auf das
aktuelle Problem anzuwenden. Diese Technik ist besonders nützlich für die Entwicklung von Programmen, weil hier eine der grössten Schwierigkeiten nicht darin besteht, einen Einzelfall zu lösen, sondern einen allgemeinen Algorithmus zu finden, der alle Fälle eines Problems löst. Genauer
gesagt, falls wir die Aufgabe hätten, ein Programm zu entwickeln, das eine Liste mit Namen sortiert, dann ist unsere Aufgabe nicht eine spezielle Liste zu sortieren, sondern einen Algorithmus zu
finden, mit dem irgendeine Namensliste sortiert werden kann.
Schrittweise Verfeinerung
Ein weiteres Verfahren besteht im Wesentlichen darin, dass man nicht versucht eine Aufgabe als
Ganzes auf einmal zu lösen sondern durch eine schrittweise Verfeinerung mit der ein gegebenes
Problem als aus verschiedenen Teilproblemen bestehend betrachtet wird. Die Idee ist, dass man
durch Zerlegen eines Problems in Teilprobleme in die Lage kommt, die gesamte Lösung schrittweise anzugehen, wobei jeder einzelne Schritt einfacher zu bewältigen ist als das ursprüngliche Problem. Die schrittweise Verfeinerung schlägt nun vor, jeden dieser Schritte wiederum in kleinere
Schritte zu zerlegen und diese wiederum in noch kleinere Einheiten, bis das ganze Problem auf
eine Sammlung einfacher Teilprobleme reduziert ist.
So gesehen ist die schrittweise Verfeinerung eine so genannte Top-Down-Methode indem sie vom
Allgemeinen zum Spezifischen schreitet. Im Gegensatz dazu bewegt man sich mit Bottom-UpMethoden vom Spezifischen zum Allgemeinen. In der Praxis werden beide Vorgehensweisen mit
Vorteil so eingesetzt, dass sie sich ergänzen.
Eine nützliche Eigenschaft von Lösungen, die durch schrittweise Verfeinerung erzeugt werden ist,
dass sie eine natürliche modulare Struktur aufweisen, und darin liegt einer der wichtigsten Gründe
für die Beliebtheit dieser Methode beim Entwurf von Algorithmen. Wenn ein Algorithmus eine
natürliche modulare Struktur aufweist, dann werden die entsprechenden Programme überschaubarer und leichter zu handhaben. Zudem sind die Teilprobleme, die durch eine schrittweise Verfeinerung erzeugt werden, mit der Programmierung in Teams gut vereinbar. Denn eine Software, die
in Teilaufgaben (oder mögliche Module) gegliedert ist, kann auf die Mitglieder eines Teams aufgeteilt werden, damit diese sie unabhängig voneinander bearbeiten können.
Die Methode der schrittweisen Verfeinerung wurde zu einer der wichtigsten Entwurfsmethodiken
in der Datenverarbeitung und ist deshalb eine Technik, mit der Programmierer vertraut sein müssen. Aber sie bleibt nur eine von vielen Entwurfsmethoden, die für Informatiker von Interesse sind.
Dateien verarbeiten
9
Problemlösungsphasen
Dass Problemlösen mehr eine Kunst als eine Wissenschaft ist hat schon in den 40er Jahren der
ungarische Mathematiker Georg Polya gezeigt als er diesen Vorgang in folgende, lose definierte
Phasen gegliedert hatte:
Phase 1:
Das Problem verstehen (Erkennen, was gefragt wird)
Phase 2:
Einen Plan ausdenken (Eingehen auf das, was gefragt wird)
Phase 3:
Den Plan ausführen (Das Resultat erarbeiten)
Phase 4:
Das Resultat evaluieren (Wie genau ist es, kann es für die Lösung anderer Probleme
verwendet werden?)
Auf das Entwerfen von Programmen angewandt kann dieses Vorgehen zu folgender Liste führen:
Phase 1:
Das Problem verstehen (Analyse)
Phase 2: Formulierung der Aufgabenstellung als Problem der Datenverarbeitung (Modellierung)
Phase 3: Einen detaillierten Problemlösungsablauf erarbeiten (Entwurf eines Algorithmus)
Phase 4: Formulieren dieser Lösung als Programm (Implementation)
Phase 5: Ausführen, Testen des Programms (Verifikation)
Die Erfahrung zeigt, dass diese Phasen nicht unbedingt in der aufgeführten Reihenfolge ausgeführt werden. Erfolgreiche Problemlöser beginnen oft mit dem Formulieren von Problemlösungsstrategien (Phase 3) bevor das Problem ganz verstanden ist (Phasen 1 und 2). Wenn diese Strategien sich als nicht erfolgreich erweisen (während den Phasen 4 und 5), führt dies meistens zu einem besseren Verständnis der Eigenheiten des Problems und man sollte dadurch in der Lage sein
andere, erfolgreichere Strategien zu formulieren.
4. Hilfsmittel für den Programmentwurf
Zwei oft angewandte Methoden um die Arbeit während den ersten drei Phasen des Programmentwurfs zu unterstützen sind Formulierungen in Pseudocode und Flussdiagramme (Programmablaufplan)
Pseudocode
Pseudocode (deutsch: Scheincode) bezeichnet eine Darstellung eines Algorithmus in einer Schreibweise in der Art einer Programmiersprache, die aber in der Regel näher an der menschlichen Sprache ist, als ausformulierter Programmcode. Pseudocode wird dann eingesetzt wenn die Funktionsweise eines Algorithmus im Vordergrund und die sprachspezifische Implementation im Hintergrund steht. Dies führt zu grösserer Übersichtlichkeit, weil triviale Codestücke je nach belieben
stark abgekürzt werden können und Formalitäten nicht "die Sicht versperren".
Der Algorithmus zur Umwandlung von Grad Fahrenheit in Grad Celsius könnte in Pseudocode etwa so formuliert werden:
read(F)
Dateien verarbeiten 10
subtrahiere 32
multipliziere mit 5/9
write(C)
Flussdiagramme
Ein Flussdiagramm (engl. flowchart) ist ein Ablaufdiagramm für ein Computerprogramm, das
auch als Programmablaufplan bezeichnet wird. Flussdiagramme werden oft unabhängig von
Computerprogrammen zur Darstellung von Prozessen und Tätigkeiten eingesetzt.
Hauptsächlich werden die folgenden drei Elemente verwendet:
Mit diesen Symbolen lassen sich die fundamentalen Konstruktionselemente für Algorithmen,
nämlich Sequenz, Fallunterscheidung und Wiederholung, grafisch darstellen.
Als Beispiel einer Sequenz sehen Sie rechts
das Flussdiagramm für die Umwandlung
von Grad Fahrenheit in Grad Celsius.
Zur Illustration der Fallunterscheidung betrachten wir den Algorithmus für die Umwandlung von Grad Fahrenheit in Grad Celsius mit der Ergänzung, dass die Umrechnung nur ausgeführt werden soll, wenn die
Temperatur nicht unter den Gefrierpunkt
fällt.
Dateien verarbeiten 11
Dieses Flussdiagramm zeigt den Programmablauf, wenn eine Tabelle erzeugt werden
soll, die für jedes Grad Fahrenheit zwischen
0 und 100 den entsprechenden Wert in Celsius angibt. Ein typischer Fall einer einfachen
Wiederholung.
Dateien verarbeiten 12
B. Tutorial
5. Angewandter Programmentwurf
Im Zentrum dieses Tutorials steht der Programmentwurf, auf Englisch Software Design. Dabei
handelt es sich nicht nur um das eigentliche Schreiben des Programmcodes, sondern es geht um
die gesamte Software-Entwicklung, welche sich von der Analyse des Problems in der realen Welt
über die Modellierung und das Programmieren bis zum Testen des fertigen Programms erstreckt.
Die nötigen Programmier-Grundlagen und -Konzepte für das Implementieren eines funktionierenden Programms (das Handwerk) haben Sie sich in den bisherigen 5 Modulen erarbeitet. Wir
werden uns darum in diesem Tutorial hauptsächlich auf die Analyse-, die Modellierungs- und die
Entwurfs-Phasen konzentrieren.
Wir haben uns entschieden dieses Tutorial auf Papier abzugeben. Die Schritte, die wir behandeln,
werden auch im professionellen Progammierumfeld oft mit Papier, Bleistift und Radiergummi angegangen, für kreative Prozesse sind das einfach sehr praktische Hilfsmittel.
Sie haben im Einführungstext mit der Gliederung des Programmentwurfs in 5 Phasen eine mögliche Vorgehensweise bei der Entwicklung von komplexeren Programmen kennen gelernt. In diesem praktischen Teil wollen wir nun diesen Ablauf anhand eines konkreten Problems umsetzen.
Bevor wir mit der Phase 1 beginnen können, muss Ihnen die Problemstellung bekannt sein.
Lesen Sie dazu den folgenden Artikel über das Benford-Gesetz, welcher im Januar 2006 in einem
NZZ Folio zum Thema „Statistik“ erschienen ist.
Das Rätsel der abgegriffenen Seiten
Vor über hundert Jahren stiess ein Astronom auf ein merkwürdiges statistisches Gesetz, das heute
Steuerbetrüger entlarven soll.
Es ist das seltsamste Gesetz, das die Statistik zu bieten hat. Nehmen Sie Ihre Steuererklärung und zählen Sie, wie oft eine Eins am Anfang einer Zahl steht, dann zählen Sie die
Zwei, die Drei, die Vier, die Fünf, bis Sie bei der Neun sind. Anders als erwartet, werden Sie
die Ziffern Eins bis Neun nicht gleich häufig an der ersten Stelle finden, sondern oft die
Eins häufiger als die Zwei, die Zwei häufiger als die Drei und die wiederum häufiger als die
Vier, die Fünf, die Sechs und so weiter. Nicht nur Steuererklärungen bergen dieses Muster,
sondern auch Börsenkurse, Bevölkerungszahlen, Dateigrössen. Selbst wer alle Zahlen einer
Ausgabe der «Schweizer Illustrierten» oder die Gewichte von hundert zufällig im Garten
gesammelten Steinen untersucht, wird auf die geheimnisvolle Ziffernverteilung stossen.
Dem amerikanischen Astronomen Simon Newcomb fiel bereits im 19. Jahrhundert auf,
Dateien verarbeiten 13
dass die ersten Seiten von Logarithmentafeln abgegriffener waren als die letzten: offenbar wurde der Logarithmus einer Zahl, die mit Eins begann, am häufigsten nachgeschlagen. Warum das so war, blieb Newcomb verborgen, aber er fand eine Formel, mit der sich
die genauen Anteile der Ziffern berechnen liessen. In einer grossen Menge Zahlen beginnen 30 Prozent mit einer Eins, 18 Prozent mit einer Zwei, 13 Prozent mit einer Drei und so
weiter bis zur Neun, die in 5 Prozent an erster Stelle steht. Newcomb publizierte seine Beobachtung 1881 im «American Journal of Mathematics». Dann ging sie vergessen, bis 1938
der Physiker George Benford auf dasselbe Phänomen stiess.
Benford zählte 20 229 Anfangsziffern in Sterbetafeln, Baseballstatistiken und Zeitungen
aus und fand in vielen seiner Beispiele Newcombs Muster. Allerdings nicht in allen: Die
Zahlen, die dem Benford-Gesetz, wie es bald genannt wurde, folgten, durften weder ganz
zufällig sein wie Lottozahlen noch starr vorgegeben wie physikalische Konstanten oder
Jahrzahlen. Am besten erfüllten die Zahlen die Regel, wenn sie den Bereich zwischen diesen Extremen belegten. Benford nannte sie «unregelmässige Zahlen». Das klingt diffus
und war es auch. Eine genauere Definition konnte Benford nicht geben, und auch den
Grund für die eigentümlichen Häufigkeiten fand er nicht. Den suchten Mathematiker
noch über 50 Jahre vergeblich.
Dabei machte ihnen besonders der merkwürdige Umstand zu schaffen, dass sich das
Muster am deutlichsten zeigte, wenn die Zahlen aus ganz verschiedenen Kategorien
stammten. Die beste Übereinstimmung mit der Theorie stellte Benford bei Zahlen aus Zeitungsartikeln fest: Körpergrössen, Temperaturen, Verluste, Erdbebenstärken.
Aber was haben 1 Meter 83, 22 Grad und 3 Millionen Dollar gemeinsam? Erst 1996 fand der
Mathematiker Theodore Hill heraus, dass gerade in der Verschiedenheit des Zahlenmaterials der tiefere Grund für das Benford-Gesetz liegt: Alles, was sich messen lässt, ist letztlich das Resultat natürlicher Prozesse. Zahlen, die solche Prozesse beschreiben, folgen mathematisch definierten Verteilungen. Hill konnte zeigen: Wenn man zufällig Zahlen aus
unterschiedlichen Verteilungen auswählt, werden ihre Anfangsziffern dem BenfordGesetz gehorchen, auch wenn die einzelnen Verteilungen keine Ähnlichkeit damit haben.
Das Benford-Gesetz ist die Mutter aller Verteilungen. Das ist selbst für Mathematiker
nicht leicht zu verstehen, aber es ist so.
Die wundersame Ziffernverteilung blieb eine statistische Kuriosität, bis Steuerbeamte und
Controller merkten, dass auch die Ziffern in Bilanzen dem Gesetz unterliegen. Und Experimente zeigten, dass Menschen spontan nicht in der Lage sind, Zahlen zu erfinden, die
dem Benford-Gesetz gehorchen. Mit dem Zählen von Ziffern sollte es also möglich sein,
Betrügern auf die Spur zu kommen.
«Als ich zum ersten Mal vom Benford-Gesetz hörte, dachte ich, das ist ja wunderbar», sagt
der Mathematiker Jean-Luc Wichoud und fügt dann bedauernd an, «in der Praxis sieht es
leider etwas anders aus.» Wichoud arbeitet bei der Eidgenössischen Steuerverwaltung
und setzt sich mit der Anwendung des Benford-Gesetzes bei der Mehrwertsteuer auseinander. Dabei zeigt sich: Die Resultate aus den Millionen von geprüften Zahlen stimmen
nicht exakt genug mit der Benford-Verteilung überein. Dafür gibt es zwei mögliche Gründe: Entweder es wird grossflächig betrogen, oder es gibt bei der Mehrwertsteuer systematische Abweichungen vom Benford-Gesetz. Es kann etwa vorkommen, dass je nach Branche und Berechnungsart die Häufigkeit der Anfangsziffern ausschert. In diesem Fall müssen die Daten vor der Analyse entsprechend bearbeitet werden. Wie genau, versucht Wichoud herauszufinden. Erst wenn er es weiss, können einzelne Abrechnungen geprüft
werden.
Sein Vorgesetzter Bernhard Stebler warnt vor übertriebenen Hoffungen. Zwar blieb ein
Dateien verarbeiten 14
vor drei Jahren auf andere Weise aufgedeckter Betrugsfall auch beim Test nach Benford
hängen. Aber eben erst im Nachhinein und nachdem das «Sieb richtig eingestellt war»,
wie Stebler sagt, die Daten also richtig bearbeitet worden waren.
Das Benford-Gesetz ist eine von vielen Möglichkeiten, mit denen sich vielleicht dereinst
ein Betrug aufspüren liesse - oder auch nicht. Im Internet kursiert bereits ein Programm
zur Erzeugung von Anfangsziffern in den richtigen Häufigkeiten. (Reto U. Schneider, NZZ
Folio 1/06)
Dateien verarbeiten 15
Phase 1 – Analyse
Beschreiben und Analysieren des Problems
Sie haben sich nun das nötige Hintergrundwissen über das Benford-Gesetz angeeignet. Können
Sie sich erklären, wieso das Benford-Gesetz nicht für alle Zahlen resp. Zahlenquellen gilt? Welche
Ursachen können für eine Abweichung verantwortlich sein?
Ziele dieser Phase
In der Analyse-Phase wollen wir das Problem in der Realität verstehen und beschäftigen uns mit
der Frage wie ein Mensch die Aufgabe lösen würde. In der Software-Entwicklung beinhaltet diese
Phase meist auch Gespräche mit den zukünftigen Benutzerinnen, um deren Wünsche an das System kennen zu lernen. Daraus lassen sich die Ziele und Anforderungen an das Programm ableiten.
Damit Sie sich eine Vorstellung machen können, wie man das Benford-Gesetz anwendet, finden
Sie untenstehend einen kurzen Text. Überprüfen Sie, ob die Zahlen in diesem Kochrezept Benfordverteilt sind.
Suchen Sie im Rezept alle Anfangsziffern und notieren Sie sich laufend im Kasten rechts die Anzahl der gefundenen Ziffern.
Glarner Zoggle
Zutaten für Teig:
80 g Schabzieger
270 g Weissmehl
200 g Knöpflimehl
2 TL Salz
1 dl Milch
1 dl Wasser
4 Eier
Zwiebel-PetersilieMischung:
1 Zwiebel
1 Bund Petersilie
30 g Butter
Salz, Pfeffer, Muskatnuss
Für den Teig Weiss-, Knöpflimehl und Salz in einer Schüssel mischen, eine Mulde formen. Milch,
Wasser und Eier verquirlen, hineingiessen. Mit
einer Kelle zu einem Teig verrühren. Teig mit
der Kelle klopfen, bis er glatt ist und Blasen
wirft. Schabzieger an der Bircherraffel reiben
und unter den Teig rühren. Zugedeckt 30 Minuten
ruhen lassen. Teig portionenweise durch das
Knöpflisieb in knapp siedendes Salzwasser streichen. Zoggle ziehen lassen, bis sie an die Oberfläche steigen. Mit einer Schaumkelle herausnehmen, abtropfen lassen. In einer vorgewärmten
Schüssel zugedeckt warm stellen. Zwiebeln schälen, hacken und in Butter bei kleiner Hitze 5 Minuten andämpfen. Gehackte Petersilie beifügen und
kurz dämpfen, würzen. Zwiebel-Petersilie-Mischung
über die Zoggle geben. Nach Belieben mit Schabzieger bestreuen.
Platz für Ihre Auswertung:
Dateien verarbeiten 16
Insgesamt sollten Sie im Rezept 12 Anfangsziffern gefunden haben und deren Verteilung zeigt,
dass diese offenbar ebenfalls dem Benford-Gesetz gehorchen.
Während der Bestimmung der Häufigkeitsverteilung der Anfangsziffern haben Sie soeben ganz
intuitiv einen Lösungsalgorithmus für das Benford-Gesetz angewandt. Da Computer allerdings
weder intelligent sind, noch intuitiv handeln können, müssen Sie diese komplexen Abläufe in einzelne Zwischenschritte aufschlüsseln. In dieser Phase der Softwareentwicklung beschränken wir
uns auf von Menschen ausführbare Algorithmen. In der nächsten Phase 2 "Modellbildung" werden
wir dann diese Algorithmen in ein Modell umwandeln um schlussendlich durch Maschinen ausführbare Algorithmen zu erhalten.
Schreiben Sie Ihren Lösungsalgorithmus möglichst detailliert auf.
Menschlicher Lösungs-Algorithmus für das Benford-Gesetz:
-
Bleistift zur Hand nehmen
-
Am Anfang des Rezepts mit dem Lesen beginnen
-
1. Zahl suchen
-
…
Ziele und Anforderungen
Die Ziele unseres Programms ergeben sich aus der Aufgabenstellung und sollten zu Beginn des
Software-Design Prozesses festgehalten werden. Die weiteren Aufgaben können anschliessend
durch schrittweise Verfeinerung modelliert und implementiert werden.
Dateien verarbeiten 17
Wir setzen uns für diese Aufgabe folgende Ziele:
Wir möchten ein Programm entwickeln, welches einen Text (beispielsweise den Geschäftsbericht
2005 der Migros) einliest und dessen Benford-Verteilung untersucht. Die Resultate sollen in eine
Datei geschrieben werden, so dass in Excel ein Diagramm erstellt werden kann.
Als nächstes definieren wir, welche Anforderungen wir an das Programm haben, wir formulieren
damit gewissermassen die Leitplanken unseres zu entwickelnden Systems:
-
Dem Programm können beliebige (Text-)Dokumente zur Verarbeitung übergeben werden
-
Das Programm soll alle Anfangsziffern des kompletten Dokuments heraussuchen. Textdateien sind daran zu erkennen, dass sie zeilenweise angeordnet sind und Buchstaben sowie
Ziffern und Leerzeichen enthalten. Zudem besitzen sie am Ende jeder Zeile ein Zeilenendzeichen (Eoln) sowie am Textende ein Eof-Zeichen.
-
Die Ausgabe aller gefundenen Ziffern soll in eine Datei und in einem Format erfolgen, welches die Weiterverarbeitung in einer Tabellenkalkulation (Excel) erlaubt. Dazu werden wir
zwischen jedem Wert ein ; einfügen und nach jeweils Ziffern auf eine neue Zeile wechseln
-
Ein Formular (GUI) soll die Benutzerin die Angabe der zu verarbeitenden Datei erleichtern
und sie zusammenfassend über die Resultate der Berechnung informieren
Unser bisheriger Lösungsansatz setzt den Menschen als Prozessor ein. Da wir aber ein Programm
schreiben wollen, in dem der Computer diese Arbeit übernehmen soll, müssen wir in unserem Algorithmus den Menschen durch eine Maschine ersetzen. Dieser Schritt passiert in der Modellierung, welche das Thema der nächsten Phase ist.
Dateien verarbeiten 18
Phase 2 – Modellierung
Formulieren der Aufgabenstellung als Problem der Datenverarbeitung
Mit der Modellbildung beginnt die Konstruktion der Lösung. Ziel dieser Phase ist es ein Modell unseres Programms zu erstellen, welches den Anforderungen aus Phase 1 gerecht wird. Die mit der
Modellbildung einhergehende Abstraktion ist einer der wichtigsten und auch schwierigsten
Schritte beim Problemlösen. Das Modell hilft uns, heikle und kritische Programmpunkte zu identifizieren und in der nächsten Phase 3 detailliert auszuarbeiten.
Für die Modellbildung verwenden die meisten Programmierer so genannte Flussdiagramme. Sie
haben im Einleitungstext das Prinzip der Flussdiagramme und dessen Symbole kennen gelernt.
Sehr hilfreich beim Entwerfen eines Flussdiagramms ist das vorgängige Definieren des Inputs und
des Outputs des Programms (I/O Schnittstelle): Welche Eingaben stehen meinem Programm zur
Verfügung und welche Daten soll es liefern?
In der folgenden Grafik sehen Sie ein mögliches Modell unseres Benford-Programms. Studieren Sie
das Diagramm und versuchen Sie den Programmablauf nachzuvollziehen. Können Sie einen kritischen Programmpunkt erkennen?
Dateien verarbeiten 19
Input
Text
Start
Migros
Geschäftsbericht
Datei auswählen
Nein
Datei vorhanden?
Ja
Zeichen
einlesen
End of file?
Ja
Nein
Anfangsziffer suchen
Zähler für Ziffer
und Total um 1 erhöhen
Output
Prozentuelle Anteile ausrechnen
Anfangsziffern
und Häufigkeit
ausschreiben
Excel-Datei
Stop
Ein kritischer Punkt versteckt sich hinter der Bezeichnung "Anfangsziffer suchen". Der Mensch
kann die erste Ziffer einer Zahl in einem grossen Text problemlos erkennen und benennen. Ein
Computer kann den Text aber nicht als Ganzes erfassen, sondern liest ihn zeichenweise ein. Zu
einem bestimmten Zeitpunkt während dem Programmablauf kennt der Computer also nur genau
ein Zeichen des ganzen Textes. Wie müssen wir vorgehen, damit der Computer trotzdem alle Anfangziffern eines Textes findet?
Die detaillierte Ausarbeitung dieses Programmpunktes und das Erstellen eines
lesbaren Algorithmus ist Ziel der nächsten Phase.
Computer-
Dateien verarbeiten 20
Phase 3 – Entwurf
Erarbeiten eines detaillierten Problemlösungsablaufs
Mit Hilfe unseres Modells haben wir in der vorangegangenen Phase den Programmpunkt "Anfangsziffer suchen" als kritisches Element identifiziert.
Nun fokussieren wir uns nur noch auf diesen Programmabschnitt und setzen diesen schrittweise
in Pseudocode um. Wie Sie in der Einführung gesehen haben, versteht man unter Pseudocode eine
formlose, individuelle Notation, in der ein Algorithmus beschrieben wird. Dazu verwenden wir eine Mischung von Deutsch und Pascal. In der nächsten Phase übertragen Sie dann diesen Pseudocode Zeile für Zeile in Programmcode.
Kritische Programmabschnitte aufschlüsseln und ausarbeiten
Zuerst gilt es wiederum den Input und Output (I/O) dieses kritischen Programmabschnitts zu
bestimmen. Wie wir aus unserem Flussdiagramm entnehmen, können wir folgende I/O-Daten
festhalten:
Input: Ein Zeichen aus der Datei (z.B. "h" aus der Rezept-Zeile "...mehl 1 Zwiebel…")
Output: Falls eine Anfangsziffer gefunden wurde, soll diese Ziffer
an den nächsten Programmschritt weitergegeben werden. (z.B. "1")
•
Überlegen Sie sich, wie Sie folgende Anfangsziffern finden könnten, obwohl Sie durch eine
Schablone nur ein Zeichen pro Mal erkennen könnten? (Sie dürfen natürlich die Schablone
über die Datei bewegen).
•
Wie muss ein Computer bei der Erkennung vorgehen? Welche Zeichen muss er dabei unterscheiden?
(Dazu ist es hilfreich, wenn Sie alle Leerschläge mit Leuchtstift einfärben, sowie alle EolnZeichen eintragen).
Dateien verarbeiten 21
Es gibt mehrere Möglichkeiten, wie man diese Anfangsziffern finden kann. Wir schlagen folgenden Weg vor, wobei Sie natürlich frei sind, Ihre eigene Idee auszuprobieren:
Da wir immer nur die Anfangsziffer einer Zahl suchen, interessiert uns vor allem das Zeichen vor
dieser Ziffer. Dieses Zeichen ist nämlich entweder ein Leerzeichen (vgl. " 1 Zwiebel") oder aber ein
Eoln-Zeichen (vgl. "80 g Schabziger"). Der Programmabschnitt "Anfangszeichen suchen" braucht
demnach zwei Informationen um die Anfangsziffer zu identifizieren: Wie lautet das aktuelle Zeichen (Ziffer?) und wie lautete das Zeichen vor dieser Zahl (Leerzeichen oder Eoln?).
Konkret heisst dies, dass der Programmabschnitt so lange ein Zeichen aus der Datei verlangt, bis
er eine Ziffer gefunden hat. Um überprüfen zu können, ob es sich bei dieser Ziffer um die Anfangsziffer handelt, muss ihm das vorgängige Zeichen bekannt sein. Deshalb muss vor dem Einlesen
eines neuen Zeichens das jeweils alte Zeichen in einer temporären Variablen abgelegt werden.
Zudem muss überprüft werden, ob es sich beim neuen Zeichen um ein Eoln-Zeichen handelt. Diese Information muss auch in einer temporären Variablen abgelegt werden.
Falls die Funktion eine Anfangsziffer gefunden hat, wird diese dem nächsten Programmschritt übergeben. Transformieren Sie nun diese Anleitung in Pseudocode:
Pseudocode des Programmschritts "Anfangsziffer suchen":
var zeichen, vorgaengiges_zeichen: Char
var vorgaengiger_zeilenumbruch: Boolean
wiederhole so lange bis das Ende der Datei (Eof) erreicht:
{
zeichen := zeichen_aus_datei()
….
FALLS (zeichen = eine Zahl zwischen 1-9)
DANN { vorgaengiges_zeichen = " " ODER vorgaengiger_zeilenumbruch = true }
…..
Dateien verarbeiten 22
Externe und interne Datenstrukturen festlegen
Neben der Ausarbeitung der kritischen Programmschritte in Pseudocode, umfasst die EntwurfsPhase aber auch noch das Festlegen der Datenstruktur. Die Datenstruktur repräsentiert dabei die
Gegenstände der realen Welt, in unserem Fall also die Eingabe- sowie die Ausgabedatei. Zusätzlich
muss aber auch die Programm-interne Datenstruktur festgelegt werden (z.B. lokale, globale Variablen und Arrays).
Das Definieren der Datenstrukturen ist wichtig, da diese uns während dem gesamten Programmablauf zur Verfügung stehen und wir mit unseren Prozeduren und Funktionen auf diese zugreifen.
Geschickt ausgewählte Datenstrukturen sind entscheidend für einen optimalen Programmablauf
und eine etwaige Fehlersuche!
Die Eingabedateien liegen uns in Form von Textdateien vor. Deren Struktur können wir nicht beeinflussen. Sie werden immer von oben links nach unten rechts Zeichen für Zeichen eingelesen.
Als Programm-interne zentrale Datenstruktur verwenden wir zwei eindimensionalen Arrays[1-9].
Darin speichern wir, wie oft eine Zahl gefunden wurde und die prozentuale Verteilung der Zahlen.
Damit sie uns in allen Prozeduren und Funktionen zur Verfügung stehen, deklarieren wir sie als
globale Arrays. Die Informationen in diesen Arrays werden wir sowohl im GUI als auch in eine Datei ausgeben. Wie immer wählen wir zwei aussagekräftige Arraynamen: ZiffernAbsolut[] und ZiffernProzent[]
Die Ausgabedatei (CSV-Datei -> comma seperated values) müssen wir an den Excel-Importfilter
anpassen. Damit Sie in Excel erfolgreich weiterverarbeitet werden kann, empfehlen wir folgende
Struktur (beachten Sie, dass die einzelnen Zahlen durch ein Semikolon ";" voneinander getrennt
sind).
CSV-Datei:
Import in Excel:
Ziffer;Absolut;Prozent
1;4;0.333333333333333
2;3;0.25
3;2;0.166666666666667
4;1;0.0833333333333333
5;1;0.0833333333333333
6;0;0
7;0;0
8;1;0.0833333333333333
9;0;0
GUI skizzieren
Durch das Skizzieren des GUIs schliessen wir die Entwurfsphase ab. Wie Ihnen nun bestimmt auffällt, sind wir bei den vorangegangenen E.Tutorials jeweils an diesem Punkt eingestiegen. Dies
mag vielleicht bei kleinen Projekten noch funktionieren, bei grösseren und komplexeren Programmier-Projekten ist jedoch eine ausführliche Planung unerlässlich und spart schlussendlich
viel Zeit und vor allem Nerven.
Dateien verarbeiten 23
Studieren Sie die nachfolgende Skizze und überlegen Sie sich, mit welchen Delphi Objekten man
diese Ausgabe erreichen könnte.
Für die Resultate-Tabelle werden wir ein neues Delphi-Objekt verwenden (StringGrid), welches
eine einfache tabellarische Darstellung von Werten ermöglicht. Die restlichen Objekte kommen
Ihnen bestimmt bekannt vor, da wir sie ja bereits in anderen Aufgaben verwendet haben.
An diesem Punkt schliessen wir nun die Planungsphase ab und wechseln das Arbeits-Medium. In
der nächsten Phase werden Sie nämlich die auf Papier entwickelten Flussdiagramme und Pseudocodes in Delphi umsetzen und am Computer ausprogrammieren. Während dieser Implementierungsphase kann es übrigens durchaus vorkommen, dass Sie auftretende Probleme kurz skizzieren
und mit Hilfe von Flussdiagrammen oder Pseudocode lösen.
Dateien verarbeiten 24
Phase 4 – Implementation
Formulieren der Lösung als Programm
Diesen Schritt kennen Sie aus den vorhergehenden Lektionen bereits bestens. In den bisherigen
Modulen haben wir uns hauptsächlich auf diese Phase konzentriert. Wir werden Ihnen daher bei
diesem Programm mehr Freiheiten lassen, damit Sie Ihre persönliche Vorgehensweise entwickeln
können. Trotzdem werden wir ein grobes Ablaufsschema vorgeben und neue Elemente und Konzepte kurz vorstellen.
Auf jeden Fall empfiehlt sich bei Fragen die kontext-sensitive Hilfe von Delphi - die immer nur einen F1-Tastendruck entfernt ist - zu konsultieren. Falls Sie vor dem Drücken auf F1 den Ausdruck
oder das Objekt zu dem Sie Hilfe wünschen im Unit- oder im Form-Fenster markieren, erscheint
automatisch die entsprechende Hilfe.
Wünschen Sie allgemeine Hilfe, können Sie die ganze Hilfedatei über Help-Topics nach Stichworten oder nach Themen geordnet durchsuchen.
Weitere Informationen zu den Operationen mit externen Files erhalten Sie im theoretischen Teil
und im Begleittext zur Vorlesung.
Erstellen Sie ein neues Projekt (BenfordProjekt) mit einer Unit-Datei (Benford)
GUI zeichnen
Erstellen Sie die grafische Benuterzoberfläche.
In der Phase 3 haben Sie bereits eine Skizze gesehen, wie das GUI ungefähr aussehen könnte. Die
meisten zu verwendenden Objekte kennen Sie bereits, für die 4 Aktionen (Einlesen, Kopieren, Berechnen, Ausschreiben) werden wir TButtons verwenden, und TEdit-Felder um die Namen der Einund Ausgabedatei anzuzeigen.
TStringGrid
Das einzig neue Element ist das Gitter-Objekt (TStringGrid,
) in welchem wir die Resultate
unserer Berechnung auf dem Bildschirm anzeigen werden. Sie finden es im Register „Zusätzlich“.
Im Objektinspektor können Sie die folgenden Eigenschaften des TStringGrid-Objekts anpassen:
-
ColCount/RowCount: Anzahl Spalten/Reihen
Dateien verarbeiten 25
-
DefaultColWidth/DefaultRowHeight: Höhe der Spalten/Reihen
-
FixedCols/FixedRows: Festlegen von Überschriftspalten und -reihen
-
ScrollBars: Verwenden von Scrollbalken
Erstellen Sie das GUI in Anlehnung an die Zeichnung von Phase 3. Geben Sie den Objekten eigene
Namen, damit Sie diese später einfacher aufrufen können
Datei einlesen und anzeigen
In diesem Schritt werden Sie eine Prozedur programmieren, welche beim Klicken auf den ‚Datei
auswählen’-Knopf ein Fenster öffnet, in welchem Sie eine Datei für die Analyse auswählen können.
Nach der Auswahl soll der Name in eine Variable geschrieben und in einem Textfeld angezeigt
werden.
Dies ist Voraussetzung, damit wir in den nächsten Schritten auf die Datei zugreifen können.
OpenDialog
Im Delphi gibt es für das Auswählen einer Datei eine vordefinierte Objektvorlage OpenDialog
(
), welches Sie im Register „Dialoge“ finden. Dieses Objekt wird für die Benutzerin unsichtbar
bleiben, es stellt unserem Programm aber die Funktionalität für die Dateiauswahl zur Verfügung.
Das Aufrufen des OpenDialogs geschieht mit dem Befehl
OpenDialog1.Execute
Dieser Befehl hat einen Rückgabewert, wie Sie es bereits von den Funktionen aus der letzten Übung kennen. Wurde eine Datei angegeben, meldet der Befehl ein True zurück, hat die Benutzerin
jedoch auf Abbrechen geklickt, gibt’s ein False zurück. Diese Rückgabewerte werden wir für eine
Eingabe-Validierung(Ist eine Datei vorhanden?) benutzen.
Der Befehl zum Auslesen des Namens der ausgewählten Datei lautet
OpenDialog1.Files[0]
Hinweis: Die Eigenschaft Files ist ein Array, auf dessen erste Komponente Sie mit [0] zugreifen können.
Fügen Sie dem Formular ein OpenDialog-Objekt hinzu. Verschieben Sie dieses an einen Ort im
Formular, wo es Ihnen nicht in den Weg kommt
Dateien verarbeiten 26
Generieren Sie für den „Datei auswählen“-Knopf einen OnClick-Event, welcher das OpenDialogObjekt aufruft
Überprüfen Sie anhand des Rückgabewerts ob eine Datei angegeben wurde, falls ja, schreiben Sie
den Namen der Datei in eine globale String-Variable und schreiben Sie den Dateinamen in das
entsprechende Textfeld
Datei kopieren und ausgeben
Bevor wir uns mit der Berechnung nach Benford beschäftigen, wollen wir uns absichern, dass das
Einlesen und Ausschreiben der Datei korrekt funktioniert. Dazu werden wir eine Testprozedur
schreiben, welche den Inhalt des ausgewählten Files in eine zweite Datei kopiert, also sozusagen
eine Backup-Datei erstellt.
Sie haben in der Vorlesung und der Einführung bereits einiges über die Arbeit mit externen Files
gehört und gelesen. Wichtig ist zu wissen, dass Sie nie direkt auf das File zugreifen können, sondern dass Sie die Datei immer einer Filevariable vom Typ Textfile zuweisen müssen. Genau so
wichtig ist, dass Sie die Datei am Schluss wieder schliessen, denn sonst kann kein anderes Programm darauf zugreifen.
Dateizugriff
Der Ablauf bei einem Lesezugriff auf ein File folgt demnach diesem Schema:
VAR
InputFile: Textfile;
Zeichen: Char;
BEGIN
AssignFile(InputFile, 'E:\rezept.txt');
Reset(InputFile);
Read(InputFile, Zeichen);
CloseFile(InputFile);
END.
Entsprechend wird beim Schreibzugriff auf eine Datei vorgegangen, mit dem Unterschied, dass
Rewrite statt Reset und Write/Writeln statt Read/Readln verwendet wird.
Eoln/Eof
Das Spezielle an Textfiles ist, dass jede Zeile mit einem Zeilenende-Zeichen (Eoln, End-of-line) und
das ganze File mit einem Fileende (Eof, End-of-file) abgeschlossen ist.
Dateien verarbeiten 27
ChangeFileExt
Der Datei, die durch das Kopieren entsteht, müssen Sie einen neuen Namen zuordnen, sonst weiss
das Programm nicht, wohin es den Output schreiben soll. Mit der Funktion
VAR DateiNameOriginal, DateiNameKopie: String;
DateiNameKopie := ChangeFileExt(DateiNameOriginal, '.bac')
können Sie die Datei-Endung ändern, aus rezept.txt wird dadurch rezept.bac (.bac für Backup).
Datei-Inhalt kopieren
Das eigentliche Kopieren können Sie mit zwei While-Schleifen (WHILE NOT Eof, WHILE NOT Eoln)
bewerkstelligen. Diese verschachtelte Schleifen-Konstruktion haben Sie bereits beim WohnblockProgramm im Tutorial 2 kennen gelernt.
Schreiben Sie eine OnClick-Prozedur für den ‚Datei kopieren’-Knopf, welcher überprüft, ob ein Eingabefile vorhanden ist. Ist das der Fall soll die Prozedur eine identische Kopie der Datei machen
mit einer anderen Datei-Endung.
Zum Testen können Sie das „Glarner Zoggel“-Rezept kopieren, welches Sie im E.Tutorial herunterladen können. Öffnen Sie die kopierte Datei im Notepad und überprüfen Sie ob die beiden Dateien
identisch sind.
Benford berechnen
Hier können Sie nun kreativ sein. Programmieren Sie den von Ihnen in der vorherigen Phase entwickelten Algorithmus zum Ausfindigmachen von Benford-Ziffern aus. Zeigen Sie die Resultate in
der Tabelle des Formulars an.
Resultate in Arrays abzulegen
Wir empfehlen, dass Sie zum Speichern der Häufigkeit der gefundenen Ziffern einen Array (ZiffernAbsolut: Array[1..9] of Integer) verwenden. In einem zweiten Array (ZiffernProzent: Array[1..9]
of Real) können Sie - nachdem das ganze File abgearbeitet wurde - die prozentuellen Anteil aller
Ziffern berechnen. Dazu bietet es sich an in einer separaten Variablen das Total der gefundenen
Ziffern abzuspeichern.
Dateien verarbeiten 28
TStringGrid aktualisieren
Ausserdem möchten wir der Benutzerin unser Resultat im Formular anzeigen, dafür haben wir ja
bereits eine Tabelle mithilfe eines TStringGrid-Objekts bereitgestellt. Wie können Sie nun die Daten aus den Arrays in die Gitterstruktur rübertransformieren? Hier kommt uns entgegen, dass die
Zellen eines TStringGrids ebenfalls wie Elemente eines Arrays behandelt werden können.
Um in der ersten Reihe der Tabelle eine Überschrift mit alle Ziffern zu erzeugen, können wir demnach folgenden Code verwenden:
FOR i := 1 TO 9 DO
StringGrid1.Cells[i-1,0] := IntToStr(i);
Nach dem gleichen Prinzip können Sie so die Resultate aus den Häufigkeits- und Prozent-Arrays
auslesen und im Gitter anzeigen lassen.
Zeichen in Ziffern umwandeln mit ord()
Wie kann man überprüfen, ob ein eingelesenes Zeichen eine Ziffer zwischen 1 und 9 ist? Verwenden Sie dazu die ord(Zeichen)-Funktion, welche den Ordinalwert des Zeichens zurückgibt. Eine entsprechende Bedingung wäre also
IF (ord(Zeichen) >= ord('1')) AND (ord(Zeichen) <= ord('9') THEN
Typenumwandlung
Denken Sie daran, dass Sie Char-Zeichen mit StrToInt in Integer-Ziffern umwandeln können. Andere evt. benötigte Umwandlungsfunktionen sind IntToStr, FloatToStr. Beispiel:
Ziffer := StrToInt(Zeichen);
Programmieren Sie eine FOR-Schleife, in welcher Sie alle Elemente der beiden Arrays mit 0 (Null)
initialisieren
Schreiben Sie in einer zweiten FOR-Schleife den Inhalt der beiden Arrays (Im Moment o) in die Felder der TStringGrid-Tabelle aus. In derselben Schleife können Sie gleich die Überschriften ausgeben lassen
Fügen Sie Ihren selber entwickelten Benford-Algorithmus zwischen diese beiden FOR-Schleifen ein
Resultate in Datei ausschreiben
Sie haben nun die Resultate programmintern in Arrays gespeichert. Diese Information geht aber
verloren, sobald Sie das Programm beenden. Wir wollen diese Information jedoch permanent speichern um sie danach ins Excel importieren zu können.
Dateien verarbeiten 29
Erstellen Sie eine Prozedur für den „Datei ausschreiben“-Knopf, welcher die absoluten und prozentualen Zahlen in eine Datei mit der Endung .csv ausgibt (Siehe Bild unten).
Damit Excel die Elemente richtig anzeigt, muss zwischen jeden Wert ein ; eingefügt werden.
So sollte das .csv-Datei - in einem Editor geöffnet – aussehen:
Und die entsprechende Datei im Excel:
Dateien verarbeiten 30
Phase 5 – Verifikation
Ausführen und Testen des Programms
Sie haben nun ein Programm von A bis Z selber entwickelt und programmiert. Als letzter Schritt
wollen wir nun überprüfen, ob ihre Software auch korrekt funktioniert. Lesen Sie dazu die TestDateien aus dem E.Tutorial ein und lassen Sie die Benford-Verteilung berechnen und ausgeben.
Vergessen Sie nicht am Schluss des E.Tutorials das Feedback-Formular auszufüllen. Besten Dank.
Dateien verarbeiten 31
C. Selbständige Arbeit
Sie haben in diesem Modul bereits genug selbständige Arbeit verrichtet, weshalb wir hier auf eine
zusätzliche Aufgabe verzichten.
D. Abgabebedingungen
Ein lauffähiges Programm für die Berechnung der Benford-Verteilung.
Drucken Sie das kommentierte Programm für die Abgabe aus. Sie sollten die Begriffe und Sprachelemente mit einfachen Worten erklären können.
Sie sollten auch die von Ihnen entworfenen Flussdiagramme und den Pseudocode vorzeigen und
erläutern können.
Sie haben das Phasen-Modell, welches beim Software-Design häufig zur Anwendung kommt, verstanden und können die Ziele der einzelnen Phasen in eigenen Worten zusammenfassen.
6. Neue Sprachelemente
Variablen sind kursiv geschrieben
Sprachelement
Bedeutung
Beispiel
Textfile
Datentyp zur Verwaltung von Textdateien. Wird zur Deklaration von
Filevariablen verwendet
Var
AssignFile
Bringt eine Filevariable in Verbindung zum Namen, inkl. Verzeichnispfad, einer Datei
AssignFile(Dokument,
'C:\Meintext.txt');
Rewrite
Bereitet ein File vor, Daten in eine
Datei zu transferieren. Der Inhalt
bestehender Dateien wird überschrieben
Rewrite(Dokument);
Append
Bereitet ein Textfile vor, Daten ans
Ende einer Textdatei anzufügen
Rewrite(Dokument);
Reset
Öffnet ein File fürs Lesen
Reset(Dokument);
CloseFile
Schliesst ein File und stellt sicher,
dass alle geschriebenen Daten in die
Datei transferiert werden
CloseFile(Dokument);
EOF
Prüft, ob bereits die letzte Filekomponente verarbeitet wurde
EOF(Dokument);
EOLN
Prüft, ob die nächste Filekomponente ein Zeilenende-Zeichen ist
EOLN(Dokument);
Dokument: Textfile;
Herunterladen