Der ganz normale IT-Wahnsinn

Zur Zeit geht auf der Arbeit IT-mäßig alles drunter und drüber. Nicht, dass letzte Woche eines morgens sogar einmal das komplette System still stand, erst nach langwieriger Datenprüfungen wieder lief und danach ziemlich viel Stress angesagt war. Nein, es kommen auch manchmal Fehler zutage, die ich nicht richtig nachvollziehen kann. Beispielsweise macht man ein Update von Office 2003 auf Office 2010 und übernimmt einfach mal einen IMAP Account. Natürlich möchte man nun auch seine gesendeten Mails im Gesendete Objekte Ordner nach dem Senden abgelegt haben. Also stellt man dies in den Kontoeinstellungen ein und sendet zum Test mal eine Mail. Was passiert? Nichts. Die Mail kommt zwar beim Empfänger an, aber wo ist die Kopie abgeblieben? Richtig, sie ist nicht mehr da...
Man ist ja nicht von gestern und löscht einfach mal das Konto, schließt Outlook, startet es wieder und richtet ein neues Konto ein.
Doch egal, was man einstellt und an welcher Schraube man dreht: Es ändert sich nichts an der Tatsache, dass keine Kopie einer versendeten Mail im Gesendeten Ordner abgelegt ist. Schließlich werden andere Clients, wie Thunderbird oder sogar auf einem anderen Rechner ausprobiert. Überall tut es und sogar mit Outlook 2010 auf einem anderen Rechner. *harhar*
Da der Rechner sowieso immer wieder einfriert, trotz neuer Hardware (i5 750 mit ordentlichem RAM und recht schneller Festplatte) wird eben ein neuer Rechner für den User eingerichtet. Sofort mal das IMAP Konto mit Outlook eingerichtet und getestet. Tut alles... ;) Solche Probleme nerven auf Dauer dann doch echt. Aber nun ist es ja gelöst, zumindest im Geschäft. Beim User daheim gibt es wahrscheinlich noch die gleichen Probleme. :)

Schon seit längerer Zeit beschweren sich die Nutzer, dass das Netzwerk manchmal extrem hängt. Man wundert sich schon seit Jahren darüber. Über die Jahre hinweg, hab ich einiges ausprobiert. Hintergrunddienste ausgemistet und zuletzt überall die Windowssearch aus dem Autostart ausgesteckt bzw. beim Server deaktiviert. Weiter dann noch alle Clients auf 100Mbit/s gedrosselt. Da wir dann einen neuen Mailserver bekommen haben, habe ich mal mal spaßeshalber die Geschwindigkeit des Server-RAIDs gemessen. *harhar* Von 130 Mbit/s Spitze mal abgesehen, war während des Tests die Geschwindigkeit meistens zwischen 1,2 Mbit/s und 15 Mbit/s. Und dann wundert sich noch einer, warum die User immer jammern, weil alles einfriert?!? :)
Die Ursache war dann aber recht schnell ausgedoktert. Schon seit jeher wurden aus Backup-Gründen die PST-Dateien von den Usern immer auf dem Netzlaufwerk ausgelagert. Wenn die User nicht immer E-Mails aufbewahren würden, wäre das auch nie ein großes Problem geworden. Nun ist es aber so, dass damals 10 Mitarbeiter mit Outlook gearbeitet haben und heute 20 Mitarbeiter am Schaffen sind und kaum einer seine E-Mails aussortiert. Kann man ja alles irgendwann noch gebrauchen! Mach ich privat ja auch so, da Googlemail ja ewig Speicher hat.
Doch dummerweise hat Microsoft die PST-Dateien nicht für Netzlaufwerke ausgelegt. Denn sobald eine neue Mail kommt, wird diese in die Datei geschrieben. Und sobald eine E-Mail gelöscht wird, bleibt diese aber weiterhin als Leiche in der PST-Datei. Außer man komprimiert die Datei hin und wieder mal. :)
Bei ein paar Clients, die gleichzeitig mit Outlook arbeiten kommen damit viele Lese und Schreibanforderungen auf den Server zu. Gleichzeitig läuft da drauf noch ein WaWi. Kein Wunder also, dass alles langsam ist. Der Server ist ansonsten nicht von schlechten Eltern. 2 XEON Prozessoren mit 2,8 GHz. 4 GB RAM mit ECC. Leider aber noch den langsamen mit 400 MHz oder sogar 333 MHz. Doch an und für sich reicht das noch locker aus, man muss nur mal die Mailgeschichte in den Griff bekommen.

