Mittwoch, 21. Dezember 2011

Datumsspielereien (Teil 8) / Date gadgets (Part 8)

Die TIME() Funktion von Visual Foxpro liefert uns eine Uhrzeit als Characterstring. Wenn wir zu dieser Zeit jedoch Stunden, Minuten oder Sekunden hinzuaddieren möchten, dann ist das bei einem String eine mühsame Arbeit.

Anstatt mit TIME() sollten wir für Berechnungen immer mit DATETIME() arbeiten. Hier steht uns neben dem Datum auch die Uhrzeit für Berechnungen zur Verfügung. Allerdings wird ein DATETIME() + 1 ein gänzlich anderes Ergebnis liefern als DATE() + 1. Wenn DATE() grundsätzlich auf Tagesbasis seine Berechnungen durchführt, so reagiert DATETIME() auf Sekundenbasis.
Um mit DATETIME() also Stunden oder Minuten zu berechnen müssen wir entweder x * 60 * 60 für Stunden oder x * 60 für Minuten eingeben.

Zur Extraktion der Uhrzeit vom Datum/Zeit-Wert steht uns dann im Anschluß die Funktion TTOC() zur Verfügung. Wichtig hierbei ist der zweite Parameterwert den wir zwingend auf 2 setzen müssen.

CLEAR
ltDatetime = DATETIME()
? ltDatetime
? CalcTime( 5 , 0 , 0 , ltDatetime )
? CalcTime( 0 , 5 , 0 , ltDatetime )
? CalcTime( 0 , 0 , 5 , ltDatetime )
? CalcTime( 5 , 5 , 5 , ltDatetime )

? CalcTime( -5 )


FUNCTION CalcTime as String
LPARAMETERS vHours as Integer, vMinutes as Integer, vSeconds as Integer, vDatetime as Datetime

    vHours      = EVL( vHours    , 0 )
    vMinutes    = EVL( vMinutes  , 0 )
    vSeconds    = EVL( vSeconds  , 0 )
    vDatetime   = EVL( vDatetime , DATETIME() )

    vDatetime = vDatetime + ( vHours   * 60 * 60 )
    vDatetime = vDatetime + ( vMinutes * 60 )
    vDatetime = vDatetime + ( vSeconds )

    RETURN TTOC( vDatetime , 2 )

ENDFUNC 

Mittwoch, 14. Dezember 2011

Erstellen von Tablet-PC Anwendungen mit VFP / Creating Tablet-PC Applications with VFP

Vor ein paar Wochen stolperte ich bei der Suche nach Informationen zur Erstellung von Tablet PC Anwendungen mit Visual Foxpro über den folgenden Blogbeitrag:

http://www.tabletpcblog.de/2010/03/11/erstellen-von-tablet-pc-anwendungen-mit-visual-foxpro/

Bei diesem Posting handelt es sich um eine Übersetzung eines MSDN Artikels von Mike Stewart aus dem Jahr 2004:

http://msdn.microsoft.com/en-us/library/ms965060

Damals, zu XP Zeiten waren die notwendigen Vorbereitungen noch ein wenig aufwändiger als heute unter Windows 7, welches die benötigten Bibliotheken bereits 'ab Werk' mit sich führt. Und erstaunlicherweise funktioniert der in den Beiträgen enthaltene Beispielcode unter Windows 7 ohne Probleme.

Wer also mit dem Gedanken spielt, seine Anwendung für Touchscreens fit zu machen, der sollte sich die Beiträge einmal genauer zu Gemüte führen!

Montag, 5. Dezember 2011

Zwei Forms gleichzeitig bewegen / Moving two forms simultaneously

Wer noch die alte Version von WinAMP(R) kennt weiß, dass dort insgesamt zwei zusätzliche, an die Hauptform andockbare Fenster zur Verfügung standen.

Einen solchen Effekt können wird in Visual FoxPro ebenfalls erzielen. Hierzu binden wir den 'Moved' Event der Hauptform einfach an die zugehörigen Unterforms.

Der für die Repositionierung benötigte Code befindet sich in der Prozedur 'MoveSubForm':

oFrmSub.Top = oFrmMain.Top
IF    ( oFrmMain.Left + oFrmMain.Width + 15 + oFrmSub.Width ) > _screen.Width
    oFrmSub.Left = oFrmMain.Left - oFrmSub.Width
