Buch-Add-Ons Jürgen Bayer Inhaltsverzeichnis Object Pascal-Tipps und Tricks 1 Konvertieren und auf numerische Werte überprüfen 2 1.1 Strings in Integerwerte konvertieren 2 1.2 Strings in Extended-Werte konvertieren 3 1.3 Überprüfen auf numerische Werte in Strings 4 2 Ein- und Ausgabe 5 2.1 Eingabe an der Konsole 5 2.2 Umlaute in Windows-Programmen an der Konsole 5 3 Formatieren 6 3.1 Zahlen formatieren 6 3.2 Datumswerte formatieren 7 4 Strings 9 4.1 Ersetzen 9 5 Textdateien lesen und schreiben 10 5.1 Textdateien lesen 10 5.2 Textdateien schreiben 11 6 Index 12
Die Konvertierung eines Strings in einen Integer-Wert ist in Object Pascal über die Funktion StrToInt sehr einfach. Diese Funktion erzeugt eine Ausnahme vom Typ EConvertError wenn die Zahl nicht konvertiert werden kann. Diese Ausnahme fangen Sie einfach ab: program Konvertierungen; var source: String; intresult: integer; doubleresult: double; { Konvertierung eines String in einen Integer-Wert } source := '1234'; try intresult := StrToInt(source); writeln(intresult); except on ex: Exception do writeln('die Konvertierung ist fehlgeschlagen'); end source := '1234,5678'; try intresult := StrToInt(source); writeln(intresult); except on ex: Exception do writeln('die Konvertierung ist fehlgeschlagen'); end Die Konvertierung ist nur dann erfolgreich, wenn der String lediglich aus Ziffern besteht. Die zweite Konvertierung im Beispiel schlägt fehl, weil ein Komma im String vorkommt. Beachten Sie, dass Sie die Option STOP ON DELPHI EXCEPTIONS bzw. BEI DELPHI- EXCEPTIONS STOPPEN in den Debugger-Optionen abschalten sollten, damit Delphi bzw. Kylix nicht bei jeder Ausnahme in den Debugger springen. Konvertieren und auf numerische Werte überprüfen 2
Alternativ können Sie die Funktion StrToIntDef verwenden, die keine Ausnahme erzeugt, sondern beim Fehlschlagen der Konvertierung den im zweiten Argument übergebenen Defaultwert zurückgibt. Damit können Sie zwar nur dann auf Gültigkeit überprüfen, wenn der Defaultwert ein als ungültig betrachteter Wert ist. Das Handling dieser Funktion ist aber einfacher, weil Sie keine Ausnahme abfangen müssen: { Konvertierung eines String über StrToIntDef in einen Integer-Wert } source := '1234'; intresult := StrToIntDef(source, -1); if intresult > -1 then writeln(intresult) else writeln('die Konvertierung ist fehlgeschlagen'); source := '1234,5678'; intresult := StrToIntDef(source, -1); if intresult > -1 then writeln(intresult) else writeln('die Konvertierung ist fehlgeschlagen'); Die Konvertierung eines String in einen Extended-Wert funktioniert auf ähnliche Weise wie bei der Konvertierung in einen Integer-Wert. Sie können die Funktion StrToFloat verwenden, die beim Fehlschlagen der Konvertierung eine Ausnahme erzeugt, oder die Funktion StrToFloatDef, die beim Fehlschlagen den im zweiten Argument übergebenen Defaultwert zurückgibt. Beide Funktionen berücksichtigen automatisch das aktuell im System eingestellte Zahlformat (so einfach kann das Leben sein (vgl. die Java-Variante)...): source := '1234,5678'; try extendedresult := StrToFloat(source); writeln(extendedresult); except on ex: EConvertError do writeln('die Konvertierung ist fehlgeschlagen'); end { Konvertierung eines String über StrToFloatDef in einen Extended-Wert } source := '1234,xyz'; extendedresult := StrToFloatDef(source, -1); if intresult > -1 then writeln(extendedresult) else writeln('die Konvertierung ist fehlgeschlagen'); Konvertieren und auf numerische Werte überprüfen 3
! " In Object Pascal existiert wie in Jave leider keine einfache Funktion zur Überprüfung auf numerische Werte in Strings. Sie müssen selbst programmieren. Zur besseren Wiederverwendung habe ich die entsprechenden Funktionen in einer Unit NumberUtils implementiert: unit NumberUtils; interface function IsInteger(value: string): boolean; function IsExtended(value: string): boolean; implementation { Funktion zur Überprüfung, ob ein String in einen Integerwert konvertiert werden kann } function IsInteger(value: string): boolean; try StrToInt(value); result := true; except on EConvertError do result := false; { Funktion zur Überprüfung, ob ein String in einen Extended-Wert konvertiert werden kann } function IsExtended(value: string): boolean; try StrToFloat(value); result := true; except on EConvertError do result := false; Die Anwendung ist einfach: source := '123'; if IsInteger(source) then writeln(source, ' kann in einen Integer-Wert konvertiert werden') else writeln(source, ' kann NICHT in einen Integer-Wert konvertiert werden'); source := '1234,5678'; if IsExtended(source) then writeln(source, ' kann in einen Extended-Wert konvertiert werden') else writeln(source, ' kann NICHT in einen Extended-Wert konvertiert werden'); Konvertieren und auf numerische Werte überprüfen 4
# $ Die Eingabe an der Konsole ist in Object Pascal eigentlich keinen Tipp wert, wird aber der Vollständigkeit halber beschrieben. Sie verwenden dazu einfach die readln-methode: program Eingabe_und_Ausgabe; var input: string; { Eingabe an der Konsole } write('ihr Name: '); readln(input); writeln('hallo ', input); %$ & $ Da die Konsole in Windows in Deutschland normalerweise mit der Zeichentabelle 850 arbeitet, werden Umlaute und andere Sonderzeichen aus einem Delphi-Programm, das normalerweise die Zeichentabelle ISO-8859-1 verwendet, falsch dargestellt. Sie müssen Strings in die passende Zeichentabelle konvertieren, damit Sonderzeichen korrekt dargestellt werden. Dazu können Sie einfach die Betriebssystemfunktion CharToOEM verwenden. Diese Funktion erwartet zwei Strings im C-Format (das ist eine Folge einzelner Zeichen, die mit dem Zeichen 0 abgeschlossen ist). Im ersten Argument übergeben Sie den zu konvertierenden String, im zweiten eine Variable, in die die Funktion das Ergebnis schreibt. Das Beispiel verwendet dazu einfach die implizite Variable result. Sehr wichtig ist, dass diese Variable auf eine korrekte Länge definiert ist, was die Funktion über die SetLength-Funktion implementiert. Die in C oder C++ geschriebene Funktion CharToOEM schreibt einfach in die Ergebnisvariable, ohne dass sie die Länge des Strings überprüfen kann. Ist die Ergebnisvariable zu klein dimensioniert, überschreibt CharToOEM einen unbestimmten Speicherbereich, was dann in der Regel zu einem Programmabsturz, aber zumindest zu schwer wiegenden Fehlern führt. Ein- und Ausgabe 5
Ansonsten ist die Funktion zur Konvertierung aber sehr einfach aufgebaut. Ich habe diese Funktion in einer separaten Unit ConsoleTools implementiert: unit ConsoleTools; interface uses Windows; function ConvertToDos(s: string): string; implementation { Funktion zum Konvertieren in den DOS-ASCII-Code } function ConvertToDos(value: string): string; SetLength(result, Length(value)); CharToOEM(PChar(value), PChar(result)); Die Anwendung ist trivial (wie es ja auch bei der Verwendung von eigenen Funktionen, Prozeduren und Methoden immer sein sollte): writeln(converttodos('äöüäöüß'));! '! ($ Zahlen können Sie in Object Pascal über die Funktion FormatFloat formatieren. Im ersten Argument übergeben Sie einen String, der das Format spezifiziert, im zweiten die zu formatierende Zahl: program Formatieren; var number: double; { Zahlen formatieren } number := 1234.56789; writeln(formatfloat('0.00', number)); // 1234,57 writeln(formatfloat('0.00', number)); // 1234,57 writeln(formatfloat('00000.0000000',number)); // 01234,5678900 writeln(formatfloat('0.0000',number)); // 1234,5679 number := 0.12; writeln(formatfloat('#.000', number)); //,120 Die Formatzeichen für Zahlen werden in der Hilfe zur FormatFloat-Funktion beschrieben. Formatieren 6
Sie können die folgenden Formatierzeichen verwenden (Auszug): Formatzeichen Bedeutung 0 Ziffer. Fehlende Ziffern werden als 0 angezeigt. Das Format "000.0000" ergibt z. B. bei der Zahl 1,23 den String "001,2300". # Ziffer. Fehlende Ziffern werden nicht angezeigt. Das Format "#.00" ergibt z. B. bei der Zahl 1,23 den String ",23".. steht für das Dezimaltrennzeichen ' steht für das Tausender-Trennzeichen Tabelle 3.1: Auszug aus den Formatierzeichen für Zahlen! ) Datumswerte können Sie über die Funktion FormatDateTime formatieren: program Formatieren; var date: TDateTime; { Datum formatieren } date := now; writeln(formatdatetime('dd.mm.yyyy', date)); // 02.09.2002 writeln(formatdatetime('dddd, dd.mm.yyyy', date)); // Montag, 02.09.2002 writeln('monat: ', FormatDateTime('mmmm', date)); // September writeln('zeit: ', FormatDateTime('hh:mm:ss', date)); // 23:59:59 Die Formatzeichen für Datumswerte werden in der Hilfe zur FormatDateTime- Funktion beschrieben. Formatieren 7
Sie können die folgenden Formatierzeichen verwenden (Auszug): Formatzeichen Bedeutung d Tag als ein- bis zweistellige Zahl ohne führende Null dd Tag als zweistellige Zahl mit führender Null dddd Tag als voll ausgeschriebener Name (Montag m Monat als ein- oder zweistellige Zahl ohne führende Null mm Monat als zweistellige Zahl mit führender Null mmmm Monat mit voll ausgeschriebenem Namen yy Jahr als zweistellige Zahl yyyy Jahr als vierstellige Zahl h Stunde als einstellige Zahl ohne führende Null hh Stunde als ein- oder zweistellige Zahl mit führender Null n Minute als einstellige Zahl ohne führende Null nn Minute als ein- oder zweistellige Zahl mit führender Null s Sekunde als einstellige Zahl ohne führende Null ss Sekunde als ein- oder zweistellige Zahl mit führender Null / stellt das aktuelle Datums-Trennzeichen dar : stellt das aktuelle Zeit-Trennzeichen dar Tabelle 3.2: Auszug aus den Formatierzeichen für Datumswerte Formatieren 8
* * + Das Ersetzen von Strings ist in Object Pascal sehr einfach. Dazu nutzen Sie die Funktion StringReplace. Im ersten Argument übergeben Sie den Quellstring, im zweiten das Suchmuster und im dritten den Ersatzstring. Im vierten Argument müssen Sie spezifizieren, wie das Ersetzen ausgeführt werden soll. Dieses Argument ist eine Aufzählung (Enumeration) von einzelnen Werten, die den Typ TReplaceFlags besitzt. In diesem Argument können Sie z. B. mit dem Wert rfreplaceall festlegen, dass alle gefundenen Suchmuster ersetzt werden sollen. Da es sich um eine Auszählung handelt, müssen Sie diesen Wert in eckigen Klammern angeben. Mehrere Werte trennen Sie durch Kommata. So können Sie z. B. alle Fundstellen ersetzen und /über rfignorecase) bei der Suche Groß- und Kleinschreibung vernachlässigen: program Strings_ersetzen; var source: string; { Strings ersetzen } source := '*Xyz*XYZ*xyz*'; writeln(stringreplace(source, 'xyz', 'abc', [rfreplaceall, rfignorecase])); // '*abc*abc*abc*' Wenn Sie rfreplaceall nicht angeben, ersetzt StringReplace nur die erste Fundstelle. Strings 9
, - $, - $ Zum Lesen einer Textdatei verwenden Sie eine Variable vom Typ TextFile, die allerdings (leider) kein Objekt ist. Sie müssen diese Variable zunächst über AssignFile mit einer Datei»verbinden«und können die Datei dann über die Reset-Prozedur öffnen. Danach können Sie über readln einzelne Zeilen einlesen. Sie müssen dabei lediglich am ersten Argument die TextFile-Variable angeben 1. Um zu überprüfen, ob das Programm am Ende der Datei angelangt ist, fragen Sie dieses über die Eof-Funktion 2 ab. Schließlich schließen Sie die Datei über die CloseFile-Prozedur. Beachten Sie den kleinen Trick im Beispiel, das der Pfad ausgelesen wird, aus dem die Anwendung heraus gestartet wurde: program Textdateien_lesen; { Variable für die Datei } var f: TextFile; { Variablen für den Darteinamen und zum Einlesen einzelner Zeilen } var filename, row: string; { Dateiname zusammensetzen. Die Datei wird im Ordner der Anwendung erwartet. Der Pfad wird über ParamStr ausgelesen. Diese Funktion wird verwendet, um Befehlszeilenargumente auszulesen, liefert mit dem Index 0 aber auch den Pfad, aus dem heraus das Programm aufgerufen wurde } filename := ExtractFilePath(ParamStr(0)) + 'demo.txt'; { Datei zum Lesen öffnen } AssignFile(f, filename); Reset(f); { Zeilenweise bis zum Ende lesen } while eof(f) = false do { Eine Zeile lesen } readln(f, row); { Zeile ausgeben (oder anders verarbeiten) } writeln(row); { Datei schließen } CloseFile(f); 1 Wenn Sie readln ohne Dateivariable am ersten Argument aufrufen, liest diese Prozedur die»standardeingabe«ein, d. h. die Konsole. Mit der Angabe einer Dateivariable wird stattdessen die Datei ausgelesen. 2 End of file Textdateien lesen und schreiben 10
, - Das Schreiben von Textdateien ist dem Lesen sehr ähnlich. Der Unterschied ist, dass Sie die Datei nicht über Reset zum Lesen, sondern über Rewrite zum Schreiben öffnen, dass Sie statt readln nun write oder writeln verwenden und dass Sie natürlich nicht sequenziell durch die einzelnen Zeilen gehen: program Textdateien_schreiben; { Variable für die Datei } var f: TextFile; { Variable für den Darteinamen } var filename: string; { Dateiname zusammensetzen. Die Datei wird im Ordner der Anwendung erwartet. Der Pfad wird über ParamStr ausgelesen. Diese Funktion wird verwendet, um Befehlszeilenargumente auszulesen, liefert mit dem Index 0 aber auch den Pfad, aus dem heraus das Programm aufgerufen wurde } filename := ExtractFilePath(ParamStr(0)) + 'demo.txt'; { Datei zum Schreiben öffnen } AssignFile(f, filename); Rewrite(f); { Zwei Zeilen schreiben } writeln(f, 'Die Antwort auf die Frage aller Fragen ist 42.'); writeln(f, 'Nur kennt keiner mehr die Frage.'); writeln('fertig'); { Datei schließen } CloseFile(f); Textdateien lesen und schreiben 11
. AssignFile-Funktion 10 CharToOEM-Funktion 5 CloseFile-Prozedur 10 Datumswerte formatieren 7 Eingaben an der Konsole 5 überprüfen 4 Eof-Funktion 10 FormatDateTime-Funktion 7 FormatFloat-Funktion 6 Formatieren Datumswerte 7 Zahlen 6 Konsole Eingaben 5 Umlaute und andere Sonderzeichen 5 Konvertieren String in Extended 3 String in int 2 Reset-Prozedur 10 Rewrite-Prozedur 11 SetLength-Funktion 5 StringReplace-Funktion 9 Strings auf numerische Daten überprüfen 4 ersetzen 9 in Extended-Werte konvertieren 3 in int-werte konvertieren 2 StrToFloatDef-Funktion 3 StrToFloat-Funktion 3 StrToIntDef-Funktion 3 StrToInt-Funktion 2 Textdateien lesen 10 schreiben 11 TextFile-Typ 10 Umlaute in der Konsole 5 Zahlen formatiert ausgeben 6 Index 12