Datei helper.dart - flutter-tutorial-de/dart-programming GitHub Wiki
Die Datei helper.dart
enthält Funktionen, die wiederverwendbar sind.
import 'dart:io'; import 'package:args/args.dart'; import 'package:path/path.dart';
- Es werden also ein "internes" Paket
dart:io
und zwei externe Pakete eingebunden: args und path
Test, ob eine Datei eine Binärdatei ist.
Algorithmus: Untersuche die ersten 4 kByte der Datei, ob dort mindestens ein Nullbyte vorkommt oder die Anzahl der Kontrollzeichen (ASCII-Code < Leerzeichen und keine Tabulator oder Zeilenendenzeichen) über 25% ist.
/// Tests whether a file named [filename] is a binary file. /// Returns true if ASCII control characters lower than ' ' are greater than 25% bool isBinary(String filename) { final file = File(filename); var rc = false; if (file.existsSync()) { final handle = file.openSync(); final buffer = handle.readSync(4096); handle.close(); var countControl = 0; for (var ix = 0; ix < buffer.lengthInBytes; ix++) { final byte = buffer[ix]; if (byte == 0) { rc = true; break; } if (byte < 8 /* TAB */ || byte > 13 /* LF */ && byte < 32 /* ' '*/) { countControl++; } } rc = rc || countControl * 4 > buffer.lengthInBytes; } return rc; }
-
/// Tests ...
Eine kurze Beschreibung der Funktion. Hinweis: Kommentare mit drei Schrägstrichen///
werden automatisch in die Dokumentation übernommen. Verweise in den Code werden mit eckigen Klammern angezeigt, z. B. [filename] besagt, dassfilename
ein Parameter ist. -
bool isBinary(String filename) {
Die Funktion hat als Ergebnis einen Wahrheitswert (bool) und als Parameter einen String, der den vollen Dateinamen angibt. -
file = File(filename);
- Im Paket "dart:io" gibt es die Klasse
File
, die im Konstruktor den Dateinamen bekommt. - Mit einem Objekt dieser Klasse können viele Dateioperationen erledigt werden.
- Im Paket "dart:io" gibt es die Klasse
-
if (file.existsSync()) {
Es wird geprüft, ob die Datei existiert. Hinweis: dasSync
im Namen bedeutet, dass die Methode synchron ausgeführt wir, also sofort. Im nächsten Kapitel werden im Gegensatz dazu asynchrone Funktionen erklärt. - Wenn nein, passiert nichts, die Variable
rc
hat dann den Wertfalse
:- Wenn die Datei existiert:
-
handle = file.openSync();
Die Datei wird geöffnet, kein Parameter bedeutet, sie wird zum Lesen geöffnet. -
buffer = handle.readSync(4096);
Es werden bis zu 4096 Bytes gelesen, dieser Inhalt landet in der Variablebuffer
. -
handle.close();
Wichtig: Wenn eine Datei geöffnet wird, muss sie auch wieder geschlossen werden, sonst werden Resourcen wie Arbeitsspeicher nicht wieder freigegeben. -
for (var ix = 0; ix < buffer.lengthInBytes; ix++) {
Wir gehen in einer Schleife alle Bytes des bis zu 4-kByte Blockes durch: -
byte = buffer[ix]
Wir merken uns das aktuelle Byte. -
if (byte == 0)
Hat das aktuelle Byte den Wert 0 (dieser Wert kommt in Textdateien nie vor). -
rc = true;
Der Rückgabewert isttrue
, die Datei ist binär. -
break
Wir brechen die Schleife ab. -
if (byte < 8 /* TAB */ || byte > 13 /* LF */ && byte < 32 /* ' '*/) {
- Es wird geprüft, ob ein in Texten selten vorkommendes Zeichen vorliegt
-
countControl++;
Wenn ja, zählen wir dieses Controlzeichen
-
rc = rc || countControl * 4 > buffer.lengthInBytes;
- Wir befinden uns hinter der for-Schleife.
- Es findet eine boolsche Operation mit dem Operator
||
statt: - Wenn
rc
den Werttrue
hat, dann wurde ein Nullbyte gefunden, das Ergebnis isttrue
, denn: (true || irgendwas) ist true - Wenn
rc
false ist (also kein Nullbyte gefunden wurde), dann wird geprüft, ob die Anzahl der Kontrollzeichen mehr als ein Viertel (also 25%) der Gesamtzahl ist. Wenn ja, ist das Ergebnistrue
, die Datei ist eine Binärdatei.
Wandelt einen String in eine Ganzzahl unter Berücksichtigung von null
.
- Wenn der Parameter
value
null
ist, ist das Ergebnis null. - Wenn nicht, wird aus dem String eine Zahl, wobei
int.tryParse()
auch null liefert, wenn der String keine Zahl ist, ansonsten die entsprechende Ganzzahl.
/// Escapes all meta characters in [string] for a regular expression string. /// Returns [string] with all meta characters escaped by a preceding backslash. /// Note: the algorithm is taken from the Python standard library. String regExprEscape(String string) { String rc; if (string != null) { rc = ''; for (var ii = 0; ii < string.length; ii++) { final cc = string[ii]; if ('()[]{}?*+-|^\$\\.&~# \t\n'.contains(cc)) { rc += r'\'; } rc += cc; } } return rc; }
- Die Funktion wandelt alle Zeichen, die in einem regulären Ausdruck eine Sonderbedeutung haben (Metazeichen) in das gleiche Zeichen mit vorausgehendem Gegenstrich
\
um, also aus*+
wird\*\+
. Alle anderen Zeichen bleiben gleich. -
for (var ii = 0; ii < string.length; ii++) {
In einer Schleife werden alle Zeichen des Parametersstring
bearbeitet. -
cc = string[ii]
Das aktuelle Zeichen wird in der Variablencc
bereitgestellt -
if ('()[]{}?*+-|^\$\\.&~# \t\n'.contains(cc))
Der String'()[]{}?*+-|^\$\\.&~# \t\n'
enthält alle Metazeichen, mittels der Methodecontains()
wird festgestellt, ob das Zeichencc
sich darin befindet. -
rc += r'\';
Wenn ja, wird ein Gegenstrich ans Ergebnis angeheftet. -
rc += cc;
Jetzt wird das Zeichen selbst angehängt.
Diese Funktion wandelt ein Suchmuster, wie sie in Kommandozeile üblich ist, in den String
eines analogen regulären Ausdrucks um: *.txt
wird zu .*\.txt
.
- Das Jokerzeichen
*
(beliebiger String) wird zu.*
. - Das Jokerzeichen
?
(genau ein beliebiges Zeichen) wird zu.
. - Eine negierte Zeichenklasse
[!X]
wird zu[^X]
. - In Zeichenklassen werden bestimmte Zeichen mit vorausgehenem Gegenstrich
\
maskiert, damit sie nicht als Metazeichen interpretiert werden.
/// Translates a unix shell pattern into a regular expression pattern. /// Example: '*.txt' is translated into r'.*\.txt' /// Note: the algorithm is a simplified version of the algorithm in the Python standard library. /// [addBeginOfString]: true: the result starts with '^'. /// [addEndOfString]: true: the result ends with r'$'. String shellPatternToRegExp(String pattern, {bool addBeginOfString = true, bool addEndOfString = true}) { var rc; if (pattern == null) { rc = null; } else { var i = 0; var length = pattern.length; rc = ''; while (i < length) { final c = pattern[i++]; if (c == '*') { rc += '.*'; } else if (c == '?') { rc += '.'; } else if (c == '[') { var j = i; if (j < length && pattern[j] == '!') { j++; } if (j < length && pattern[j] == ']') { j++; } while (j < length && pattern[j] != ']') { j++; } if (j >= length) { rc += r'\[' + pattern.substring(i); break; } var stuff = pattern.substring(i, j); if (stuff[0] == '!') { stuff = '^' + stuff.substring(1); } stuff = stuff.replaceAll(r'\', r'\\').replaceAll(']', r'\]'); rc += '[$stuff]'; i = j + 1; } else { rc += regExprEscape(c); } } if (addBeginOfString) { rc = '^' + rc; } if (addEndOfString) { rc += r'$'; } } return rc; }
- Der Algorithmus ist aus der Standardroutine von Python abgeschaut, eine reine Fleißaufgabe, nichts Aufregendes oder Neues.
Die Funktion prüft, ob für eine Liste von Programmoptionen jeweils eine Ganzzahl eingegeben wurde.
/// Tests whether the [options] are integers. If not the callback usage is called. /// Returns true if all arguments are integers. bool testIntArguments( ArgResults argResults, List<String> options, Function usage) { var rc = true; for (var opt in options) { if (argResults[opt] != null && int.tryParse(argResults[opt]) == null) { usage('$opt is not an integer: ${argResults[opt]}'); rc = false; break; } } return rc; }
-
bool testIntArguments(ArgResults argResults, List<String> options, Function usage)
- Das Funktionsergebnis ist bool, als Argument wird übergeben:
- Ein Objekt der Klasse
ArgResults
aus dem Paketargs
(siehe Inport). - Die Liste der Optionsnamen, die überprüft werden soll:
options
- Eine Callbackfunktion, die im Fehlerfall aufgerufen wird.
- Ein Objekt der Klasse
- Das Funktionsergebnis ist bool, als Argument wird übergeben:
- In einer Schleife werden alle Elemente der Optionsnamensliste untersucht:
- Ist der Optionswert keine Zahl, dann wird die Callbackfunktion mit der Fehlermeldung als Parameter aufgerufen und der Ergebniswert auf
false
gesetzt.
Analogon zur Funktion testIntArguments()
, nur dass hier geprüft wird, ob ein syntaktisch korrekter regulärer Ausdruck vorliegt.
/// Tests whether the [options] are integers. If not the callback usage is called. /// Returns true if all arguments are integers. bool testRegExpArguments( ArgResults argResults, List<String> options, Function usage) { var rc = true; for (var opt in options) { try { if (argResults[opt] != null) { RegExp(argResults[opt]); } } on FormatException catch (exc) { usage('$opt: error in regular expression "${argResults[opt]}": $exc'); rc = false; break; } } return rc; }
Diese Funktion schreibt eine String oder eine Stringliste in eine Datei. Es wird geprüft, ob das Verzeichnis, in der die Datei steht, existiert. Wenn nicht, wird dieses Verzeichnis angelegt.
/// Writes a [string] or a [list] into a [file]. /// The path is void writeString(String filename, {String string, List<String> list}) { try { final base = dirname(filename); if (base.isNotEmpty) { Directory(base).createSync(recursive: true); } if (list != null) { string = list.join('\n'); } File(filename).writeAsStringSync(string); } on FileSystemException catch (exc) { print('+++ $exc'); } }
-
void writeString(String filename, {String string, List<String> list}) {
- Es werden benannte Parameter benutzt:
string
undlist
. -
try { ... } on FileSystemException catch (exc) {
Passiert bei einer Dateioperation ein Fehler (z. B. keine Berechtigung beim Erzeugen der Datei), wird dieser Fehler gemeldet:print('+++ $exc');
- Es werden benannte Parameter benutzt:
-
final base = dirname(filename);
Wir verwenden das Paketpath
. Dort ist die Funktiondirname()
definiert, die den Namen des Verzeichnisses einer Datei ermittelt, für alle Betriebssysteme. -
if (base.isNotEmpty)
Wenn ein Verzeichnis Teil des Dateinamens ist... -
Directory(base).createSync(recursive: true);
- ... wird ein Verzeichnis angelegt, wenn es noch nicht existiert:
-
Directory
ist eine Klasse aus "dart:io", die als Konstruktor den Namen des Verzeichnisses bekommt -
createSync()
legt das Verzeichis mit allen "Vaterverzeichnissen" an. Wenn es schon existiert, passiert nichts.
- Die benannten Parameter
string
undlist
sind alternativ. - Im Fall von
list
wird diese in einen String umgewandelt, mit der Methodejoin()
, die alle Elemente zusammenfügt, mit dem als Parameter übergebenen Trenner, in unserem Fall ein Zeilenwechsel\n
-
File(filename).writeAsStringSync(string);
- Die Klasse
File
aus "dart:io" wird mit dem Dateinamen im Konstruktor instantiiert (als Objekt erstellt) - und der String wird in diese Datei geschrieben und zwar mit dem Zeichensatz UTF-8 (einen anderen Zeichensatz kann man per Parameter wählen).
- Die Klasse
- Geht beim Dateischreiben was schief, beispielsweise ein Rechteproblem, wird eine Ausnahme vom Typ
FileSystemException
geworfen. Im diesem Fall wird eine Fehlermeldung ausgegeben.-
on FileSystemException catch (exc)
das Objekt der Ausnahme steht in der Variablenexc
zur Verfügung - Wie jede Klasse hat auch
FileSystemException
eine MethodetoString()
, die bei der Interpretation von+++ $exc
die Fehlermeldung einbaut.
-