Unsere Lösung ist nun IMAP. Damit ist zwar auch alles auf dem Server, aber jede E-Mail als einzelne Datei und vor allem auf einem anderen Server. Das hat natürlich auch für die iPhone User immense Vorteile. Man kann von unterwegs aus mal eben seine Mails checken oder auch schreiben. :)

So ziemlich parallel habe ich auch mal die ehrenwerte Aufgabe bekommen eine Lösung zur Synchronisation mit der COM-Schnittstelle zu programmieren. Über die COM-Schnittstelle habe ich mich ja schon Anfang des Jahres ausgekotzt und damals das durch fremde Hilfe und externe Programmaufrufe lösen können. Nun musste ich das aber selbst lösen. Durch ein paar Tipps in die richtige Richtung und mal genauerem Lesen der Dokumentation ist es mir dann doch leichter gefallen. Das war fast sogar schon leichter, als die ewige Fehlersuche bei dem Performanceproblem und dem blöden Outlook-Problem. Nebenbei bemerkt mag ich VB.NET nun richtig gerne. Klar sieht C# eleganter aus, doch wenn man mal keine ; schreiben muss, ist das doch auch toll. Performancetechnisch macht das heute ja eh kein Unterschied mehr. Mit den ganzen Sicherheitsroutinen und der großen Rechenleistung geht ja alles flott. Nur bei den COM-Aufrufen muss man sinnvoll programmieren, sonst dauert alles ewig. ;)

Genug gekotzt. Jetzt kommt endlich auch mal ein WLAN in unser Netz (natürlich Fremdnetz), damit unsere Besucher auch noch arbeiten können. Manche Kollegen freuen sich darüber auch. Denn WLAN ist doch um einiges schneller als das Handy-Internet.
Nebenbei habe ich endlich auch wieder Zeit gefunden um meine persönlichen Dokumentationen für die Aufgaben, die wiederkehren (Rechner installieren, Netzwerkkonfig, Excelformeln) zu pflegen. Irgendwie war das Ganze also auch sinnvoll. :) Meistens macht man sich ja nicht die Mühe die Doku zu pflegen und in ein paar Wochen, Monaten oder gar Jahren kommt dann große Erwachen und die ewige Sucherei: Wie ging das nochmal?

Kommentare im Quellcode

Gehörst du auch zu den Leuten, die meinen, dass man auch ohne Kommentare im Quellcode gut programmieren kann? Eigentlich hast du damit recht. Das Compilieren geht vielleicht auch ein paar Sekunden schneller, wenn er die Kommentare nicht mitlesen muss.

Doch es ist sehr wichtig, wenn man gewisse Stellen einfach auskommentiert. Mir geht es zum Beispiel so, dass ich jetzt einen Code, denn ich vor 3 Monaten mal reingehackt habe kaum mehr lesen kann. Das macht die Sache nicht besonders einfach.

Also fangt an und nehmt euch die Zeit und kommentiert auch mal Sachen aus. In den üblichen Sprachen wie PHP, C, C#, VB und Co. wird einfach ein // genommen und schon ist die Zeile auskommentiert.

Oder eben mal /* */ um bestimmte Funktionen auszuschalten.

Damit wird vielleicht der Code etwas länger, aber es hilft ungemein und erleichtert die Lesbarkeit des Codes auch später noch.

Wollte ich nur mal so anmerken, da ich gerade wieder alten Code von jemand anderem durchschaue um die COM-Programmierung vielleicht besser zu kapieren. Gar nicht so einfach. :)

Regexp in VB - ein kleines How To

Für mein letztes Projekt habe ich auch immer wieder Reguläre Ausdrücke gebraucht, da sonst das Auslesen bestimmter Werte unmöglich gewesen wäre.
Wer wissen will, was Reguläre Ausdrücke sind findet im Wikipedia eine gute Anlaufstelle.
Da es leider auch sehr viele unterschiedliche Implementierungen gibt, gehe ich mal auf die VB.NET Variante ein. Diese dürfte auch in allen anderen .NET Sprachen funktionieren, aber garantieren tue ich es nicht.

