Was ist ein Array?

aktualisiert für geänderte Syntax ab AutoIt-Version 3.3.10

Am einfachsten lässt sich ein Array mit einer Tabelle vergleichen. Auch der Begriff Matrix ist in anderen Sprachen üblich.
Die einfachste Form ist eine einspaltige Liste. Nach oben sind physikalisch Grenzen gesetzt, aber selbst die sind mental kaum vorstellbar.
Ich beschränke mich hier auf Ein- und Zweidimensionale Array.

Inhalt:

Beginnen wir mit einem Ein-Dimensionalen Array

Dies entspricht einer einspaltigen Tabelle.

Bevor ich ein Array mit Daten füllen kann, muss ich es deklarieren, also einen Variablennamen vergeben und optional die Anzahl der Elemente festlegen.
Es empfiehlt sich an den Beginn des Variablennamens 'a' oder 'ar' zu setzen um immer sofort zu wissen, dass dieses ein Array ist.
Jedes Element in einem Array wird über seinen Index angesprochen. Das ist gewissermassen die Hausnummer des Elements.
Hierbei ist zu beachten, dass im Array der erste Index = 0 ist. Man spricht hier von einem Null-Basierten Index.

Deklarieren wir ein Array mit 5 Elementen:

Local $aTest[5] ; Die Anzahl der Elemente wird in eckigen Klammern hinter dem Variablennamen angegeben.

Das kleinste (sinnvoll) deklarierbare Array ist eines mit einem Element:

Local $aTest[1]

Angesprochen wird dieses Element mit:

$aTest[0] ; 0 ist der Index für das erste Element

[top] [Inhalt]

Um nun Werte zuzuweisen, wird die Arrayvariable mit dem Index des Elements angegeben und dann der Wert zugewiesen.
Dran denken - Element Nr. 1 hat den Index 0.

$aTest[0] = 'Wert 1'
$aTest[1] = 'Wert 2'
$aTest[2] = 'Wert 3'
$aTest[3] = 'Wert 4'
$aTest[4] = 'Wert 5'

Die Adressierung der Elemente innerhalb des Array über einen Index ermöglicht es uns Arrays in einer Schleife lesend/schreibend zu bearbeiten.
Hierbei lasse ich den Index in der Schleife bis zum Endwert erhöhen. Da ich nicht immer weiß, wie viele Elemente das Array enthält, verwende ich zur Bestimmung der Elementeanzahl die Funktion UBound()
. Mit dieser Funktion lassen sich die Größenverhältnisse eines Arrays ermitteln. Folgende Informationen können abgefragt werden:

$Anzahl_Dimensionen = UBound($aTest, 0)
$Anzahl_Elemente_in_Dim_1 = UBound($aTest, 1) ; Standard-Flag
$Anzahl_Elemente_in_Dim_2 = UBound($aTest, 2)
$Anzahl_Elemente_in_Dim_3 = UBound($aTest, 3)
; ...
$Anzahl_Elemente_in_Dim_n = UBound($aTest, n)

Die Beschreibung zu dieser Funktion in der Hilfe ist unvollständig und auch etwas irreführend.
Laut Hilfe liefert das Dimensions-Flag mit "0" die Anzahl der Dimensionen, mit "1" die Anzahl Zeilen und mit "2" die Anzahl der Spalten.
Da Spalten und Zeilen aber auch entgegengesetzt angeordnet werden können (z.B. sind aus Web-Seiten ausgelesene Array standardmäßig entgegengesetzt aufgebaut), ist diese Aussage nicht korrekt.
Weiterhin fehlt der Hinweis, dass die Anzahl der Elemente in jeder Dimension über den Index der Dimension als Flag abgefragt werden kann. AutoIt ermöglicht bis zu 64 Dimensionen. Mit UBound($aTest, 64) lässt sich z.B. die Anzahl der Elemente in der 64.ten Dimension abfragen.

In meiner Schleife adressiere ich den Index bis UBound($aTest) -1.
Da mein Array 5 Elemente hat, liefert mir UBound($aTest) auch den Wert 5. Ich muss also von diesem Wert "-1" rechnen um den Index des letzten Elements im Array zu erhalten.