ELSE 
    oFrmSub.Left = oFrmMain.Left + oFrmMain.Width + 15
ENDIF 

Da das MOVED() Event feuert, sobald wir die Hauptform verschieben, sorgt der o.a. Code für die Repositionierung der Unterform auf Basis der aktuellen Position der Hauptform.


Hier nun der vollständige Mustercode:

CLEAR ALL 
PUBLIC oFrmMain as Form, oFrmSub as Form, oHandler as Object 
* // Forms und Handler erzeugen                        
* // create forms and handler                        
oFrmMain            = CREATEOBJECT( [form] )
oFrmSub             = CREATEOBJECT( [form] )
oHandler            = CREATEOBJECT( [oHandlerObj] )
* // Hauptform positionieren und konfigurieren        
* // positioning and configureing the main form        
oFrmMain.Top        = 100 
oFrmMain.Left       = 100 
oFrmMain.AddObject([cmdReleaseAll],[commandbutton])
oFrmMain.cmdReleaseAll.Caption  = [Exit]
oFrmMain.cmdReleaseAll.Height   = 25
oFrmMain.cmdReleaseAll.Visible  = .T. 
oFrmMain.Visible                = .T.
* // Unterform konfigurieren                        
* // configureing the subform                        
oFrmSub.BorderStyle             = 0 
oFrmSub.TitleBar                = 0
oFrmSub.BackColor               = RGB( 128 , 128 , 128 )
oFrmSub.Visible                 = .T. 
* // Unterform direkt neben Hauptform platzieren    
* // place the subform right beside the main form    
oHandler.MoveSubForm() 
* // Handler an relevante Events binden                
* // bind the handler to essential events            
BINDEVENT( oFrmMain , [Moved] , oHandler , [MoveSubForm] )
BINDEVENT( oFrmMain.cmdReleaseAll , [Click] , oHandler , [CloseSubForm] )


DEFINE CLASS oHandlerObj AS custom

    Name = [ohandler]

    PROCEDURE MoveSubForm
        oFrmSub.Top = oFrmMain.Top
        IF    ( oFrmMain.Left + oFrmMain.Width + 15 + oFrmSub.Width ) > _screen.Width
            oFrmSub.Left = oFrmMain.Left - oFrmSub.Width
        ELSE 
            oFrmSub.Left = oFrmMain.Left + oFrmMain.Width + 15
        ENDIF 
    ENDPROC

    PROCEDURE CloseSubForm 
        UNBINDEVENT( oFrmMain , [Moved] , oHandler , [MoveSubForm] )
        UNBINDEVENT( oFrmMain.cmdReleaseAll , [Click] , oHandler , [CloseSubForm] )
        oFrmMain.Release
        oFrmSub.Release
        oFrmMain    = .Null.
        oFrmSub     = .Null.
        RELEASE oFrmMain, oFrmSub
    ENDPROC 
    
ENDDEFINE

Dienstag, 29. November 2011

Entfernen von Worten aus Textdateien / Removing words from textfiles

Visual FoxPro stellt uns nicht nur eine Menge Funktionen zur Stringmanipulation zur Verfügung. Auch das Öffnen und Bearbeiten von Dateiinhalten kann auf mehreren Wegen durchgeführt werden.

Wollen wir bspw. bestimmte Worte (oder Zeichenfolgen) aus einer Textdatei entfernen, so können wir die Datei natürlich über die Funktionen FOPEN(), FGETS(), FWRITE() und FCLOSE() verarbeiten, mit Hilfe von FILETOSTR() und STRTOFILE() funktioniert dies jedoch erheblich einfacher.

Über FILETOSTR() lesen wir den Inhalt einer Datei komplett in eine Variable die wir anschliessend gezielt bearbeiten und mit STRTOFILE() wieder auf die Festplatte schreiben können.

Im Blogeintrag zur Manipulation von MP3 ID3v1 Tags arbeite ich ebenfalls mit diesen beiden Funktionen, allerdings wird dort ein kompletter Bereich des Datenstroms verändert.

lcFile = [D:\Temp\test.txt]
lcFileContent = FILETOSTR( lcFile )

