AutoHotkey, Teil 5 CSV-Daten als Array nutzen – am Beispiel AutoHotkey

Autor / Redakteur: Mirco Lang / Stephan Augsten

Daten liegen häufig in Form von CSV-Dateien vor. Da stellt sich die Frage, wie man diese am einfachsten in Skripten nutzen kann. AutoHotkey zeigt, wie das per Array funktionieren kann.

Firmen zum Thema

Will man CSV-Daten in Skripten verwenden und einzelne Zeilen gezielt ansprechen, ist ein Array die nächstliegende Wahl.
Will man CSV-Daten in Skripten verwenden und einzelne Zeilen gezielt ansprechen, ist ein Array die nächstliegende Wahl.
(© Andrea Danti - stock.adobe.com)

Ein wenig mag es nach Mottenkiste klingen, aber das Grundprinzip der Datenverarbeitung vereinfacht Software auf wunderbare Weise: Eingabe, Verarbeitung, Ausgabe (EVA). Der V-Part ist häufig sicherlich der komplexeste, aber auch der einfachste, da alles intern passiert. Ein- und Ausgabe machen aus der spannendsten Programmlogik erst etwas, mit dem man interagieren kann.

In Lernprogrammen werden gerne mal am Anfang in einem, sagen wir Python-Terminal, Variablen angelegt und dann ausgelesen. Einfach, aber irgendwie unbefriedigend, Daten liegen eben selten hartkodiert im Programmcode. Viel häufiger findet man sie in Form von kommaseparierten Datensätzen, den omnipräsenten CSV-Dateien aus Datenbankexporten, Logs, Formulareingaben und anderem.

Will man solche Daten in Skripten verwenden und einzelne Zeilen/Datensätze gezielt ansprechen, dann ist ein Array die nächstliegende Wahl. Und um es etwas flexibler zu machen, ein assoziatives Array, damit ein beliebiger Schlüssel genutzt werden kann, etwa eine Personalkennung oder dergleichen.

AutoHotkey unter Windows ist eine extrem nützliche Skriptsprache, und sie eignet sich gut, um Dinge zu demonstrieren. Als Beispielprojekt dient hier ein imaginäres Lernprogramm für Gimp: Ein simples Tutorial in Form von Hinweisfenstern, die jeweils nach Bestätigung per Klick aufeinander folgen.

Die Hinweisfenster sollen dabei über einem geöffneten Gimp-Fenster an den passenden Koordinaten auftauchen. Die CSV-Daten liefern für jedes Fenster Koordinaten, Titel, Text und ein Bild – und eine Schlüssel-Spalte. Jetzt wird es etwas krumm, denn wir nutzen ein assoziatives Array, aber als Schlüssel-Spalte dient in diesem Fall dennoch eine simple Durchnummerierung.

Der Grund dafür ist einfach: Die Fenster kommen nacheinander, also können auch die Daten nacheinander ausgelesen werden – einfach per Vor- und Zurück-Buttons, die den Schlüssel hoch/runter zählen. Aber man könnte eben auch „foo“ und „bar“ als Schlüssel verwenden. Hier zwei Beispieldatensätze der Datei „data_gimp.ini“:

"ID","Title","Text","Xcoord","Ycoord","Image" ; Titelzeile
"1","Hallo","Willkommen in Fenster 1.","320","300","wilkommen.jpg"
"2","Open","Los geht’s: Wähle hier das Pinsel-Werkzeug.","50","60","schritt1.jpg"

Ein grafisches Frontend für die Eingabe solcher Datensätze ließe sich mit AutoHotkey mit ein zwei Dutzend sich wiederholenden Zeilen Code realisieren – und schon hätte man ein „Autoren-Backend“.

Ein funktionierendes Beispielskript finden Sie unten, inklusive einiger Zusätze, etwa zum Prüfen, ob ein Gimp-Fenster existiert und dieses zu skalieren, damit die Koordinaten passen. Hier geht es jetzt nur um die drei wichtigen Teile: Aus den Daten ein Array erstellen, die Array-Daten in Fenstern ausgeben und mit Vor- und Zurück-Buttons aufrufen.

Das Array

Die kompletten CSV-Zeilen liegen in der „data_gimp.ini“-Datei, der Einfachheit halber in die Variable „meincsv“ eingelesen. Man könnte auch eine CSV-Datei direkt einlesen oder diese einfach um „meincsv ( CSV-Daten )“ erweitern, was einfacher erscheint.

Hier zunächst der Code:

;;;;;;;;;;; Daten ins Array
UserArray := [], FieldNames := []
Loop, Parse, meincsv, `n, `r
{
   row := A_Index
   Loop, Parse, A_LoopField, CSV
   {
      If (row = 1)
         FieldNames.Push(A_LoopField)
      else {
         if ( A_Index = 1 )
            key := A_LoopField
         UserArray[key,FieldNames[A_Index]] := A_LoopField
      }
   }
}

Angelegt werden hier das assoziative Array „UserArray“ für die eigentlichen Daten und das normale Array „FieldNames“ für die Schlüssel. Die AutoHotkey-internen Variablen „A_Index“ und „A_LoopField“ stehen für Durchlauf einer Loop und dem zugehörigen Datensatz.

Die erste Parse-Loop liest die Daten Zeile für Zeile ein. Die untergeordnete Loop liest die Zeilen als CSV-Daten Feld für Feld ein und trennt Kopfzeile und Daten: Über die Hilfsvariable „row“ (gleich A_Index) wird getestet, ob es sich um die erste Zeile mit den Headern handelt. Falls ja, wird jeder Spalteninhalt/jedes Feld einzeln in das Array „FieldNames“ aufgenommen – über „FieldNames.Push(A_LoopField)“.