; schreibender Zugriff (Wertzuweisung)
For $i = 0 To UBound($aTest) -1
    $aTest[$i] = "Wert_" & $i +1
Next

; lesender Zugriff
For $i = 0 To UBound($aTest) -1
    ConsoleWrite("$aTest[" & $i & "] --> " & $aTest[$i] & @LF)
Next

[top] [Inhalt]

Viele Funktionen geben als Ergebnis ein Array zurück. Dabei wird häufig die Arrayposition [0] genutzt um die Anzahl der Arrayelemente anzugeben.
Ein Bsp. hierfür ist StringSplit(). Diese Funktion zerlegt einen gegebenen String an den angegebenen Trennzeichen und gibt ein Array zurück, das Anzahl der Elemente und die Ergebnisse der Trennung beinhaltet.

$string = "abcdef"
$aSplit = StringSplit($string, "") ; leerer Trennzeichenstring führt zu Trennung nach jedem Zeichen

; Inhalt des Array
$aSplit[0] = 6   ; hier haben wir die Anzahl der Ergebnisse
$aSplit[1] = "a"
$aSplit[2] = "b"
$aSplit[3] = "c"
$aSplit[4] = "d"
$aSplit[5] = "e"
$aSplit[6] = "f"

[top] [Inhalt]

Es empfiehlt sich, wenn man mit Funktionen arbeitet die ein Array zurückgeben sollen, vor der Weiterverarbeitung zu prüfen ob auch ein Array vorhanden ist, bzw. auszuwerten ob das Array Werte enthält. Da  StringSplit() im Fehlerfall ein Array mit dem Originalstring zurückgibt könnte das so aussehen:

$aSplit = StringSplit('Hallo Welt!', '|')
If $aSplit[0] = 1 Then                ; Wenn String nicht gesplittet wurde (Seperator nicht enthalten, String leer), dann Fehler
    MsgBox(0, '', 'Fehler')
Else
    For $i = 1 To UBound($aSplit) -1  ; Hier könnte man auch $arSplit[0] statt UBound($arSplit) -1 verwenden, da die Anzahl dort geführt wird
        MsgBox(0, 'Ergebnis Splitten', 'Wert Nr.:' & $i & ' = ' & $aSplit[$i])
    Next
EndIf

[top] [Inhalt]

Bei Funktionen, die nur im Erfolgsfall ein Array zurückgeben, empfiehlt sich folgende Überprüfung:

$aReturn = Funktionsaufruf()
If Not IsArray($arReturn) Then    ; Wenn kein Array, dann Fehler

Anmerkung:
Auch wenn Funktionen ein Array zurückgeben, sollte die Arrayvariable vorher deklariert werden. Allerdings ist es hier ausreichend den Namen zu deklarieren. Die Größe des Array wird von der Funktion festgelegt.
Für StringSplit ist dies nicht zwingend, aber _FileListToArray bringt einen Fehler, wenn die Arrayvariable vorab nicht deklariert wurde. Also ist es günstiger sich generell anzugewöhnen, Variablen vorab zu deklarieren.

[top] [Inhalt]

Beispiel:
Mit dem folgenden Beispiel will ich zeigen, wie man Programmieraufwand durch Einsatz eines Array verringern kann.

Eine GUI mit Controls wird deklariert. Die Controls sollen per Button wahlweise aktiv/deaktiv sein. Die Art der Controls lasse ich mal offen.

• Variante ohne Array

Local $aktiv = True

$GUI = GUICreate('Titel')
$control1 = GUICtrlCreate...
$control2 = GUICtrlCreate...
$control3 = GUICtrlCreate...
$control4 = GUICtrlCreate...
$control5 = GUICtrlCreate...
$control6 = GUICtrlCreate...
$control7 = GUICtrlCreate...
$button   = GUICtrlCreate...
; ...
If $msg = $button Then _SwitchActive()
; ...
Func _SwitchActive()
    Local $state
    If $aktiv Then
        $state = $GUI_DISABLE   ; aktueller Status 'aktiv' - dann 'disable' setzen
    Else
        $state = $GUI_ENABLE    ; aktueller Status 'inaktiv' - dann 'enable' setzen
    EndIf
    $aktiv = Not $aktiv         ; Wert umkehren (True zu False und umgekehrt)
    GUICtrlSetState($control1, $state)
    GUICtrlSetState($control2, $state)
    GUICtrlSetState($control3, $state)
    GUICtrlSetState($control4, $state)
    GUICtrlSetState($control5, $state)
    GUICtrlSetState($control6, $state)
    GUICtrlSetState($control7, $state)
