|
|
|
|
|
Selbst routinierten Vielschreibern unterlaufen sie ständig, die
Ubchstabendreher, pardon, Buchstabendreher. Wie wäre es damit, dass
Sie Ihre TextBoxen oder RichTextBoxen mit einer Funktion aufrüsten,
die auf Tastendruck einen Dreher korrigiert?
"Das ist doch kein Problem!" - werden Sie sagen.
"Man selektiere einfach die beiden betroffenen Zeichen bzw.
schneide sie aus einem String heraus, vertausche den rechten und
linken Buchstaben und füge das korrigierte Buchstabenpaar wieder an
der richtigen Stelle ein...". Ja, das ist so einfach, dass ich
Ihnen dafür sicher kein Code-Beispiel zu zeigen brauche, oder?
Aber ist es wirklich damit getan? Was ist mit dem obenstehenden
"Ubchstabendreher", den ich Ihnen durchaus mit voller
Absicht untergejubelt habe? Er würde lediglich zu einem "bUchstabendreher"
vermurkst - was sicher nicht das gewünschte Resultat wäre. Die
Groß- und Kleinschreibung an den betroffenen Positionen müsste
also auf jeden Fall noch beibehalten werden. Versuchen wir uns
einmal an einer ersten Funktion, die die Aufgabenstellung zu
bewältigen versucht. Gleich vorweg gesagt: Zur Prüfung, ob es sich
bei einem Zeichen um einen Klein- oder um einen Großbuchstaben
handelt, verwenden wir die API-Funktionen IsCharLower
und IsCharUpper,
die sowohl schneller ausgeführt werden als auch einfacher
handzuhaben sind, als die Konstruktion einer Prüfung über LCase$
und UCase$.
Der Funktion SwapChars1 wird der zwei Zeichen lange String, der
die verdrehten Buchstaben enthält, übergeben. Sie liefert den
korrigierten Dreher als Rückgabewert wieder ab. Für den Fall, dass
der übergebene String länger als zwei Zeichen sein sollte, wird
der Überhang ab dem dritten Zeichen dem Rückgabe-String wieder
angehängt.
Private Declare Function IsCharLower Lib "user32" _
Alias "IsCharLowerA" (ByVal cChar As Byte) As Long
Private Declare Function IsCharUpper Lib "user32" _
Alias "IsCharUpperA" (ByVal cChar As Byte) As Long
Public Function SwapChars1(Chars As String) As String
Dim nCharR As String
Dim nCharL As String
Dim nCases As Integer
nCharR = Mid$(Chars, 2, 1)
nCharL = Left$(Chars, 1)
If IsCharUpper(Asc(nCharL)) Then
nCases = 1
End If
If IsCharUpper(Asc(nCharR)) Then
nCases = nCases Or 2
End If
Select Case nCases
Case 0, 3
SwapChars = nCharR & nCharL & Mid$(Chars, 3)
Case 1
SwapChars = UCase$(nCharR) & LCase$(nCharL) & _
Mid$(Chars, 3)
Case 2
SwapChars = LCase$(nCharR) & UCase$(nCharL) & _
Mid$(Chars, 3)
End Select
End Function
Das sieht schon ganz gut aus, nicht wahr? Die Lösung enthält
aber noch ein paar Fallgrübchen. Eine solche Operation sollte ohne
weiteres reversibel sein. Der korrigierte Dreher muss sich
zurückdrehen lassen, falls sich etwa der Anwender nachträglich
entscheiden sollte, dass der Dreher nun doch keiner war. In der
gezeigten Form ist sie unter bestimmten Umständen jedoch nicht
umkehrbar. Ist nämlich eines der Zeichen ein Großbuchstabe, das
andere jedoch eine Ziffer oder irgendein anderes nicht
alphanumerisches Zeichen (wie etwa ein Leerzeichen oder ein
Bindestrich), wird der Großbuchstabe zwar als solcher erkannt, die
Ziffer bzw. das Zeichen jedoch als Kleinbuchstabe identifiziert. Der
Großbuchstabe wird daher in einen Kleinbuchstaben an der
vertauschten Position verwandelt, was bereits der erste Fehler
wäre. Des weiteren muss die versuchte Umwandlung der Ziffer bzw.
des Zeichens in einen Großbuchstaben zwangsläufig scheitern. Ein
erneuter, rückgängigmachender Aufruf für diese Kombination träfe
dann auf vermeintliche zwei Kleinbuchstaben, die einfach nur
ausgetauscht würden. Die umgekehrte Umkehrung hätte nun also einen
Kleinbuchstaben plus der Ziffer bzw. dem Zeichen an den
ursprünglichen Positionen zur Folge.
Wir führen daher zuallererst mittels der API-Funktion IsCharAlpha
eine Prüfung durch, ob beide Zeichen reine Buchstabenzeichen sind.
Trifft dies für mindestens eines der beiden Zeichen nicht zu, wird
lediglich eine einfache Vertauschung vorgenommen.
Private Declare Function IsCharAlpha Lib "user32" _
Alias "IsCharAlphaA" (ByVal cChar As Byte) As Long
Public Function SwapChars2(Chars As String) As String
Dim nCharR As String
Dim nCharL As String
Dim nCases As Integer
nCharR = Mid$(Chars, 2, 1)
nCharL = Left$(Chars, 1)
If CBool(IsCharAlpha(Asc(nCharL))) _
And CBool(IsCharAlpha(Asc(nCharR))) Then
If IsCharUpper(Asc(nCharL)) Then
nCases = 1
End If
If IsCharUpper(Asc(nCharR)) Then
nCases = nCases Or 2
End If
Select Case nCases
Case 0, 3
SwapChars = nCharR & nCharL & Mid$(Chars, 3)
Case 1
SwapChars = UCase$(nCharR) & LCase$(nCharL) & _
Mid$(Chars, 3)
Case 2
SwapChars = LCase$(nCharR) & UCase$(nCharL) & _
Mid$(Chars, 3)
End Select
End If
End Function
So weit, so gut - wir könnten es bei diesem Stadium der Lösung
belassen. Nun wäre es aber möglich, dass zufällig (oder aus
welchem Grund auch immer) eines der beiden Zeichen ein
Zeilentrennzeichen ist. Etwa dann, wenn sich die Schreibmarke gerade
am Anfang oder am Ende einer Zeile befindet, während der Anwender
(versehentlich) den Befehl zum Tauschen gibt. Allerdings macht es
jedoch nicht viel Sinn, eine Vertauschung über einen Zeilenwechsel
(in der Regel wäre ein solcher ja sogar ein Absatzbeginn)
vorzunehmen. Und zudem besteht eine Zeilentrennung meistens aus den
zwei aufeinanderfolgenden Zeichen für den (historischen) Wagen-
bzw. Druckkopfrücklaufs (vbCR = "Carriage Return")
und der eigentlichen Zeilenschaltung (vbLf = "Line
Feed"), die nicht auseinander gerissen werden dürfen.
In diesen Fall müssen wir also eine Tauschung strikt unterbinden.
Fügen wir nun noch die im Bedarfsfall vielleicht ganz nützliche
Option hinzu, eine einfache Tauschung ohne Berücksichtigung der
Groß-/Kleinschreibung zu erzwingen (optionaler Parameter
SwapSimple, Datentyp Boolean), haben wir eine Funktion, die wir
universell und sicher einsetzen können:
Public Function SwapChars(Chars As String, _
Optional ByVal SwapSimple As Boolean) As String
Dim nCharR As String
Dim nCharL As String
Dim nCases As Integer
nCharR = Mid$(Chars, 2, 1)
nCharL = Left$(Chars, 1)
If Not SwapSimple Then
If CBool(IsCharAlpha(Asc(nCharL))) _
And CBool(IsCharAlpha(Asc(nCharR))) Then
If IsCharUpper(Asc(nCharL)) Then
nCases = 1
End If
If IsCharUpper(Asc(nCharR)) Then
nCases = nCases Or 2
End If
Select Case nCases
Case 0, 3
SwapChars = nCharR & nCharL & Mid$(Chars, 3)
Case 1
SwapChars = UCase$(nCharR) & LCase$(nCharL) & _
Mid$(Chars, 3)
Case 2
SwapChars = LCase$(nCharR) & UCase$(nCharL) & _
Mid$(Chars, 3)
End Select
Exit Function
End If
End If
If Not (CBool(InStr(Chars, vbCr)) _
Or CBool(InStr(Chars, vbLf))) Then
SwapChars = nCharR & nCharL & Mid$(Chars, 3)
Else
SwapChars = Chars
End If
End Function
Diese Funktion ist jedoch erst etwas mehr als die halbe Miete.
Sie verarbeitet ja nur ein bereits isoliertes Zeichenpaar und gibt
es korrigiert zurück. Es fehlen noch die Operationen, die das zu
korrigierende Zeichenpaar an der Position der Schreibmarke in einer
TextBox oder einer RichTextBox auslesen und wieder einfügen.
In den Prozeduren SwapCharsTextBox und SwapCharsRichTextBox (die
sich nur im Datentyp des übergebenen Objekts unterscheiden) wird
zunächst die Position der Schreibmarke ermittelt und festgehalten -
sie entspricht der Eigenschaft SelStart.
Dann werden der Beginn der Markierung vor das Zeichen links von der
Position und die Länge der Markierung auf 2 gesetzt
(Eigenschaft SelLength
gleich 2). Unserer Funktion SwapChars wird nun der
Inhalt der Eigenschaft SelText
(eben die zwei nun markierten Zeichen) übergeben und nach Rückkehr
aus der Funktion durch den Rückgabewert, also durch das korrigierte
Zeichenpaar ersetzt. Nun stellen wir die Schreibmarke wieder an die
ursprüngliche Position zurück. Falls sich die Schreibmarke am
Textanfang oder am Textende befindet, wird die Tauschung nicht
ausgeführt - weil es ja nichts zu vertauschen gibt. Im optionalen
Parameter SwapSimple übergeben wir gegebenenfalls wieder die
Anforderung einer strikten Tauschung, die wir an unsere Funktion
SwapChars weiterreichen.
Public Sub SwapCharsTextBox(TextBox As TextBox, _
Optional ByVal SwapSimple As Boolean)
Dim nSelStartOld As Integer
With TextBox
nSelStartOld = .SelStart
Select Case nSelStartOld
Case 1 To Len(.Text) - 1
.SelStart = nSelStartOld - 1
.SelLength = 2
.SelText = SwapChars(.SelText, SwapSimple)
.SelStart = nSelStartOld
End Select
End With
End Sub
Public Sub SwapCharsRichTextBox(TextBox As RichTextBox, _
Optional ByVal SwapSimple As Boolean)
' *** Inhalt identisch zu SwapCharsTextBox ***
End Sub
Warum ich die Vertauschung nicht gleich in dieser Prozedur
vornehme? Ganz einfach - damit die folgende Funktion SwapCharsString
für die Tauschung zweier Zeichen innerhalb eines Strings beliebiger
Herkunft ebenfalls in den Genuss der separaten Vertauschungsfunktion
kommt und wir uns doppelten, identischen Code sparen.
Auch hier wird die Tauschung nur vorgenommen, wenn sich die
Tauschposition (die wir hier natürlich extra als Parameter angeben
müssen) nicht am Anfang oder am Ende des String befindet. Der
Einfachheit halber machen wir uns zunutze, dass die
(historisch-anachronistische Basic-)Funktion Mid$
auch links
von einer Zuweisung stehen darf. Sie funktioniert quasi wie die
SelText-Eigenschaft einer TextBox, indem einerseits der über ihre
Parameter angegebene Bereich ausgelesen wird und andererseits ein
angegebener Bereich durch einen anderen String ersetzt werden kann.
Public Function SwapCharsString(Text As String, _
ByVal Position As Long, _
Optional ByVal SwapSimple As Boolean) As String
Dim nText As String
nText = Text
Select Case Position
Case 1 To Len(nText) - 1
Mid$(nText, Position, 2) = _
SwapChars(Mid$(nText, Position, 2))
End Select
SwapCharsString = nText
End Function
Als Eingangs erwähnten Tastendruck, mit dem der Anwender
komfortabel die Tauschung veranlassen kann, bieten Sie am besten
eine Kombination aus einem Buchstaben und der Strg-Taste
an, zum Beispiel Strg+W - diese Kombination ist bequem
zu erreichen und leicht zu merken. Buchstabenkombinationen mit der Strg-Taste
werden auch als KeyAscii-Wert in der Ereignisprozedur KeyPress
einer TextBox (bzw. RichTextBox) gemeldet, auch wenn sie eigentlich
gar kein darstellbares Zeichen repräsentieren. Dies stammt
wahrscheinlich noch aus den Zeiten, als alle Ascii-Zeichen als
"druckbar" galten, wobei die Zeichen-Codes kleiner als
32 zur unmittelbaren Druckersteuerung verwendet wurden (wie
etwa die bereits erwähnten Zeilenschaltungszeichen vbCr =
Ascii-Code 13 und vbLf = Ascii-Code 10). Wenn
auch diese Zeichen nicht in einer TextBox sichtbar werden,
erscheinen sie jedoch hörbar - es wird ein Beep-Signal ausgelöst.
Daher sollten Sie die Rückgabe des KeyAscii-Wertes unterdrücken,
indem Sie diesen gleich 0 setzen. Für die Zeichen mit
Ascii-Codes kleiner als 32 gibt es allerdings keine von
Visual Basic vordefinierten Key-Konstanten. Die Auswertung stellt
aber kein Problem dar, da der Unterschied zum entsprechenden
Großbuchstaben immer genau 64 beträgt ("W"
ist 87, Strg+W ist 23).
Private Sub Text1_KeyPress(KeyAscii As Integer)
Select Case KeyAscii + 64
Case vbKeyW
SwapCharsTextBox Text1
KeyAscii = 0
End Select
End Sub

|
|
|