Ab der zweiten Zeile landen die Felder dann im assoziativen Array „UserArray“, interessant sind dabei diese Zeilen:

if ( A_Index = 1 )
   key := A_LoopField
UserArray[key,FieldNames[A_Index]] := A_LoopField

Handelt es sich um das erste Feld der Zeile wird „key“ auf den ersten Feldinhalt „A_LoopField“ gesetzt – hier also der händisch eigetragene, durchnummerierte Schlüssel in der Spalte „ID“ (1, 2, 3 etc.).

Dann wird „UserArray“ aufgebaut. Der Wert ist simpel: Hinten wird er mit „:= A_Loopfield“ wieder auf den Inhalt selbst gesetzt. Der Schlüssel ergibt sich aus „[key,FieldNames[A_Index]]“: Bei Loop-Feld Nummer 2 (Titel) ergäbe sich also „[2,Titel]“ – da ja im Array FieldNames beim Schlüssel 2 (gleich Spalte 2) der Header „Titel“ ist.

Das klingt furchbar durcheinander, aber schaut man sich den Schlüssel „2,Titel“ an, dürfte Menschen klar sein was gemeint ist: Der Inhalt der Zelle in Spalte 2 (oder Schlüssel „Titel“) in Zeile 2 (Durchlauf/A_Index gleich 2). Anders ausgedrückt: Es ist ein mehrdimensionales Array entstanden, das sich wie folgt ansprechen lässt:

UserArray[7].Titel

Und das ist im Grunde schon der eigentliche Kniff, ein mehrdimensionales assoziatives Array, das sich automatisch aus CSV-Daten bildet.

Die GUI

Nun muss eine simple GUI her, die die CSV-Daten anzeigt. Das geht bei AutoHotkey auch mit dem schönen AutoHotkey-Skript AutoGUI, aber der Code ist letztlich recht simpel, hier als Funktion „thegui“ realisiert:

;;;;;;;;;;;;;;;;;;;;;;;; The GUI
thegui(a,x,y,title,text,image)
{
Gui +AlwaysOnTop
Gui Color, 0xC0C0C0
Gui, Add, Text, x15 y10 w260 h430, Schritt %a%`n`n%text%
Gui Add, Picture, x290 y10 w100 h100, %image%
Gui, Add, Button, gweiter x15 y460 w80 h23, Weiter
Gui, Add, Button, gback x305 y460 w80 h23, Zurück
Gui, Show, X%x% Y%y% w400 h500, IGT - %title%
return
}

Über „Add“ werden Elemente hinzugefügt, „Show“ erzeugt das Fenster selbst. Fenster und Elemente (Buttons, Text, Bild) werden mit Koordinaten und Größen angegeben. Interessanter sind sind die Variablen und die „g-label“: Ein Button mit dem g-label „gweiter“ bekommt seine Funktionalität durch ein andernorts durch „weiter:“ eingeleitetes Makro – und dort werden nun auch die Variablen mit den CSV-Daten gefüllt. Neu ist die Variable „a“, die lediglich zum Durchzählen dient.

Die Buttons/Funktionen

Die einzigen Nutzeraktionen im Projekt sind Vor- und Zurück-Button. Der Vor-Button hat das g-label „gweiter“, und so folgt das entsprechende Makro, wenn der Button angeklickt wird – hier in möglichst einfacher Variante:

weiter:
a += 1
x := UserArray[(a)].Xcoord
y := UserArray[(a)].Ycoord
title := UserArray[(a)].Title
text := UserArray[(a)].Text
image := UserArray[(a)].Image
Gui, Destroy
thegui(a,x,y,title,text,image)
return

Die Zählvariable „a“ wird zunächst um 1 erhöht, beispielsweise auf 3; anschließend werden die Variablen auf die entsprechenden CSV-Daten gesetzt, beziehungsweise den Inhalt des Arrays „UserArray“ beim Schlüssel „[3,Titel]“, also dem Fenstertitel aus Zeile 3 und so weiter. Zum Schluss wird das Fenster (das aktuell natürlich noch die Daten aus Zeile 2 zeigt) zerstört und über den erneuten Aufruf der GUI-Funktion erneut erstellt, nun mit den Daten aus Zeile 3.

Was Syntax und Details angeht, ist das freilich alles nicht immer selbsterklärend, aber das Prinzip ist eigentlich recht simpel: Eine Parse-Loop liest alle Daten als Zeilen ein, eine zweite Parse-Loop liest jede der restlichen Zeilen als Felder/Spalten ein und erzeugt daraus dann aus der ersten kompletten Zeile und der ersten kompletten Spalte die beiden Schlüssel/Koordinaten für das zweidimensionale Array. Und wenn einer der beiden Schlüssel aus einer Nummerierung besteht, stehen auch die Türen für Zählvariablen und Schleifen offen.

Das Demo-Skript dient nur zur Veranschaulichung, echte Inhalte und Funktionen hat es freilich nicht – aber vielleicht hilft es bei der Einarbeitung. Und es muss ja kein Gimp-Lernprogramm sein, aber mit dem Prinzip ließe sich mit AutoHotkey etwa problemlos ein simpler Viewer für nahezu beliebige Datenbankexporte zaubern!

(ID:47496655)

Über den Autor

 Mirco Lang

Mirco Lang

Freier Journalist & BSIler