EndFunc ; ==>_SwitchActive

• Variante mit Array

Local $aktiv = True
Local $arControl[8]

$GUI = GUICreate('Titel')
$arControl[0] = GUICtrlCreate...
$arControl[1] = GUICtrlCreate...
$arControl[2] = GUICtrlCreate...
$arControl[3] = GUICtrlCreate...
$arControl[4] = GUICtrlCreate...
$arControl[5] = GUICtrlCreate...
$arControl[6] = GUICtrlCreate...
$arControl[7] = GUICtrlCreate... ; Button
; ...
If $msg = $arControl[7] Then _SwitchActive()
; ...
Func _SwitchActive()
    Local $state = $aktiv ? $GUI_ENABLE : $GUI_DISABLE
    $aktiv = Not $aktiv
    For $i = 0 To 6
        GUICtrlSetState($arControl[$i], $state)
    Next
EndFunc ; ==>_SwitchActive

Es ist sichtbar, dass der Einsatz eines Array mit steigender Anzahl von Controls immer effektiver wird. Zum Setzen des Status ist, egal wieviel Controls vorhanden sind, nur eine Schleife mit 3 Codezeilen nötig.
Zusätzlich kann man auch die Erstellung der Controls in eine Schleife packen und somit den Aufwand minimieren.
Es erfordert natürlich etwas abstraktes Denken um sich hinter Array+Index etwas vorstellen zu können. Zum Anfang empfehle ich, einfach jede Zeile im Code mit einem kurzen Kommentar zu versehen. Dann kann man sich viel schneller einlesen.
Auch sollte der Name des Array immer aussagekräftig sein. Skriptpuristen schreiben da gerne mal: $a2 etc. Das spart zwar Schreibarbeit, beeinträchtigt aber die Lesbarkeit des Codes. Und gerade, wenn ich vielleicht um Hilfe zu suchen, mein Skript jemand Anderem vorlege, soll dieser auch die Chance haben sich zügig einzulesen.

[top] [Inhalt]

Das Mehrdimensionale Array am Beispiel eines 2-Dimensionalen Array

Das 2-Dimensionale Array kann man vergleichen mit einer mehrspaltigen Tabelle. Eine Dimension entspricht dann den Zeilen, während die andere die Spalten verkörpert.
Im Allgemeinen ist es üblich, dass die erste Dimension die Zeilen (Anzahl Elemente) und die zweite Dimension die Spalten (Werte je Element) enthält.

[top] [Inhalt]

Daraus ergibt sich folgende Deklaration:

Local $a2Dimensional[$ElementeZahl][$Spaltenzahl]

Hier muss das kleinste deklarierbare Array 1 Element und 1 Spalte enthalten. Natürlich macht es wenig Sinn, ein mehrdimensionales Array mit nur einer Spalte zu deklarieren, dann kann ich ja gleich ein 1-D Array verwenden.
Also ist die kleinste 'sinnvolle' Deklaration für ein 2-D Array: 1 Element und 2 Spalten.

Hier mal ein Array mit 5 Elementen und 2 Spalten:

Local $a2D[5][2]

[top] [Inhalt]

Auch hier gilt selbstverständlich für Zeilen- und auch Spaltenwert, dass das erste Element über den Index 0 angesprochen wird.
Ich weise jetzt allen Elementen Werte zu:

Zeile 1
  $a2D[0][0] = 'Zeile1/Spalte1'
  $a2D[0][1] = 'Zeile1/Spalte2'

Zeile 2
  $a2D[1][0] = 'Zeile2/Spalte1'
  $a2D[1][1] = 'Zeile2/Spalte2'

Zeile 3
  $a2D[2][0] = 'Zeile3/Spalte1'
  $a2D[2][1] = 'Zeile3/Spalte2'

Zeile 4
  $a2D[3][0] = 'Zeile4/Spalte1'
  $a2D[3][1] = 'Zeile4/Spalte2'