* Do something

STRTOFILE( lcFileContent , lcFile )

Zur Verarbeitung des Datenstroms welcher sich nun in der Variablen 'lcFileContent' befindet, stehen uns zwei Funktionen zur Verfügung, von denen die eine ( OCCURS() ) die Häufigkeit des Auftretens der zu entfernenden Zeichenkette aufzeigt und die andere ( STRTRAN() ) den Austausch bzw. das Entfernen vornimmt.

Im nun folgenden Codebeispiel wird zunächst eine Testdatei erzeugt und anschliessend wieder eingelesen, das Auftreten der Zeichenfolge 'Lorem ipsum' geprüft und das zweite Auftreten aus dem Datenstrom entsorgt. Anschliessend wird die Datei aktualisiert.



* // Schritt 1: Demodaten erzeugen
* // Step 1   : Creating demo data
LOCAL    lcFileName as String , lcString as String , lcFileContent as String , ;
        lcSafety as String
lcFileName    = [D:\Temp\test.txt]
lcSafety    = SET( [Safety] )
SET SAFETY OFF 

* // Der Beispieltext enthält insgesamt 4x die Zeichenfolge 'Lorem ipsum'
* // This demo data contains 4x the character string 'Lorem ipsum'
TEXT TO lcString TEXTMERGE NOSHOW PRETEXT 2+4
    Lorem ipsum dolor sit amet, consetetur sadipscing elitr, 
    sed diam nonumy eirmod tempor invidunt ut labore et dolore 
    magna aliquyam erat, sed diam voluptua. At vero eos et 
    accusam et justo duo dolores et ea rebum. Stet clita kasd 
    gubergren, no sea takimata sanctus est Lorem ipsum dolor 
    sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing 
    elitr, sed diam nonumy eirmod tempor invidunt ut labore et 
    dolore magna aliquyam erat, sed diam voluptua. At vero eos 
    et accusam et justo duo dolores et ea rebum. Stet clita 
    kasd gubergren, no sea takimata sanctus est Lorem ipsum 
    dolor sit amet.
ENDTEXT 
STRTOFILE( lcString , lcFileName )

* // Schritt 2: Einlesen der erzeugten Testdatei
* // Step 2   : Reading the created textfile
lcFileContent = FILETOSTR( lcFileName )

* // Schritt 3: Feststellen der Häufigkeit des Auftretens von 'Lorem ipsum'
* // Step 3   : Checking the frequency of occurrence of 'Lorem ipsum'
CLEAR 
? [Lorem ipsum was found ]
?? OCCURS( [Lorem ipsum] , lcFileContent )
?? [ times within the filestream]

* // Schritt 4: Entfernen des zweiten Auftretens von 'Lorem ipsum'
* // Step 4   : Removing the second occurance of 'Lorem ipsum'
lcFileContent = STRTRAN( lcFileContent , [Lorem ipsum] , [] , 2 , 1 )

* // Schritt 5: Aktualisieren der Datei auf der Festplatte
* // Step 5   : Updating the file on harddisk
STRTOFILE( lcFileContent , lcFileName )

SET SAFETY &lcSafety

RELEASE lcFileName , lcString , lcFileContent , lcSafety

Freitag, 25. November 2011

Datumsspielereien (Teil 7) / Date gadgets (Part 7)

Im Folgenden eine kleine Abwandlung der Routine aus Teil 6 in der es um die Berechnung des letzten Sonntags eines Monats ging.
Wollen wir nicht den letzten Sonntag sondern einfach nur den letzten Tag des Monats berechnen, so ist es am einfachsten zunächst das Datum des 1. Tages des Folgemonats zu berechnen und davon einfach einen Tag abzuziehen. Ähnlich funktioniert auch die Methode aus Teil 6.
Hier nun der Mustercode:

CLEAR 

?GetLastDayOfMonth( DATE() )
?GetLastDayOfMonth( {12.02.2012} )

FUNCTION GetLastDayOfMonth as Date 
LPARAMETERS vDate as Date

    RETURN DATE( YEAR( GOMONTH( vDate , 1 ) ) , MONTH( GOMONTH( vDate , 1 ) ) , 1 ) - 1

ENDFUNC 

