Datei helper.dart: Unterschied zwischen den Versionen

Aus Info-Theke
Zur Navigation springen Zur Suche springen
 
Zeile 70: Zeile 70:
= Die Funktion intValue() =
= Die Funktion intValue() =
Wandelt einen String in eine Ganzzahl unter Berücksichtigung von <code>null</code>.
Wandelt einen String in eine Ganzzahl unter Berücksichtigung von <code>null</code>.
<pre>int intValue(String value){
<pre>/// Converts a string [value] into an int.
/// Returns null on null or invalid numbers.
int intValue(String value){
   return value == null ? null : int.tryParse(value);
   return value == null ? null : int.tryParse(value);
}
}
</pre>
</pre>
* Wenn der Parameter <code>value</code> <code>null</code> ist, ist das Ergebnis null.
* Wenn der Parameter <code>value</code> <code>null</code> ist, ist das Ergebnis null.

Aktuelle Version vom 4. Januar 2021, 21:44 Uhr

Links[Bearbeiten]

Zielsetzung[Bearbeiten]

Die Datei helper.dart enthält Funktionen, die wiederverwendbar sind.

Import[Bearbeiten]

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

Die Funktion isBinary()[Bearbeiten]

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 3 Schrägstrichen /// werden automatisch in die Dokumentation übernommen. Verweise in den Code werden mit eckigen Klammern angezeigt, z.B. [filename] besagt, dass filename 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.
  • if (file.existsSync()) { Es wird geprüft, ob die Datei existiert. Hinweis: das Sync 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 Wert false:
    • 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 Variable buffer.
    • 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 ist true, 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 Wert true hat, dann wurde ein Nullbyte gefunden, das Ergebnis ist true, 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 Ergebnis true, die Datei ist eine Binärdatei.

Die Funktion intValue()[Bearbeiten]

Wandelt einen String in eine Ganzzahl unter Berücksichtigung von null.

/// Converts a string [value] into an int.
/// Returns null on null or invalid numbers.
int intValue(String value){
  return value == null ? null : int.tryParse(value);
}

  • 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.

Die Funktion regExprEscape()[Bearbeiten]

/// 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 Parameters string bearbeitet.
  • cc = string[ii] Das aktuelle Zeichen wird in der Variablen cc bereitgestellt
  • if ('()[]{}?*+-|^\$\\.&~# \t\n'.contains(cc)) Der String '()[]{}?*+-|^\$\\.&~# \t\n' enthält alle Metazeichen, mittels der Methode contains() wird festgestellt, ob das Zeichen cc sich darin befindet.
  • rc += r'\'; Wenn ja, wird ein Gegenstrich ans Ergebnis angeheftet.
  • rc += cc; Jetzt wird das Zeichen selbst angehängt.

Die Funktion shellPatternToRegExp()[Bearbeiten]

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 testIntArguments()[Bearbeiten]

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 Paket args (siehe Inport).
      • Die Liste der Optionsnamen, die überprüft werden soll: options
      • Eine Callbackfunktion, die im Fehlerfall aufgerufen wird.
  • 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.

Die Funktion testRegExpArguments()[Bearbeiten]

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;
}

Die Funktion writeString()[Bearbeiten]

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 und list.
    • 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');
  • final base = dirname(filename); Wir verwenden das Paket path. Dort ist die Funktion dirname() 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 und list sind alternativ.
  • Im Fall von list wird diese in einen String umgewandelt, mit der Methode join(), 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).
  • 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 Variablen exc zur Verfügung
    • Wie jede Klasse hat auch FileSystemException eine Methode toString(), die bei der Interpretation von +++ $exc die Fehlermeldung einbaut.