Zeile 5
  $a2D[4][0] = 'Zeile5/Spalte1'
  $a2D[4][1] = 'Zeile5/Spalte2'

Nun kommt der Punkt, an dem die 'Knoten' im Gehirn entstehen.

Ich möchte ja den Vorteil des Array nutzen und in einer Schleife alles abarbeiten.
Aber wie muss ich vorgehen?

Am Besten überlege ich, wie ich von Hand Daten in eine Tabelle eingebe:
   - Schreib Zeile 1, Spalte 1
   - dann schreib Zeile 1, Spalte 2
   - dann die nächste Zeile, Spalte 1 und
   - in dieser Zeile die Spalte 2
   - bis zur letzten Zeile

Als Pseudo-Code:
    Beginne bei StartZeile bis EndZeile
        Schreib Spalte 1 dieser Zeile
        Schreib Spalte 2 dieser Zeile
   Nächste Zeile

Bei 2 Spalten ist das so machbar. Habe ich aber mehrere Spalten, deren Anzahl ich auch vorab nicht weiß, müssen die Spalten auch in einer Schleife bearbeitet werden:
    Beginne bei StartZeile bis EndZeile
        Beginne bei erster Spalte bis letzte Spalte
           Schreib Spaltenwert
        Nächste Spalte
   Nächste Zeile

Und so sieht das in AutoIt-Code aus:

For $i = 0 To UBound($ar2D) -1                         ; $i ist der Zähler für die Zeilen
    For $k = 0 To UBound($ar2D, 2) -1                  ; $k ist der Zähler für die Spalten
        $ar2D[$i][$k] = 'Zeile' & $i & '/Spalte' & $k
    Next
Next

Hier ist auch zu sehen, dass die äußere Schleife UBound für die erste Dimension und die innere Schleife UBound für die zweite Dimension verwendet.

[top] [Inhalt]

Auch einige Funktionen liefern von Haus aus ein 2-D Array zurück.

Nehmen wir als Bsp. IniReadSection.
So sieht unsere INI-Datei aus:

    [sektion]
    schluesselA=1
    schluesselB=2
    schluesselC=3
    schluesselD=4

Jetzt lesen wir die Sektion ein:

#include <Array.au3>
Local $val                                                    ; Variable für Array deklarieren
$val = IniReadSection($pathINI, "sektion")                    ; Sektion einlesen
If Not IsArray($val) Then                                     ; Wenn zurückgegebene Variable kein Array ist
    MsgBox(0, '', 'INI-Sektion konnte nicht gelesen werden')
    Exit
EndIf
_ArrayDisplay($val, 'Inhalt INI-Sektion: [sektion]')          ; eingelesenes Array anzeigen

Schauen wir uns den Inhalt des Array an:

$val[0][0] = 4                ; Anzahl der eingelesenen Schlüssel-Wert Paare

$val[1][0] = 'schluesselA'    ; Name des Schlüssels
$val[1][1] = 1                ; Wert des Schlüssels

$val[2][0] = 'schluesselB'
$val[2][1] = 2

$val[3][0] = 'schluesselC'
$val[3][1] = 3

$val[4][0] = 'schluesselD'
$val[4][1] = 4

Möchte ich jetzt auf den Wert von 'schluesselC' zugreifen, muss ich das Array nach dem Schlüssel durchsuchen und mir den zugehörigen Wert ausgeben lassen:

Local $SuchWert, $SuchSchluessel = 'schluesselC'
For $i = 1 To UBound($val) -1
    If $val[$i][0] = $SuchSchluessel Then
        $SuchWert = $val[$i][1]
        ExitLoop
    EndIf
Next
MsgBox(0, '', 'gesuchter Wert ist: ' & $SuchWert)

Ich vergleiche also in jedem Element des Array ob der enthaltene Schlüsselname (Position[n][0]) mit meinem gesuchten Namen übereinstimmt.
Wird der Schlüssel gefunden, lese ich den zugehörigen Wert aus (Position[n][1]).

[top] [Inhalt]

Array als Arrayelement

In einem Array lassen sich unterschiedliche Datentypen speichern. Somit können auch Arrays als Elemente in einem Array enthalten sein.
Um Werte eines Array-Im-Array zu ändern, muss ich dieses Array in eine Variable auslagern, kann nun Werte ändern und anschließend das Array zurückschreiben.