Dienstag, 8. November 2011

Arbeiten mit Ganzzahl und Kommazahlen / working with integers and decimals

Für die Arbeit mit Ganz- und Kommazahlen stellt uns Visual Foxpro ein paar Funktionen zur Verfügung, mit deren Hilfe wir Vorzeichen sowie Vor- und Nachkommastellen extrahieren können. Je nach Aufgabenstellung sind diese Funktionen recht hilfreich.

Im einzelnen dreht es sich um die folgenden Funktionen: INT(), ABS(), CEILING(), FLOOR() und MOD().

Der folgende Mustercode soll die Arbeitsweise der hier vorgestellten Funktionen veranschaulichen. Als Basis dient eine negative Kommazahl. Dies ermöglicht das Aufzeigen evtl auftretender Fallstricke. Natürlich nur für den Fall, das wir vergessen haben sollten, wie mit negativen Zahlenwerten umgegangen wird.... ;-)

An dieser Stelle noch ein Verweis auf einen älteren Blogeintrag zum Thema Modulo-Berechnungen, der dies hier ganz gut ergänzt:
http://tomsvfpblog.blogspot.com/2007/12/wieso-verhlt-sich-die-modulo-funktion.html

CLEAR 
lnNumeric = -123.456
?[Original                   :]
??lnNumeric

* Entfernen der Nachkommastellen / remove decimal places            
?[INT( lnNumeric )           :]
??INT( lnNumeric )

* Vorzeichenlose Ganzzahl / unsigned integer                        
?[ABS( lnNumeric )           :]
??ABS( lnNumeric )

* Nächst höhere Vorzeichen-Ganzzahl / next higher signed integer    
?[CEILING( lnNumeric )       :]
??CEILING( lnNumeric )

* Nächst niedrigere Vorzeichen-Ganzzahl / next lower signed integer    
?[FLOOR( lnNumeric )         :]
??FLOOR( lnNumeric )

* extrahieren der Nachkommastellen / extracting decimal places        
?[MOD( lnNumeric , 1 )       :]
??MOD( lnNumeric , 1 )

* Nächst höhere Ganzzahl / next higher integer                        
?[CEILING( ABS( lnNumeric ) ):]
??CEILING( ABS( lnNumeric ) )

* Nächst niedrigere Ganzzahl / next lower integer                    
?[FLOOR( ABS( lnNumeric ) )  :]
??FLOOR( ABS( lnNumeric ) )

* extrahieren der Nachkommastellen / extracting decimal places        
?[MOD( ABS( lnNumeric ) , 1 ):]
??MOD( ABS( lnNumeric ) , 1 )

Freitag, 28. Oktober 2011

Berechnen der NMEA Checksumme / Computing the NMEA checksum

Die National Marine Electronics Association (NMEA) zeichnet u.a. für den digitalen Datenaustausch bei GPS Geräten verantwortlich. Egal ob wir ein solches Gerät auf einem Schiff, im Auto, am Fahrrad oder beim Wandern benutzen. Die zum Einsatz kommenden Datensätze entspringen (mit kleinen individuellen Herstellervarianten) den NMEA Definitionen.

Da jedwede Art der Datenübertragung qualitativen Schwankungen des Übertragungsmediums unterliegt, beinhaltet die Definition der Datensätze eine Checksumme die zur Verifikation des korrekten Empfanges herangezogen wird.

Der u.a. Beispielcode zeigt, wie einfach die Überprüfung der Checksumme eines solchen Datensatzes in VFP realisiert werden kann.

INFO: Der als Basis dienende Datenbereich des jeweiligen Datensatzes beginnt grundsätzlich hinter dem $ Zeichen und endet am * dem sich dann die Checksumme des Datenbereichs anschliesst.

$GPXTE,A,A,0.67,L,N*6F

* // NMEA Checksumme berechnen
CLEAR 

