In Dateilisten in String-Form, wie sie etwa Anwendungen als Kommandozeile übergeben werden, und die Sie in Visual Basic von Command$ geliefert bekommen, sind die einzelnen Dateinamen oftmals per Leerzeichen voneinander getrennt. Einen solchen Listen-String können Sie in Visual Basic 6 einfach mit der Split-Funktion in seine einzelnen Bestandteile zerlegen, etwa:
Split("abc.txt def.txt ghi.txt jkl.txt", " ")
Die Funktion gibt ein Array mit den einzelnen Bestandteilen als Elemente zurück.
Diese einfache Anwendung der Split-Funktion ist allerdings untauglich, wenn die Dateinamen selbst Leerzeichen enthalten. Das Betriebssystem "klammert" solche Dateinamen in Anführungszeichen ein. Eine Zerlegung mit Split würde die Dateinamen an den Stellen ihrer Leerzeichen unbarmherzig zerlegen. Der String
abc.txt "de fg.txt" hij.txt klm.txt "no p q.txt" "rs tu.txt" vwx.txt
würde zerlegt in:
abc.txt
"de
fg.txt"
hij.txt
klm.txt
"no
p
q.txt"
"rs
tu.txt"
vwx.txt
Sie können nun auf die Split-Funktion verzichten und mehr oder weniger aufwändig mit Abzählen beginnen und dabei den gesamten String Zeichen für Zeichen von Anfang bis Ende durchlaufen. Wenn Sie sich nicht beim Zählen und Merken und Trennen verheddern, werden Sie sicher zum Ziel gelangen, irgendwie. Aber abgesehen von der Gefahr des Verhedderns ist eine solche Zeichen-für-Zeichen-Schleife nicht sonderlich effizient.
Erheblich effizienter ist es, das anscheinend unbrauchbare Resultat, das Sie von Split geliefert bekommen, aufzugreifen und lediglich zu bereinigen. Wie Sie an obenstehender Folge sehen können, beginnt ein Array-Alement mit einem Anführungszeichen, wenn es der erste Teil eines zerrissenen Teiles ist, und es endet mit einem Anführungszeichen, wenn es der letzte Teil ist. Sie können also das Array in einer Schleife durchlaufen, sich dabei Anfang und Ende eines zerrissenen Teiles merken und dazwischen die Bruchstücke einfügen. Dabei verschieben Sie die fertig zusammengefügten Teile einfach aufeinanderfolgend aufwärts und kappen die zum Schluss überzähligen Array-Elemente mit einem ReDim Preserve. Je nach dem, ob die ursprünglich zur Einklammerung dienenden Anführungszeichen beibehalten werden sollen, oder ob sie entfallen sollen, können Sie sie entfernen oder stehen lassen. Sollte die Stringliste von vornherein keine Anführungszeichen enthalten, können Sie natürlich auf den Schleifendurchlauf verzichten.
Public Function SplitQuotesSpaces1(Str As String, _
Optional ByVal KeepQuotes As Boolean) As String()
Dim nParts() As String
Dim nInside As Boolean
Dim i As Integer
Dim ii As Integer
nParts = Split(Str, " ")
If InStr(Str, Chr$(34)) Then
For i = 0 To UBound(nParts)
If Left$(nParts(i), 1) = Chr$(34) Then
If Not KeepQuotes Then
nParts(ii) = Mid$(nParts(i), 2)
End If
nInside = True
ElseIf Right$(nParts(i), 1) = Chr$(34) Then
If Not KeepQuotes Then
nParts(ii) = nParts(ii) & " " & Left$(nParts(i), _
Len(nParts(i)) - 1)
Else
nParts(ii) = nParts(ii) & " " & nParts(i)
End If
ii = ii + 1
nInside = False
ElseIf nInside Then
nParts(ii) = nParts(ii) & " " & nParts(i)
Else
nParts(ii) = nParts(i)
ii = ii + 1
End If
Next 'i
ReDim Preserve nParts(0 To ii - 1)
End If
SplitQuotesSpaces1 = nParts
End Function
Eine andere Möglichkeit sieht auf den ersten Blick ziemlich widersinnig aus, weil der den einschließenden Anführungszeichen zugrunde liegende Paar-Gedanke zunächst völlig ignoriert wird. Hierbei wird der String zunächst mit der Split-Funktion anhand der Anführungszeichen als Trennzeichen zerlegt. Das Resultat sieht zunächst so aus (ich habe die Leerzeichen zur Kenntlichmachung durch rote Sternchen ersetzt):
abc.txt*
de*fg.txt
*hij.txt*klm.txt*
no*p*q.txt
*
rs*tu.txt
*vwx.txt
Aber auch hier ist ein brauchbares und programmatisch auswertbares Schema zu erkennen. Zunächst haben alle Teile, die mit einem Leerzeichen beginnen oder enden, zuvor keine Leerzeichen enthalten. Sie sind also recht einfach durch Trimmen der Leerzeichen zurecht zu stutzen. Enthalten diese Teile immer noch (ein) Leerzeichen, ist bzw. sind diese(s) tatsächliche Trenner, und der Teil-String kann noch einmal einfach per Split-Funktion aufgeteilt werden. Besteht aus einem Teil nur noch aus einem Leerzeichen, oder nach dem Trimmen nur noch aus einem Leeren String, braucht er nicht weiter berücksichtigt zu werden. Schließlich bleiben noch die Teile zu berücksichtigen, die nicht mit einem Leerzeichen beginnen oder enden, aber dennoch mindestens ein Leerzeichen enthalten. Diese Teile waren ursprünglich von Anführungszeichen eingeschlossen (die Sie gegebenenfalls wieder hinzufügen können). Sie durchlaufen einfach alle Teile, bearbeiten sie entsprechend und fügen sie in ein neues Array ein, das sie bei Bedarf in größeren Schüben mit ReDim Preserve vergrößern müssen. Sollten Sie es zufällig über den tatsächlichen Bedarf hinaus vergrößert haben, stutzen Sie es zum Schluss wieder zurück. Auch hier erübrigt sich natürlich die ganze Bastelei, wenn der Anfangs-String schon gar keine Anführungszeichen enthalten hat.
Public Function SplitQuotesSpaces2(Str As String, _
Optional ByVal KeepQuotes As Boolean) As String()
Dim nPartsQuotes() As String
Dim i As Integer
Dim nPartsStr() As String
Dim nTestTrim As String
Dim nPartPartsStr() As String
Dim ii As Integer
Dim nCount As Integer
Dim nBlock As Integer
If InStr(Str, Chr$(34)) Then
nPartsQuotes = Split(Str, Chr$(34))
nBlock = UBound(nPartsQuotes)
ReDim nPartsStr(0 To nBlock)
For i = 0 To nBlock
nTestTrim = Trim$(nPartsQuotes(i))
Select Case True
Case nTestTrim <> nPartsQuotes(i)
nPartPartsStr = Split(nTestTrim, " ")
For ii = 0 To UBound(nPartPartsStr)
nPartsStr(nCount) = nPartPartsStr(ii)
nCount = nCount + 1
If nCount > nBlock Then
nBlock = nBlock + 5
ReDim Preserve nPartsStr(0 To nBlock)
End If
Next 'ii
Case Len(nTestTrim) = 0
Case Else
If KeepQuotes Then
nPartsStr(nCount) = Chr$(34) & nPartsQuotes(i) & Chr$(34)
Else
nPartsStr(nCount) = nPartsQuotes(i)
End If
nCount = nCount + 1
If nCount > nBlock Then
nBlock = nBlock + 5
ReDim Preserve nPartsStr(0 To nBlock)
End If
End Select
Next 'i
ReDim Preserve nPartsStr(0 To nCount - 1)
SplitQuotesSpaces2 = nPartsStr
Else
SplitQuotesSpaces2 = Split(Trim$(Str), " ")
End If
End Function
Dass beide Verfahren nur dann sauber und fehlerfrei funktionieren können, wenn die Ausgangs-String "sauber" vorliegen, versteht sich von selbst. Beide kommen ins Schleudern, wenn sich etwa zwischen einleitendem Anführungszeichen und eingeschlossenem String oder zwischen diesem und dem abschließenden Anführungszeichen weitere Leerzeichen befinden (bei Dateinamen bzw. Pfadangaben an sich eine Unmöglichkeit).
Sie werden sich (und mich) nun fragen, welche der beiden Varianten die bessere, empfehlenswertere sei. Dazu kann ich nach einigen Zeit-Messungen nur sagen, dass bei kurzen Ausgangs-Strings die erste Variante ungefähr ein Drittel schneller ist. Aufgabenstellungen, bei denen solche kurzen String-Listen vorkommen, sind jedoch allermeistens nicht zeitkritisch - es ist also egal. Die zweite Variante mag vielleicht eher ein wenig wegen ihres größeren Umfangs und ihres etwas verqueren Prinzips abschreckend wirken.
Doch je länger der Ausgangs-String ist, je länger die Teil-Strings sind und je mehr in Anführungszeichen eingeschlossene Teile im Ausgangs-String enthalten sind, um so mehr holt die zweite Variante gegenüber der ersten auf und überholt sie schließlich. Dies liegt vor allem daran, dass die erste Variante weitaus ausgiebiger Gebrauch von String-Operationen macht, bei denen Teil-Strings hin und her kopiert werden. Eine solche Kopiererei frisst grundsätzlich immer Performance und benachteiligt die erste Variante zunehmend.
Die zweite Variante habe ich Ihnen aber auch noch aus einem anderen Grund gezeigt. Es sieht nämlich derart widersinnig und zunächst entgegengesetzt der ursprünglichen Aufgabenstellung aus, dass man sie vom "natürlichen Programmierer-Menschenverstand" her eher erst gar nicht in Erwägung zieht. Doch mag gerade erst dieser Ansatz bei so manch kompliziert und an kaum einer Regel festmachbaren Teilungsaufgabe eine brauchbare Grundlage ergeben. Setzen Sie sich doch einfach einmal damit an andere Paarungs-Probleme, etwa an Klammerungen in mathematischen Ausdrücken oder an Strings voller HTML-("<...>")-Tags...
|