; Innere Arrays erstellen (oder Werte zuweisen/ändern)
Global $aInner1[] = [11,12,13,14,15,16
Global $aInner2[] = [21,22,23,24,25,26
 
; und in das äußere Array schreiben 
Global $aOuter[] = [$aInner1,$aInner2]


Um nun einen Wert aus einem inneren Array auszulesen geht man entgegengesetzt vor:

; Zugriff auf Inneres Array2, 3.ter Wert ("23") 
Global $IndexArray = 
Global $IndexValue = 
 
; inneres Array extrahieren 
Global $aTmp = $aOuter[$IndexArray
 
; Wert an Index abfragen 
Global $getValue = $aTmp[$IndexValue
ConsoleWrite("$getValue --> " & $getValue & @LF)


Alternativ kann man auf das innere Array auch direkt zugreifen, ohne dieses in eine Variable zu extrahieren:

$getValue = ($aOuter[$IndexArray])[$IndexValue
ConsoleWrite("$getValue --> " & $getValue & @LF)


Die runden Klammern um ($aOuter[$IndexArray]) bewirken, dass der Ausdruck in der Klammer aufgelöst wird, $aOuter[$IndexArray] wird also zu $aInner2. Für dieses Array ist dann der in den eckigen Klammern folgende Index wirksam und gibt den dort gespeicherten Wert zurück.

Das funktioniert auch für tiefer verschachtelte Arrays. Jedoch wird eine derartige Verschachtelung wohl selten angewendet werden und ist auch schwerer nachvollziehbar.
Deshalb das Bsp. nur zum Nachweis, dass es möglich ist.

Global $aIn_Deep2_1[] = [1,2,3
Global $aIn_Deep2_2[] = [4,5,6
Global $aIn_Deep1_1[] = [$aIn_Deep2_1,$aIn_Deep2_2
Global $aIn_Deep2_3[] = [7,8,9
Global $aIn_Deep2_4[] = [10,11,12
Global $aIn_Deep1_2[] = [$aIn_Deep2_3,$aIn_Deep2_4
Global $aOuter[] = [$aIn_Deep1_1,$aIn_Deep1_2
 
; Ich möchte zugreifen auf "$aIn_Deep2_2", Wert an Index "1" (5) 
Global $IndexDeep1 = 
Global $IndexDeep2 = 
Global $IndexValue = 
 
; normaler Weg 
Global $aTmp1 = $aOuter[$IndexDeep1] ; gibt mir das Array "[$aIn_Deep2_1,$aIn_Deep2_2]" zurück 
Global $aTmp2 = $aTmp1[$IndexDeep2]  ; gibt mir das Array "[4,5,6]" zurück 
Global $getValue = $aTmp2[$IndexValue
ConsoleWrite("$getValue --> " & $getValue & @LF
 
; Direktzugriff 
Global $getValue = (($aOuter[$IndexDeep1])[$IndexDeep2])[$IndexValue
ConsoleWrite("$getValue --> " & $getValue & @LF


Mit mehrdimensionalen Arrays funktioniert dieser Zugriff ebenso.

[top] [Inhalt]

Array - Statisch oder Dynamisch

Ein Array ist statisch, wenn wir eine feste Größe für das Array definieren und dann damit arbeiten.
Dynamisch ist ein Array dann, wenn wir zur Laufzeit (also während das Programm abgearbeitet wird) die Größe des Array anpassen.

• statisch

Local $array[20]     ; Array hat 20 Elemente
Local $zaehler = -1  ; Indexwert vor erstem Eintrag

Ich kann jetzt bis zu 20 Werte in das Array eintragen. Der letzte Wert ist also: $array[19] = 'letzter Wert'. Immer wieder dran denken: Array-Adressierung beginnt mit Index 0, erfahrungsgemäß sind Indexfehler die häufigsten Fehler bei der Arbeit mit Array.

; im Programm gibt der User Daten in ein Input ein, die dem Array zugefügt werden sollen
$newData = GUICtrlRead($Input)
If $newData <> '' Then               ; prüfen ob nicht leer
    $zaehler += 1                    ; Indexwert um eins erhöhen, für ersten Eintrag jetzt 0
    If $zaehler < 20 Then            ; wenn Index kleiner als 20 ist
        $array[$zaehler] = $newData  ; Daten an Indexposition eintragen
    Else                             ; sonst Fehlermeldung
        MsgBox(0, 'FEHLER', 'Keine weiteren Einträge möglich! - Array ist voll.')
    EndIf
EndIf

• dynamisch

Local $array[1]                            ; Array hat zum Programmstart 1 Element

$newData = GUICtrlRead($Input)
If $newData <> '' Then                     ; prüfen ob nicht leer
    ; der erste Eintrag soll in das bestehende 1. Array-Element eingetragen werden, z.Zt. das letzte
    ; für weitere Einträge muß das Array vorher um ein Element vergrößert werden
    If $array[Ubound($array)-1] <> '' Then ; ist das letzte Element NICHT leer (dann ist es NICHT das erste)
        ReDim $array[Ubound($array)+1]     ; Arraygröße neu: 1 größer als aktuelle Größe Ubound($array)
    EndIf
    $array[Ubound($array)-1] = $newData    ; Daten in das letzte Element schreiben, im ersten Durchlauf ist dies das erste.
EndIf

Die aktuelle Arraygröße ist immer mit Ubound feststellbar.
Mit ReDim wird das Array neu bestimmt. Wichtig dabei - die enthaltenen Daten gehen nicht verloren (sofern nur die Elementeanzahl und nicht auch die Dimensionsgröße neu festgelegt wird).
Wichtig nochmal für das Befüllen des ersten Elements. Bei der Array-Deklaration ist $array[0] ohne Inhalt.
Dieses Element ist zum Programmstart auch das letzte Element, weil:
Ubound($array) gibt 1 zurück
• Da der Index fü r das letzte Element Obergrenze -1 ist, gilt: $Index = Ubound($array) -1;   ( 1 -1 = 0 )
• Im ersten Durchlauf ist somit $array[Ubound($array)-1] identisch mit $array[0], dem ersten Element. Die Inhaltsprüfung ergibt KEINEN Inhalt und somit wird die Arraygröße NICHT verändert, sondern in das letzte Element geschrieben.
In allen weiteren Durchläufen ist das letzte Element MIT Inhalt, somit wird das Array um ein Element vergrößert und dann in das neue, letzte Element geschrieben.

Es läßt sich nicht generell sagen, wann man mit statischen und wann mit dynamischen Array arbeiten soll.
Ein Entscheidungskriterium ist die Datenmenge. Die Neudeklaration der Arraygröße beansprucht natürlich auch Zeit. Insofern kann es durchaus günstiger sein, einfach vorab ein Array mit z.B. 1.000 Elementen zu definieren, wenn man weiß, dass diese Anzahl nicht überschritten wird. Oder man vergrößert das Array in Blöcken von 200-300 Elementen und erreicht so eine Kombination von statisch und dynamisch.

[top] [Inhalt]

Deklaration mit Wertzuweisung

Es können bei der Deklaration des Array sofort Werte zugewiesen werden. Hier die Vorgehensweise für 1D-Array und 2D-Array.

Local $aTest1[5] = [1,2,3,4,5]

Local $aTest2[5][2] = [ [1,1], [2,2], [3,3], [4,4], [5,5] ]
Local $aTest3[5][3] = [ [1,1,1], [2,2,2], [3,3,3], [4,4,4], [5,5,5] ]

Mit der neuen Syntax für Deklaration mit Wertzuweisung ist die Angabe der Elementezahl für das Array nicht zwingend erforderlich, also optional.

Local $aTest[] = _
[ 'Wert 1', _
  'Wert 2', _
  'Wert 3', _
  'Wert 4', _
  'Wert 5'  ]

Dies soll euch den Einstieg in die Arbeit mit Arrays erleichtern.
Fragen könnt ihr gerne an mich richten

Euer BugFix        Kontakt: Mail BugFix


Für die Codedarstellung wurde der Font Source Code Pro verwendet. Der Font steht unter der "Open Font License" (Lizenzdatei).
Der Text wurde in Quattrocento Roman verfaßt, einem ebenfalls frei verwendbaren Font.