* // Quelle der Musterstrings: http://www.gpsinformation.org/dale/nmea.htm
LOCAL laString( 1 , 19 )
laString(  1 ) = [$GPGGA,162045,5058.809,N,00647.103,E,1,03,2.5,63.1,M,47.2,M,,*79]
laString(  2 ) = [$GPGSA,A,3,04,05,,09,12,,,24,,,,,2.5,1.3,2.1*39]
laString(  3 ) = [$GPGSV,2,1,08,01,40,083,46,02,17,308,41,12,07,344,39,14,22,228,45*75]
laString(  4 ) = [$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A]
laString(  5 ) = [$GPGLL,4916.45,N,12311.12,W,225444,A,*1D]
laString(  6 ) = [$GPVTG,054.7,T,034.4,M,005.5,N,010.2,K*48]
laString(  7 ) = [$GPWPL,4807.038,N,01131.000,E,WPTNME*5C]
laString(  8 ) = [$GPAAM,A,A,0.10,N,WPTNME*32]
laString(  9 ) = [$GPAPB,A,A,0.10,R,N,V,V,011,M,DEST,011,M,011,M*3C]
laString( 10 ) = [$GPBOD,045.,T,023.,M,DEST,START*01]
laString( 11 ) = [$GPBWC,225444,4917.24,N,12309.57,W,051.9,T,031.6,M,001.3,N,004*29]
laString( 12 ) = [$GPRMB,A,0.66,L,003,004,4917.24,N,12309.57,W,001.3,052.5,000.5,V*20]
laString( 13 ) = [$GPRTE,2,1,c,0,W3IWI,DRIVWY,32CEDR,32-29,32BKLD,32-I95,32-US1,BW-32,BW-198*69]
laString( 14 ) = [$GPXTE,A,A,0.67,L,N*6F]
laString( 15 ) = [$HCHDG,101.1,,,7.1,W*3C]
laString( 16 ) = [$GPZDA,201530.00,04,07,2002,00,00*60]
laString( 17 ) = [$GPMSK,318.0,A,100,M,2*45]
laString( 18 ) = [$GPMSS,55,27,318.0,100,*66]
laString( 19 ) = [$PGRME,15.0,M,45.0,M,25.0,M*1C]
FOR liflag = 1 TO ALEN( laString )
    ? laString( liFlag )
    ? GetNMEACheckSum( laString( liFlag ) )
ENDFOR

FUNCTION GetNMEACheckSum as String
LPARAMETERS vNMEAString as String

    LOCAL lcString as String, liCheck as Integer, i as Integer

    IF    AT( [$] , vNMEAString ) > 0 ;
    AND    AT( [*] , vNMEAString ) > 0
        lcString    = STREXTRACT( vNMEAString , [$] , [*] , 1 )
    ELSE 
        lcString = vNMEAString
    ENDIF 
    STORE 0 TO liCheck , i

    FOR i = 1 TO LEN( lcString )

        liCheck = BITXOR( liCheck , ASC( SUBSTR( lcString , i , 1 ) ) )

    ENDFOR 

    RETURN RIGHT(TRANSFORM( liCheck , [@0] ) , 2 )
    
ENDFUNC 

Die Generierung der Prüfziffer beruht auf einer XOR Verknüpfung des aktuellen XOR Ergebnisses mit dem nächsten Byte des Datenbereichs. Die erste Verknüpfung erfolgt auf Basis des Wertes 0 mit dem 1. Byte.
Das Endergebnis wird ins Hexadezimalformat gewandelt wodurch eine Wertigkeit von 0-255 (00-FF) gewährleistet ist.

Was ist nun dieses 'XOR'?

XOR Vergleiche sind die einfachste Methode, auf Bits basierende Prüfungen zu realisieren. Mit Hilfe eines Prüfbits kann der Status eines als Grundlage herangezogenen Bits jederzeit korrigiert werden.
Die Idee dahinter ist, wenn zwei Werte (Bits) miteinander verglichen werden, dann sind sie entweder gleich (Prüfbit = 0) oder ungleich (Prüfbit = 1). Aufgrund dieser Logik kann nun eines der beiden Bits auf Basis des anderen sowie des Prüfbits wieder hergestellt werden.

Die entsprechende Funktion innerhalb von Visual Foxpro lautet BITXOR()

Die verwendeten Musterdatensätze stammen von hier:
http://www.gpsinformation.org/dale/nmea.htm

Montag, 17. Oktober 2011

Versionskontrolle kann Spaß machen / Version Control can be fun

