Datei helper.dart: Unterschied zwischen den Versionen

Aus Info-Theke
Zur Navigation springen Zur Suche springen
 
(4 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
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.
Zeile 103: Zeile 106:
* <code>rc += cc;</code> Jetzt wird das Zeichen selbst angehängt.
* <code>rc += cc;</code> Jetzt wird das Zeichen selbst angehängt.


= shellPatternToRegExp() =
= Die Funktion shellPatternToRegExp() =
Diese Funktion wandelt ein Suchmuster, wie sie in Kommandozeile üblich ist, in den String
Diese Funktion wandelt ein Suchmuster, wie sie in Kommandozeile üblich ist, in den String
eines analogen regulären Ausdrucks um: <code>*.txt</code> wird zu <code>.*\.txt</code>.
eines analogen regulären Ausdrucks um: <code>*.txt</code> wird zu <code>.*\.txt</code>.
Zeile 169: Zeile 172:
* Der Algorithmus ist aus der Standardroutine von Python abgeschaut, eine reine Fleißaufgabe, nichts Aufregendes oder Neues.
* Der Algorithmus ist aus der Standardroutine von Python abgeschaut, eine reine Fleißaufgabe, nichts Aufregendes oder Neues.


= testIntArguments() =
= Die Funktion testIntArguments() =
Die Funktion prüft, ob für eine Liste von Programmoptionen jeweils eine Ganzzahl eingegeben wurde.
Die Funktion prüft, ob für eine Liste von Programmoptionen jeweils eine Ganzzahl eingegeben wurde.
<pre>/// Tests whether the [options] are integers. If not the callback usage is called.
<pre>/// Tests whether the [options] are integers. If not the callback usage is called.
Zeile 194: Zeile 197:
* Ist der Optionswert keine Zahl, dann wird die Callbackfunktion mit der Fehlermeldung als Parameter aufgerufen und der Ergebniswert auf <code>false</code> gesetzt.
* Ist der Optionswert keine Zahl, dann wird die Callbackfunktion mit der Fehlermeldung als Parameter aufgerufen und der Ergebniswert auf <code>false</code> gesetzt.


= testRegExpArguments() =
= Die Funktion testRegExpArguments() =
Analogon zur Funktion <code>testIntArguments()</code>, nur dass hier geprüft wird, ob ein syntaktisch korrekter regulärer Ausdruck vorliegt.
Analogon zur Funktion <code>testIntArguments()</code>, nur dass hier geprüft wird, ob ein syntaktisch korrekter regulärer Ausdruck vorliegt.


Zeile 217: Zeile 220:
</pre>
</pre>


= writeString() =
= Die Funktion writeString() =
Diese Funktion schreibt eine String oder eine Stringliste in eine Datei.
Diese Funktion schreibt eine String oder eine Stringliste in eine Datei.
Es wird geprüft, ob das Verzeichnis, in der die Datei steht, existiert.
Es wird geprüft, ob das Verzeichnis, in der die Datei steht, existiert.

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.