Übrigens eine gute Einstiegshilfe für Reguläre Ausdrücke gibts bei txt2re.com

Stell dir vor, du hast ungefähr immer gleiche bzw. ziemlich ähnlich aufgebaute Textzeilen, die sich wiederholen.

000010 02.123.7421.0       BUCHSENKONTAKT   4                                                QMM
       Ihre Artikelnummer: 21076
                      200 ST          20,83 EUR                                     /   100      ST                               41,66
       Sonderrabatt 1                 60,00-%                                                                                     25,00-
       Positionsnetto                  8,33 EUR                                     /   100      ST                               16,66
        ST
       Liefertermin (Tag): 25.02.2010

Nun sollst du diese Nummer heraus extrahieren: 02.123.7421.0
Der Aufbau der Ziffern ist immer gleich. Nur vorne kann auch mal ein Buchstabe vorkommen: also Z7.123.1234.0

Der reine Reguläre Ausdrück wäre wie folgt:

((\w{1}\d{1}|[0-9]{2})\.[0-9]{3}\.[0-9]{4}\.[0-9]{1})

1. Ziffernbereiche und Buchstabenbereiche mit Mengenangaben
Trennen wir also mal den Ausdruck auf: "(\w{1}\d{1}|[0-9]{2})\." (natürlich ohne die Gänsefüsschen " gesehen) ist eine Oder Bedingung. Zuerst wird gesucht, ob ein Buchstabe und eine Zahl nacheinander stehen ("\w{1}\d{1}" ) oder eben zwei Ziffern ("[0-9]{2}" ) und davon zwei Stück nacheinander stehen. Der obligatorische Punkt wird durch die Kombination \. dargestellt! Ein einzelner . würde in diesem Fall für ein beliebiges Zeichen stehen und das wäre ungeschickt. Um also nur den . zu finden, muss dieser noch maskiert werden durch ein Backslash \ gefolgt von dem . also eben \.
Eine vollständige Erklärung für solche Metazeichen gibts hier

Die anderen Zeichen wie "[0-9]{3}\." sind dann sicher recht einfach. In den eckigen Klammern [] steht der Ziffernbereich (es könnte auch [025] stehen, dann wären nur die Ziffern 0, 2 und 5 erlaubt) und in den geschweiften Klammern die Anzahl der Ziffern. Zu guter Letzt muss die Ziffernfolge wieder durch ein . beendet werden, bis die nächste Ziffernfolge folgt.

Diese Zeichenkette findet man sicher noch relativ leicht, da sie ziemlich eindeutig ist.

2. Ziffernbereiche (bzw. jede beliebige Bereichsgruppe) und min und max Angaben
Du willst aus dem oben gezeigten Text die Artikelnummer herausextrahieren.
Leider kann diese Artikel aus allen beliebigen Ziffern bestehen und entweder 5 oder 6 Ziffern haben.

Doch eine Regelmäßigkeit gab es: "Ihre Artikelnummer: 21076" Es steht immer der gleiche Text vor der Artikelnummer, nämlich: Ihre Artikelnummer
Warum also nicht ein Regexp schreiben und der die gesamte Zeichenkette (also Text plus Nummer) beinhaltet. Gesagt getan, hier also der Reguläre Ausdruck:

Dim re1 As String = ".*?"           'Non-greedy match on filler
Dim re4 As String = "(Ihre Artikelnummer: [0-9]{5,})"    'Word 3
Dim r2 As Regex = New Regex(re1 + re4, RegexOptions.IgnoreCase Or RegexOptions.Singleline)

So sieht also auch mal eine Beispielsyntax für die Verwendung der Regexp in VB aus. Für die Bedeutungen von IgnoreCase und Singleline gibts hier von Microsoft eine Erläuterung.
IgnoreCase beachtet also die Groß und Kleinschreibung, wenn das Muster gefunden wurde und Singleline gibt dem . die Bedeutung "alle Zeichen".

Der obige Ausdruck sucht nach dem Text "Ihre Artikelnummer: " und einer Nummer mit 5 Ziffern bzw. mehr Ziffern "[0-9]{5,}" Man kann in den geschweiften Klammern nämlich eine Mindestanzahl und eine Maximalanzahl angeben. Die Mindestanzahl steht vor dem Komma und die Maximalanzahl nach dem Komma.

Um also den regulären Ausdruck auch auszuwerten benötigen wir noch weiteren Code.

Dim m2 As Match = r2.Match(txt)     'txt ist eine ausgelesene Zeile
                If (m2.Success) Then                'h-team Nummer
                    Dim word3 As Group = m2.Groups(1)
                    'erste zwanzig Zeichen sprich "Ihre Artikelnummer: " abschneiden
                    Dim hteam As String = word3.ToString.Remove(0, 20)
                    exWS.Range("D" & zählerb - 1).Value = hteam 'Damit wird der Wert in Excel geschrieben. Kann aber auch jeder beliebe andere Befehl verwendet werden.
                End If

Das ist soweit also auch noch klar.

3. Bestimmter Preis in einer bestimmten Zahl finden
Doch nun wollen wir den Preis in der Positionszeile erwischen, das war damals für mich eine ernsthafte Herausforderung und nur dank diesem Forumseintrag habe ich endlich eine Lösung gefunden.

Und er hat es auch ziemlich gut beschrieben, deshalb möchte ich es gerne hier zitieren:

So geht's:

Zunächst testen ob in der Zeile steht: Das Wort  Positionsnetto gefolgt von     einem oder mehreren Zeichen bis die Zeichenfolge      ST kommt und auf diese wieder      ein oder mehrere Zwischenraumzeichen folgen.

Dann muß man noch berücksichtigen, daß der gesuchte Betrag mehrere Tausenderpunkte haben kann , z.B.     12.345.678,90  und immer genau 2 Dezimalstellen hat.

Das Suchmuster muß also so aussehen:

        Dim re7 As String = "(?<=Positionsnetto.+ ST\s+)([0-9]{1,3}.)*[0-9]{1,3},[0-9]{2}"   'word 5

Na alles klar? Zuerst wird also nach der Zeile gesucht, in der "Positionsnetto" vorkommt, dann dürfen ein oder mehere beliebige Zeichen vorkommen ".+" , dann folgt ST, dann wieder ein oder mehrere Zwischenraumzeichen "\s+" und dann kommen die Zahlen. Das ist oben schon ziemlich gut erklärt.

4. Als nächstes war noch die PE herauszufinden, dass ist eigentlich relativ leicht und ist im Prinzip nur eine abgewandelte Regexp von oben. Da ich es auch im Forum schon geschrieben habe, hier die Erklärung dazu:

Erstmal wieder danke, durch deine Erklärungen hat sich mir  dann auch das Suchmuster für die PE (1 oder 100) erschlossen.

Hier das Suchmuster, dass ich zuerst ausprobiert habe.

Dim re8 As String = "(?<=Positionsnetto.+ EUR\s+/\s+) [0-9]{1,3}" 'word 7

Das hat aber dann nicht genau hingehauen, da damit zwar

Positionsnetto                  8,33 EUR                                     /   100      ST                               16,66

und

Positionsnetto                 71,66 EUR /                                   100    ST                              71,66

abgedeckt waren.

Aber solche Zeilen wie diese hier:

Positionsnetto                  41,09 EUR / 100 ST                                                            82,18

wurden verworfen.

Also habe ich nun folgendes Suchmuster angewendet:

Dim re8 As String = "(?<=Positionsnetto.+ EUR.+) [0-9]{1,3}" 'word 7

Sprich in der Zeile muss das Wort Positionsnetto gefolgt von einem oder mehreren Zeichen bis die Zeichenfolge EUR kommt und auf diese wieder  ein oder mehreren Zeichen folgen und dann eine Zahl mit 1 oder 3 Ziffern (also entweder 1 oder 100).

Nur so als Hilfestellung, falls jemand mal ähnliche Probleme hat.

5. Und zu guter letzt wäre natürlich auch noch der Liefertermin interessant.

Dieser ist eigentlich ziemlich logisch aufgebaut. "Liefertermin (Tag): 25.02.2010"
Doch leider kommt es auch vor, dass "Liefertermin unbestätigt" drin steht.
Aber auch hier gab es wieder Hilfe von diesem Peter_Punkt:

So geht's:

Bei dieser Aufgabenstellung treten gleich mehrere Probleme auf:

Umlaute im Suchmuster (z.B. ä) sind nicht erlaubt.

Klammer () haben eine besondere Bedeutung. Wenn man sie als normale Zeichen behandelt wissen will, dann muß man \ voranstellen.

Außerdem muß man die erzielten Treffer nachbehandeln und Teile (z.B. "(Tag): ") entfernen.

Code-Beispiel:

Dim re6 As String = "(?<=Liefertermin )(\(Tag\): \d{2}\.\d{2}\.\d{4})|(unbest){1}"
            Dim r2 As Regex = New Regex(re6, RegexOptions.IgnoreCase Or RegexOptions.Singleline)
            Dim m2 As Match = r2.Match(txt)     ' In txt ist der zu untersuchende String

            If (m2.Success) Then
                Dim word3 As Group = m2.Groups(0)
                Dim treffer As String = word3.ToString
                If treffer = "unbest" Then      ' bis zum ä
                    treffer = "unbestätigt"
                Else
                    treffer = treffer.Replace("(Tag): ", "") ' den Vorspann vor dem Datum entfernen
                End If
                Debug.WriteLine(treffer)
            End If

Man sieht also: Reguläre Ausdrücke sind eine tolle Sache, da man sich damit viel Arbeit ersparen kann. Doch ohne Hilfe in konkreten Problemstellungen ist man ganz schön aufgeschmissen.
Ich hoffe, ich konnte dem einen oder anderen vielleicht eine kleine Hilfestellung sein.
Falls Verbesserungsvorschläge vorliegen. Einfach Kommentar posten und ich werde es korrigieren.

Stundenlöhne in der freien Wirtschaft

Es ist ja allgemein bekannt, dass Stundensätze für Kunden immer teuer sind. Schließlich ist nicht nur der Arbeiter, sondern auch Nebenkosten miteinkalkuliert.
In der Autowerkstatt sind 60 bis 100 je nach Auto üblich. Was leider oft schmerzlich auf den Geldbeutel drückt, aber trotzdem hat man meistens hinterher Qualität.
Bei Computerhändlern sind locker auch mal so ca. 50 Euro (kann mal mehr, mal weniger sein) pro Stunde eingerechnet. Wobei ich das manchmal nur unverschämt finde. Hallo, das bisschen Kopfarbeit und Googlen und Einrichten. Na gut, es ist ja nicht immer alles so einfach. Vor allem deshalb, weil man nicht physisch an einem "Rädchen" drehen kann und dann ausgehen kann, dass alles läuft. Dazu kommen dann unendliche Tests.

//Nachtrag: Klar ist ja trotzdem: Der Mitarbeiter, der die Zeit am Rechner ist, kann derweil nichts anderes machen. Daher sind die 50 Euro schon ok! Wollte ich doch noch klarstellen.

Als ich gerade das Angebot für eine Individualprogrammierung gelesen habe, blieb mein Herz beinahe stehen.
160 Euro (netto natürlich) pro Programmierstunde. Wenn du dann noch eine etwas aufwendigere Anwendung haben möchtest, biste bald im unteren 5 stelligen Bereich.
Wie kann denn das bitte zustande kommen, dass sich Programmierer so einen Stundenlohn erlauben können?
Klar ein ITler ist schon höher gestellt als zum Beispiel ein Elektriker (ca. 40 Euro pro Stunde würde ich mal schätzen vom Hören-Sagen) und natürlich sind die Entwicklungsumgebungen auch nicht gerade billig. Rechner kostet gleich mal 1.000 Euro, dann eine IDE kostet locker mal 3.000 bis 5.000 Euro. Aber hey, das relativiert sich doch wieder auf zwei Jahre gesehen. Das sind im Monat dann auch bloß 250 Euro (6.000 / 24).
Wie kommt man also auf die Idee solche hohen Stundensätze an den Tag zu legen?

//Nachtrag: 80 - 100 Euro pro Stunde zu verlangen finde ich fair, da die Arbeit schon etwas anspruchsvoller ist (die Programmierer werden es mir bestätigen) und vor allem auch zeitaufwendig. Denn nach Fertigstellung, kommt noch der Funktionstest und Bugfixing dran.

Im Endeffekt war die Pauschale dann nur ein Drittel des berechneten Preises (soll ja auch wirtschaftlich sein). Aber trotzdem. Erstmal die Leute schocken und dann nachgeben.

Was ist eure Meinung und eure Erfahrungen dazu.