Nein, das ist kein Witz, Versionskontrolle kann wirklich Spaß machen, zumindest wenn man weiß wofür sie gut ist und ... wenn man das neue Buch von Erik Sink gelesen hat ... :-)

Anmerkung:
Für diejenigen denen dieser Name nichts sagt sei ein Besuch der SourceGear Homepage angeraten.

Ich habe mir mittlerweile diverse Videos von Erik Sink im Web angesehen und ohne Zweifel hat er eine Menge Humor und weiß diesen auch geschickt bei seinen Präsentationen einzusetzen. Um so angenehmer war es, dass er dies auch in seinem Buch 'Version Control by Example' schafft.

Sein Buch kann sowohl bei den bekannten Onlinestores gekauft als auch kostenlos online gelesen werden. Besitzer von eBooks können es auch direkt im epub Format herunterladen.

Viel Spaß beim schmökern! :-)


Freitag, 7. Oktober 2011

Stringinvertierung - andersherum / inverting strings - the other way around

Beim durchstöbern meiner Codeschnipsel stolperte ich eben über eine andere Sichtweise der Stringinvertierung. Bei dieser geht es um einen einfachen Positionstausch innerhalb eines übergebenen Strings.

Mit anderen Worten: aus
 MeineInvertierteZeichenkette
wird
etteknehcieZetreitrevnIenieM
Das folgende Codemuster zeigt drei mögliche Wege auf, dies zu erreichen...

* // Funktionstest 
CLEAR 
liS=SECONDS()
?RevertString1( [etteknehcieZetreitrevnIenieM] )
?SECONDS() - liS

liS=SECONDS()
?RevertString2( [etteknehcieZetreitrevnIenieM] )
?SECONDS() - liS

liS=SECONDS()
?RevertString3( [etteknehcieZetreitrevnIenieM] )
?SECONDS() - liS

* Invertieren mit VFP Bordmitteln                        
FUNCTION RevertString1
LPARAMETERS vString AS String
    LOCAL liZaehler AS Integer, lcReturn AS String
    liZaehler    = 0
    lcReturn    = []
    FOR liZaehler = LEN ( vString ) TO 1 STEP -1
        lcReturn = lcReturn + SUBSTR ( vString , liZaehler , 1 )
    ENDFOR
    RETURN lcReturn
ENDFUNC

* Invertieren über die MS C++ Laufzeitbibliothek        
FUNCTION RevertString2
LPARAMETERS vString as String
    DECLARE STRING _strrev IN msvcrt20.dll STRING @
    m.vString = m.vString + CHR( 0 )
    RETURN _strrev( @m.vString )
ENDFUNC 

* Invertieren mit Bordmitteln und optimierter Schleife    
FUNCTION RevertString3
LPARAMETERS vString AS String
    LOCAL lcReturn as String
    lcReturn = SPACE( LEN( vString ) )
    FOR liZaehler = 1 TO LEN( vString ) / 2
        lcReturn = STUFF( lcReturn , liZaehler , 1 , SUBSTR( vString , LEN( vString ) + 1 - liZaehler , 1) )
        lcReturn = STUFF( lcReturn , LEN( vString ) + 1 - liZaehler , 1 , SUBSTR( vString , liZaehler , 1) )
    ENDFOR 
    RETURN lcReturn
ENDFUNC 

Bei kleinen Strings ist der zeitliche Unterschied marginal. Bei größeren Textmengen sieht es aber ganz anders aus und die Schere zu Variante 1 geht immer weiter auseinander.

Mittwoch, 5. Oktober 2011

Stringinvertierung - segmentiert und komplett / inverting strings - segmented and complete

Im Microsofts deutschem Foxpro Forum kam vor einigen Wochen die Frage wie ein String von A nach Z, B nach Y, C nach X usw. invertiert werden kann.

Im folgenden Codebeispiel steht wahlweise eine segmentierte Invertierung (nur für Buchstaben und Zahlen) sowie eine komplette Invertierung (sämtlich Zeichen der ASCII Tabelle = 0-255) zur Verfügung.

Vor der eigentlichen Funktion findet sich wie immer ein Funktionstest. Also einfach den Code markieren und über die Zwischenablage in ein PRG kopieren... ;-)


