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 die Anzahl der
Elemente bestimmen.
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 gewissermaßen 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:
Dim $arTest[5] ; Die Anzahl der Elemente wird in eckigen Klammern hinter dem Variablennamen angegeben.
Der kleinste, deklarierbare Wert ist ein Array mit einem Element:
Dim $arTest[1]
Angesprochen wird dieses Element mit: $arTest[0]
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.
$arTest[0]
= 'Wert 1'
$arTest[1]
= 'Wert 2'
$arTest[2]
= 'Wert 3'
$arTest[3]
= 'Wert 4'
$arTest[4]
= 'Wert 5'
Wer nicht mit dem Index 0 arbeiten möchte, kann an dieser Position ja einen Zähler platzieren oder einfach leer lassen.
Die identische Wertzuweisung kann ich auch in einer
Schleife ausführen.
Hierbei lasse ich den Index in der Schleife bis zum Endwert erhöhen.
Da ich nicht immer weiß, wie viel Elemente das Array enthält, verwende ich die
Funktion UBound()
zur Größenbestimmung.
Für unser Array liefert: UBound($arTest)
den Wert 5.
Das letzte Element muss also 1 kleiner als UBound
sein, da wir mit 0 beginnen zu adressieren.
For
$i =
0 To
UBound($arTest)
-1
$arTest[$i]
= 'Wert '
&
$i +1
; Da die Ziffer hinter Wert nicht mit 0, sondern
mit 1 beginnt, muss ich 1 größer als der Schleifenzähler setzen
Next
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().
Nehmen wir an, wir haben einen String mit durch '|' getrennten Werten.
$string = 'a|b|d|f|h|i|k'
Wir möchten jetzt jeden Wert einzeln haben:
$arSplit = StringSplit($string, '|')
Und das ist jetzt der Inhalt von $arSplit
$arSplit[0]
= 7
$arSplit[1]
= 'a'
$arSplit[2]
= 'b'
$arSplit[3]
= 'd'
$arSplit[4]
= 'f'
$arSplit[5]
= 'h'
$arSplit[6]
= 'i'
$arSplit[7]
= 'k'
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:
$arSplit
= StringSplit($string,
'|')
If
$arSplit[0]
= 1
Then ; Wenn String nicht gesplittet wurde (Seperator
nicht enthalten, String leer), dann Fehler
MsgBox(0,
'', 'Fehler')
Else
For $i
= 1
To UBound($arSplit)
-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
& ' = '
& $arSplit[$i])
Next
EndIf
Bei Funktionen, die nur im Erfolgsfall ein Array zurückgeben, empfiehlt sich folgende Überprüfung:
$arReturn
= Funktionsaufruf()
If
(Not
IsArray($arReturn ))
Then ; Wenn kein Array, dann
Fehler
Der Ausdruck (Not IsArray($arReturn )) muss nicht zwingend in Klammern gesetzt werden. Es dient hier zur Verbesserung der Übersicht.
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.
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
Dim
$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
_SwitchActiv()
;...
Func
_SwitchActiv()
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
; ==>_SwitchActiv
• Variante mit Array
Dim
$aktiv =
True
Dim $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
_SwitchActiv()
;...
Func
_SwitchActiv()
Local
$state
If
$aktiv
Then
$state
=
$GUI_DISABLE
Else
$state
=
$GUI_ENABLE
EndIf
$aktiv
= Not
$aktiv
For
$i =
0
To
6
GUICtrlSetState($arControl[$i],
$state)
Next
EndFunc
; ==>_SwitchActiv
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.
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: $a1,
$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.
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.
Daraus ergibt sich folgende Deklaration:
Dim $ar2Dimensional[$ElementeZahl][$Spaltenzahl]
Hier muß 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:
Dim $ar2D[5][2]
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
$ar2D[0][0]
= 'Zeile1/Spalte1'
$ar2D[0][1]
= 'Zeile1/Spalte2'
Zeile 2
$ar2D[1][0]
= 'Zeile2/Spalte1'
$ar2D[1][1]
= 'Zeile2/Spalte2'
Zeile 3
$ar2D[2][0]
= 'Zeile3/Spalte1'
$ar2D[2][1]
= 'Zeile3/Spalte2'
Zeile 4
$ar2D[3][0]
= 'Zeile4/Spalte1'
$ar2D[3][1]
= 'Zeile4/Spalte2'
Zeile 5
$ar2D[4][0]
= 'Zeile5/Spalte1'
$ar2D[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 muß 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
Wie ihr seht verwende ich in der äußeren Schleife
UBound($ar2D)
und in der inneren Schleife UBound($ar2D,
2) .
Die äußere Schleife gilt für die erste Dimension, deren Wert standardmäßig von
der Funktion UBound(
) zurückgegeben wird.
Um die Spaltenzahl zu ermitteln, muß ich aber der Funktion
UBound(
) mitteilen, für welche Dimension ich den Wert
ermitteln möchte, in unserem Fall ist dies die 2.
Nicht durcheinander kommen. Hier wird nicht der Index verwendet! Für die
erste Dimension die 1, für die zweite die 2 usw.
Ich erwähnte oben, dass es üblich ist, dass die erste
Dimension die Zeilen (Anzahl Elemente) und die zweite Dimension die Spalten
(Werte je Element) enthält.
Das ist aber nicht zwingend! Man kann ein Array auch genau entgegengesetzt
bestücken. Also die Elemente in den Spalten und zugehörige Werte in den Zeilen.
Darauf gehe ich jetzt aber nicht näher ein, man muß halt in der
Schleifenabarbeitung dann in der äußeren Schleife die Spalten und in der inneren
Schleife die Zeilen führen.
Auch einige Funktionen liefern von Haus aus ein 2-D Array zurück.
Nehmen wir als Bsp.
IniReadSection("filename",
"section")
So sieht unsere INI-Datei aus:
[sektion]
schluesselA=1
schluesselB=2
schluesselC=3
schluesselD=4
Jetzt lesen wir die Sektion ein:
#include
<array.au3>
Dim $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, muß ich das Array nach dem Schlüssel durchsuchen und mir den zugehörigen Wert ausgeben lassen:
Dim
$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]).
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
Dim
$array[20]
; Array hat 20
Elemente
Dim
$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.
Ich werde das sicher noch öfter wiederholen, denn
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
Dim
$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 weiter 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($array)
feststellbar.
Mit ReDim wird das Array neu
bestimmt. Wichtig dabei - die enthaltenen Daten gehen nicht verloren.
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.
Dies soll euch den Einstieg in die Arbeit mit Arrays
erleichtern. Ich habe mich bewußt etwas beschränkt um nicht mit zu vielen
Details Verwirrung zu stiften.
Ich würde mich aber freuen, wenn ihr mir mitteilt, zu welchen Punkten ihr nähere
Erläuterungen wünscht. Wenn man erst mal mit umgehen kann ist es gar nicht so
einfach, sich auf die Probleme der Anfangszeit zurück zu besinnen.
Eure Fragen mit den Erklärungen, würde ich dann in Form einer FAQ anhängen.
Euer BugFix Kontakt: Mail BugFix