* // Stringinhalt unterscheidet sich von der Anzeige    
* // da nicht darstellbare Zeichen enthalten sein können
* // string content may differ from displayed value due 
* // to non-displayable chars                            

* // test - start --------------------------------
CLEAR 
lcTest = [Aa Bb Cc Dd Ee Ff Gg Hh Ii Jj Kk Ll Mm Nn Oo Pp Qq Rr Ss Tt Uu Vv Ww Xx Yy Zz]
* // Original
?lcTest
* // segmentiert / segemented
?InvertString( lcTest )
* // komplett / complete
?InvertString( lcTest , 1 )
?
lcTest = [0 1 2 3 4 5 6 7 8 9]
* // Original
?lcTest
* // segmentiert / segemented
?InvertString( lcTest )
* // komplett / complete
?InvertString( lcTest , 1 )
* // test - ende ---------------------------------



FUNCTION InvertString as String
LPARAMETERS vString as String, vVersion as Integer
    * // Param #1:    string to invert            
    * // Param #2:    0|1 (optional)                
    * //            0 or ommit = sequenced ascii
    * //             1 = complete ascii range    
    LOCAL i as Integer, liAsc as Integer, lcReplace as String    
    vVersion = EVL( vVersion , 0 )
    FOR i = 1 TO LEN( vString )
        liAsc = ASC( SUBSTR( vString , i , 1 ) )
        DO CASE 
        * // complete ASCII    
        CASE vVersion = 1
            lcReplace = CHR( BITNOT( liAsc ) + 256 )
        * // A - Z            
        CASE BETWEEN( liAsc , 65 ,  90 )
            lcReplace = CHR( ABS( 25 - ( liAsc - 65 ) ) + 65 )
        * // a - z            
        CASE BETWEEN( liAsc , 97 , 122 )
            lcReplace = CHR( ABS( 25 - ( liAsc - 97 ) ) + 97 )
        * // 0 - 9            
        CASE BETWEEN( liAsc , 48 , 57 )
            lcReplace = CHR( ABS(  9 - ( liAsc - 48 ) ) + 48 )
        * // anything else    
        OTHERWISE 
            lcReplace = SUBSTR( vString , i , 1 )
        ENDCASE 
        vString = STUFF( vString , i , 1 , lcReplace )
    ENDFOR
    RETURN vString
ENDFUNC 

Mittwoch, 28. September 2011

Byte invertieren - Viele Wege führen zum Ziel / invert a byte - There's more than one way to skin a cat

Wenn wir den Wert eines Bytes invertieren wollen/sollen, dann gibt es wie immer mehrere Vorgehensweisen, dies zu tun.

Wer keine Ahnung davon hat, wie Bits und Bytes ticken, der greift u.U. zur simpelsten (pfui) Lösung:

FUNCTION ByteInverter1
LPARAMETERS vValue as Integer
    RETURN 255 - vValue
ENDFUNC 


Wer zu denen gehört, die das duale/binäre Zahlensystem beherrschen werden evtl. dazu neigen, jedes Bit einzeln zu bearbeiten.
FUNCTION ByteInverter2
LPARAMETERS vValue as Integer
    LOCAL liReturn as Integer , liLoop as Integer
    FOR liLoop = 0 TO 7
        vValue    = IIF( ;
                        BITTEST(  vValue , liLoop ) , ;
                        BITCLEAR( vValue , liLoop ) , ;
                        BITSET(   vValue , liLoop ) ;
                     )
    ENDFOR 
    RETURN vValue
ENDFUNC 

Mit Hilfe der Funktionen BITTEST (zum auslesen des Bits), BITCLEAR (zum löschen des Bits) und BITSET (zum setzen des Bits), stellt dies auch kein wirkliches Problem dar. Natürlich vorausgesetzt wir denken daran, das Bits bei Position 0 beginnen... ;-)

Dumm ist nur, das die 'simpel'-Lösung um ein vielfaches kompakter ist... ;-)

Gott sei Dank gibt es eine VFP Funktion die das ebenfalls in einem 'professionelleren' Design als 1-Zeiler ermöglicht :-)

FUNCTION ByteInverter3
LPARAMETERS vValue as Integer
    RETURN BITNOT( vValue ) + 256
ENDFOR 

Wie gesagt: Es führen viele Wege zum Ziel...:-)