Ruby wird 20

Ruby wird 20 Jahre alt:
http://ruby20th.herokuapp.com/

Share Button

Fließkommazahlen als Ersatz für Ganzzahlen

Wie ärgerlich ist eigentlich die Tatsache, dass Lua und JavaScript keine „int“s kennen und man stattdessen Fließkommazahlen („double“) verwenden muss?

Natürlich fühlt es sich völlig falsch an und ich möchte die Entscheidung, nur diese 8-byte-Fließkomma-Zahlen als numerischen Typ zu unterstützen, nicht gutheißen.

Aber was bedeutet das nun genau? Bekommen wir nun plötzlich irgendwo 7.999999999 statt 8?

Die Fließkommazahlen sind heute zum Glück standardisiert und fast jede Implementierung hält sich einigermaßen an diesen Standard. Es gibt eine 4-Byte-Variante und eine 8-Byte-Variante. Die 4-Byte-Variante ist nur dann sinnvoll, wenn man dem Entwickler die Wahl lässt, und fast jeder Entwickler nimmt „zur Sicherheit“ lieber die 8-Byte-Variante. Diese Fließkommazahl sieht nun ungefähr so aus:
1 Bit ist das Vorzeichen
11 Bits drücken den 2er-Exponenten aus
52 Bits drücken die Mantisse aus, dazu kommt als 53stes eine führende 1, die man nicht speichern muss, weil sie ja immer da ist.
Damit kann man die ganzen Zahlen von -2^{53} bis 2^{53} exakt ausdrücken und hat damit etwas, was etwa 54-Bit-Ganzzahlen entsprechen würde. Rein informationsmäßig ist das eine zusätzliche Bit im Vorzeichen und das andere im 2-er-Exponenten codiert. Wenn man also nur addiert, subtrahiert und multipliziert und dabei immer im Bereich von -2^{53} bis 2^{53} bleibt, kann man exakte Ergebnisse erwarten. Natürlich ist es langsamer, aber auch das ist bei heutigen CPUs, die schnelle Fließkommaoperationen seit etwa 20 Jahren standardmäßig (und vorher gegen Aufpreis) in der Hardware eingebaut haben, nicht so tragisch. Wie ist es mit den ungenutzten Bits? Normalerweise stören die nicht, aber wenn man Applikationen entwickelt, die sehr speicherintensiv sind, kann auch so etwa ausnahmsweise mal relevant werden.

Ärgerlicher ist da schon die Division. Hat man bei ints so etwas wie „/“ für die Division mit Abrundung und „%“ für den Rest, wird bei Fließkommazahlen mit „/“ wirklich dividiert und eine neue Fließkommazahl berechnet. Wie rundet man hier ab, um das „int-/“ und das „int-%“ zu bekommen? Es sollte dabei ja trotzdem noch das 6.9999999999999 zu 7 gerundet werden.

Ungefähr so etwas könnte funktionieren:

def divmodf(x, y)
xf = x.to_f
yf = y.to_f
df = if (yf < 0) then
-0.2
else
0.2
end
qf = (xf + df) / yf;
q = qf.floor
r = (yf*(qf-q)).round(0)
return q,r
end

Das ist jetzt in Ruby geschrieben, aber es würde natürlich ähnlich in Lua oder JavaScript funktionieren.

Share Button

Wie könnte ein C-Nachfolger aussehen?

Man verwendet heute C kaum noch für Applikationsentwicklung, aber wenn wir uns anschauen, welche Software wir täglich dauernd verwenden, ist ein großer Teil davon in C oder eventuell in C++ geschrieben, z.B. gängige Betriebssysteme (Linux, MS-Windows,…), Web-Browser, die meisten Datenbanken, die meisten Editoren, die meisten Office-Programme, die meisten Fenstersysteme, viele Bildverarbeitungsprogramme, u.s.w.

Was könnten die Gründe sein, daß man für diese Zwecke C verwendet? Vielleicht die relative Nähe an der Assemblersprache kombiniert mit der richtigen Abstraktion und einigen nützlichen Library-Funktionen? Vielleicht der direkte Zugriff auf Betriebssystem-APIs? Das einigermaßen gut vorhersehbare Zeitverhalten? Oder einfach die vorhandene Codebasis? Und C-Programme sind schnell. Aber nur wenn man genügend Zeit zum Entwickeln hat. Die C-Syntax hat sich zum de-facto-Standard für viele Programmiersprachen entwickelt.

Diese Vorteile will man natürlich beibehalten. Daher scheiden als direkte Nachfolger Sprachen mit einer hohen Abstrahierung, einem kommplizierten Laufzeitsystem mit Garbage Collection u.s.w. aus. Natürlich wurde C in der Applikationsentwicklung durch Java, Scala, C#, Ruby, Perl, PHP, Python, JavaScript u.s.w. teilweise verdrängt. Gerade deswegen braucht man die Features dieser Sprachen nicht einfach in C zu übernehmen, denn wenn man das will, kann man diese Sprachen statt C verwenden.

Vielleicht will also niemand einen C-Nachfolger? Kann sein, aber es gibt doch ein paar Dinge, die man besser machen könnten und die vielleicht einen Nachfolger, aber sicher nicht einen ganzen Zoo von Nachfolgern rechtfertigen würden:

  • Bessere arithmetische Operationen auf den primitiven Typen: Durchreichung von nützlichen Assembler-Features wie Addtion mit Carry, Multiplikation mit einem Ergebnis von der doppelten Länge der Faktoren.
  • Echte Zeichenketten mit einfach handhabbarer nativer Unterstützung für verschiedene Zeichensatzcodierungen (UTF-8, ISO-8859-*, ISO-646-irv, UTF-16,…)
  • Bessere Unterstützung für Thread-Safety.
  • Vielleicht eine bessere Fehlerbehandlung
  • So etwas wie ein Schlüsselwort „unsafe“, bei dessen Benutzung C-übliche Dinge wie Casts von Zahlen auf Pointer, indizieren von Arrays außerhalb von deren Bereich u.s.w. möglich sind. Ohne das Schlüsselwort werden diese Operationen nicht unterstützt oder führen zu Laufzeitfehlern.
  • Mehrfache Rückgabewerte bei Funktionsaufrufen

Einen C-Nachfolger, der nicht Link-Compatibilität zu C bietet, könnte man natürlich vergessen. Man möchte ja auf jeden Fall die bestehende Code-Basis und die Libraries weiterhin benutzen können.

Wird es so etwas geben? Ich weiß es nicht, es sieht natürlich im Moment nicht danach aus und man behilft sich damit, Dinge, die in C nicht gut gehen, stattdessen in anderen Sprachen zu entwickeln. Wir sehen es ja an dem ähnlichen Beispiel Ceylon, das einen Java-ähnlichen Java-Ersatz bieten soll und dabei einige unglaublich ärgerliche Design-Fehler von Java vermeidet. Wenn Ceylon einmal ausgereift ist, ist es wahrscheinlich absurd, noch Dinge in Java zu entwickeln, aber wenn man schon wechselt, kann man auch zu moderneren Sprachen wie
Ruby, Scala oder Clojure wechseln. So halten sich ärgerliche Designfehler in Programmiersprachen länger als eigentlich nötig.

Es hilft viel, für die Aufgabe das passende Werkzeug zu verwenden, aber es ist sicher gut, auch einmal darüber nachzudenken, wie man einige dieser Werkzeuge verbessern könnte.

Share Button

Verknüpfung Social Media anderen mit Webseiten

Wir finden oft auf beliebigen Webseiten Facebook-„Like“-Buttons oder „+1“-Buttons oder „Twitter“-Buttons. Ich habe selber welche hier drauf.

Es gibt aber auch Seiten, die anbieten „login mit Facebook“ oder „login mit Google+“ oder „login mit Twitter“.

Was bedeuten diese Verknüpfungen?

Es sind, wie man unschwer erkennen kann, zwei verschiedene Mechanismen. Grundsätzlich basieren sie darauf, daß viele von uns an einem oder mehreren dieser Social-Media-Systeme teilnehmen. Dazu muß man dort einen Account erwerben, eine Mailadresse und eventuell eine Mobiltelefonnummer angeben, ein Password und ein Captcha abtippen und dann ist man nach der Bestätigung einer Testmail dabei. Nun gibt es Leute, die sich nach Gebrauch bei diesen Social-Media-Seiten abmelden und sogar die Cookies aufräumen oder im „private-Modus“, den Firefox anbietet, auf diese Seiten gehen. Das ärgerliche daran ist, daß man mehr und mehr dazu übergehen muß, wirklich gute Passwörter zu verwenden und auch noch verschiedene für jede dieser seiten. Und man darf sie nicht aufschreiben. Das ganze soll dann noch auf diversen Computern, Mobiltelefonen und anderen Geräten funktionieren. Und google bietet schon einen sichereren Login-Mechanismus wie bei E-Banking an, wo man zusätzlich noch einen SMS-Code eingeben muß. Schlimm, wenn man sein Leben in diese Social-Media-Systeme verlegt hat und sein Mobiltelefon verlegt hat. Aber sowas kann uns in der heutigen Zeit ja nicht passieren… Eher vergißt man seine Füße oder seinen Kopf oder so etwas zuhause, wenn man aus dem Haus geht.

Die Erfahrung zeigt, daß die meisten Computerbenutzer immer bei allen Social-Media-Netzen eingeloggt sind. Man kann also Facebook, Google+ und Twitter starten und einfach losschreiben. Wie sicher ist das? Nun, gemäß N. Kaspersky: „A security guy who says that something is absolutely secure is not a real security guy“. Ob ich nun ein Security-Guy bin oder nicht, es ist also nicht absolut sicher, denn wenn der Rechner, an dem wir sitzen, Teil eines Bot-Netzes ist, hat der Bot-Netz-Betreiber Zugriff auf alles, was auf dem Rechner läuft, also auch auf Cookies, Passwörter, Tastatureingaben u.s.w. Je nach Features der Bot-Netz-Software. Und es sind so viele Rechner in der Welt mit Schadsoftware „infiziert“, daß diese Botnetze groß sind. Man kann sie ja von entsprechenden Geheimfirmen im zehntausender-Pack mieten, habe ich gehört.

Aber gut, wenn unser Rechner sauber ist und wenn wir nicht glauben, daß der Hersteller eines propritären Betriebssytems Features eingebaut hat und nutzt, um sich auf unserem Rechner umzusehen, dann kommen wir zur Serverseite. Jede von diesen Seiten, auf der wir uns einloggen können, muß Passwörter speichern, außer sie setzen ganz auf dieses „login in via…“-Zeug. Wenn wir überall verschiedene Passwörter verwenden, ist das nicht so tragisch, kann aber schnell *sehr* ärgerlich werden, wenn jemand Kommentare oder EMails oder Tweets unter unserem Namen schickt. Vor allem, wenn man das nicht so schnell merkt. Aber man kann auch Server, die im Internet stehen, vernünftig absichern und das Risiko von Einbrüchen über das Netz verringern. Wie sieht es mit der Netzwerkverbindung aus?

Auf Seiten, die ein Passwort erfordern, sollten wir immer https verwenden. Das hat den Nachteil, daß die Proxys nichts mehr cachen können, weil für dieselbe Seite durch die verwendete Verschlüsselung für jeden Empfänger andere Daten übertragen werden. Die grobe Idee ist etwa die folgende: Der https-Server hat ein Zertifikat, das im Wesentlichen aus einem privaten Schlüssel besteht, der nur auf dem Server bekannt sein darf, und aus einem öffentlichen Schlüssel, der in mit dem Domainnamen und weiteren Metainformationen verknüpft ist und wiederum mit dem privaten Schlüssel einer Zertifizierungsstelle signiert ist. Die Browser kennen nun die Public-Keys aller gängigen Zertifizierungsstellen und können so diese Kombination abprüfen. Wenn also die Zertifizierungsstelle sauber arbeitet, vergibt sie das Zertifikat nur an denjenigen, der die entsprechende Domain hat. Nun kann unser Browser erkennen, ob wir wirklich mit der Domain kommunizieren, zu der das Zertifikat gehört. Wenn nicht, erscheinen komische Fehlerboxen, die man nicht einfach wegklicken sollte. So wird schon verhindert (oder sehr erschwert), daß wir über gefälschtes DNS, gefälschtes Routing oder gefälschte Proxys auf die falsche Seite gelangen. Denn die falsche Seite kommt nicht an das richtige Zeritifikat heran, bzw. kann es nicht einfach aus dem öffentlichen Teil rekonstruieren, weil der private Schlüssel fehlt.

Die gesamte Kommunikation findet verschlüsselt statt und man kann dabei eine Verschlüsselung haben, die wahrscheinlich nach heutigen Erkenntnissen sichtbar ist. Vielleicht kann jemand den https-Traffic aufzeichnen und in 20 Jahren entschlüsseln, aber dafür muß es schon sehr interessant sein. Wichtig ist, daß das Passwort nicht im Klartext über das Netz geht. Nun bekommt der Browser einen Cookie, also ein paar Bytes. Jedesmal wenn wir auf dieselbe Seite gehen, wird dieser Cookie wieder an den Server geschickt und daran kann dieser erkennen, daß wir noch eingeloggt sind. Wenn nur das login via https erfolgt, der Rest der Kommunikation aber über http, kann unterwegs jemand den Cookie abgreifen und unsere Session übernehmen. Daher ist es sinnvoll, die Seiten mit login komplett via https anzusprechen.

Wenn nun jemand, der in Facebook noch eingeloggt ist, auf eine Seite mit like-Buttons kommt, dann wird mit dem Laden dieser Seite auch ein bißchen Code von Facebook geladen. Dabei wird der Cookie an Facebook geschickt und die wissen dadurch etwas darüber, welche Webseiten uns interessieren. Mit dem Like-Button wird nun etwas bequemer dasselbe nachgebildet wie wenn wir die URL in ein Facebook-Fenster Kopieren und man kann je nach Social-Media-System noch etwas dazu schreiben.

Dieser Login-via-Google-Knopf dient zwei Zwecken. Er soll uns Anwender sparen, noch ein Passwort mehr zu lernen und er soll dem Serverbetreiber sparen, die Passwörter sicher zu handhaben. Aber auch hier wird natürlich google (oder twitter, Facebook o.ä.) über den Besuch der Seite jeweils informiert. Damit das funktioniert, müssen wir als Benutzer in unserem google-Account die Seite, auf der wir uns einloggen, autorisieren, unsere Identitätsinformation zu beziehen, wenn wir dort eingeloggt sind. Das ist, wenn man es sich überlegt, nichts anderes als ein Login. Aufpassen muß man aber, daß wir diesen Seiten nicht zu viele Rechte auf unserem Account einräumen, sonst schicken die Spam-Nachrichten oder so etwas in unserem Namen.

Share Button

Drittanwendungen in Twitter, Facebook, Google+ & Co.

Diese Social-Media-Systeme erlauben es, daß man Drittanwendungen Zugriff auf seinen Account gibt. Es gibt durchaus legitime Gründe, aber man sollte schauen, ob man der Drittanwendung und deren Security-Team traut und eventuell die Zugriffe abstellen oder reduzieren. So geht es:

Twitter mit Web-Client (geht auch auf dem Mobiltelefon mit twitter.com):

  • Oben rechts das Zahnrad anklicken
  • In dem Zahnrad-Menü auf „Einstellungen“
  • Links auf „Apps“
  • Die Liste der Apps durchschauen und die unerwünschten Zugriffsrechte sperren

Facebook mit Web-Client (geht auch auf dem Mobiltelefon mit facebook.com):

  • Auch oben rechts das Zahnrad anklicken, ist nur kleiner als bei Twitter
  • In dem Zahnrad-Menü auf „Konto-Einstellungen“
  • Links auf Anwendungen
  • Unerwünschte Anwendungen weg-x-en
  • Legitime Anwendungen mit „Bearbeiten“ überprüfen

Google+ mit Web-Clienten (Gugel+):

  • Auch oben rechts das Zahnrad anklicken, ist wieder größer als bei Facebook. Haben die drei sich abgesprochen??
  • In dem Zahnrad-Menü auf „Einstellungen“
  • Sicherheit
  • Verbundene Anwendungen und Websites: „Zugriff verwalten“
  • Verbundene Websites, Apps und Dienste
  • Bei unerwünschten Anwendungen die Zugriffsrechte widerrufen

Nun gibt es Seiten, die ein „Login mit Facebook“, „Login mit Google(+)“, „Login mit Twitter“ o.ä. erlauben. Dieser Mechanismus ist besser als überall dasselbe Passwort zu verwenden, ob man ihn will oder nicht ist eine längere Diskussion. Aber alle Seiten, auf denen man sich mit Twitter, Google oder Facebook anmeldet, tauchen hier auf und wenn man die weglöscht, löscht man sein Login auf diesen Seiten. Aber warum eine Seite, auf die ich mich mit Twitter anmelden kann, deshalb das Recht bekommen soll, in meinem Namen Tweets oder Facebook-Kommentare oder Google+-Beiträge zu schreiben, ist eine andere Frage.

Und noch was: Seiten, auf denen man sich einloggt, bitte immer mit https und nicht http ansprechen! Wenn die Seite kein http kann, dann ist das Passwort dieser Seite quasi öffentlich, sollte also auf keinem Fall mit einem Passwort auf einer https-Seite übereinstimmen. E-Banking ist immer mit https. Google(+), Facebook, Xing, Wikipedia und viele andere Seiten können mit https angesprochen werden oder schalten automatisch darauf um.

Ich versuche bis zum kommenden Freitag einen Blog-Artikel zu schreiben, in dem ich genauer auf die Funktionsweise dieser Mechanismen eingehe.

Share Button

Cyber-Attacke gegen Twitter

tagesanzeiger.ch

Share Button

Micro-Komponenten-Antipattern

Eine größere Software muss man sicher strukturieren, sonst baut man sich ein Monster.

Nun kann man versuchen, Komponenten zu definieren, die so klein wie nur möglich sind. Die Komplexität innerhalb der Komponenten wird dadurch reduziert und überschaubar. Aber man bekommt ein Problem, weil die Menge der Komponenten dabei zu groß wird und man eine sehr hohe Komplexität bekommt, diese vielen Komponenten miteinander zu verknüpfen.

Man sieht es bei Büchern, bei denen die Gliederung gut gelungen ist, dass es eine Hierarchie von Gliederungen gibt und in jeder Ebene findet man etwa 2-10 Untereinträge zu den Einträgen aus der nächst höheren Ebene, nicht aber 50 Kapitel, die nicht in irgendeiner Form gruppiert sind.

Solche Ebenen wie „Teil“, „Kapitel“, „Abschnitt“… hat man in der Software-Architektur auch zur Verfügung, wobei es von der Technologie abhängt, welche Hierarchieebenen für dieses Gliederung und Strukturierung zur Verfügung stehen, z.B. Methode – Klasse – Package – Library – Applikation und man sollte sie mit Bedacht einsetzen. Was gehört zusammen und was nicht?

Es gibt aber noch einen anderen Aspekt. Oft verbaut man sich durch zu feingranulare Aufteilung Wege.

Ein Beispiel: Es soll eine Multiplikation von Matrizen mit kmoplexen Zahlen als Elementen implementiert werden. Nun ist es sehr elegant, die Matrizenmulitplikation einmal zu implementieren und dabei einen abstrakten numerischen Typ zu verwenden. Für jeden dieser numerischen Typen implementiert man die Grundoperationen.

Dies kann aber in Bezug auf die Perforamnce und auch in Bezug auf die Rechengenauigkeit beim Arbeiten mit Fließkommazahlen zu Problemen führen. Es lassen sich viel performantere Algorithmen für diese Matrizenmultiplikation finden, wenn man sie speziell für komplexe Zahlen schreibt und auch auf die Realteile und Imagniärteile der Matrizenelemente zugrifen kann. Besonders tückisch sind aber auch die Rundungsfehler. Um Rundungsfehler zu verringern muss man auf die Kalkulationen Zugriff haben und deren Reihenfolge und Assoziierung steuern können.

Hier ein Beispiel:

a=3.0
b=4.0
c=5e30
d=-5e30

Berechnet man nun (a+b)+(c+d), erhält man 7.0, aber mit (a+c)+(b+d) erhält man 0.0.
In irb (ruby) sieht es etwa so aus:


$ irb
irb(main):001:0> a=3.0
=> 3.0
irb(main):002:0> b=4.0
=> 4.0
irb(main):003:0> c=5e30
=> 5.0e+30
irb(main):004:0> d=-5e30
=> -5.0e+30
irb(main):005:0> (a+b)+(c+d)
=> 7.0
irb(main):006:0> (a+c)+(b+d)
=> 0.0
irb(main):007:0>

Der Fehler kann sich nun natürlich noch beliebig fortpflanzen.

Das ändert aber nichts daran, dass ein auf Polymorphie basierender Ansatz in den allermeisten Fällen der richtige Weg ist, solange man nicht auf die entsprechende Optimierung angewiesn ist.

Es bleibt aber dabei, dass bei einer Aufteilung in Komponenten die richtige Granularität gewählt werden sollte, also keine Microkomponenten, aber auch nicht wahlloses Zusammenfügen von Dingen, die nicht zusammengehören, nur um die richtige Größe der Komponenten zu erreichen.

Share Button

Unicode, UTF-8, UTF-16, ISO-8859-1: Warum ist das so schwierig?

English

Seit etwa 20 Jahren schlagen wir uns mit der Umstellung auf Unicode herum.

Warum ist das so schwierig?

Das größte Problem ist, dass man Dateien nur sehr begrenzt ansieht, wie ihr Inhalt zu interpretieren ist. Wir haben letztlich ein paar Tricks, mit denen man es oft erkennen kann:
Die Endungen funktionieren für häufige und gut definierte Dateitypen, z.B. .jpg oder .png recht gut. In anderen Fällen wird der Inhalt der Datei untersucht und zum Beispiel am Anfang der Datei so etwas wie

#!/usr/bin/ruby

gefunden, woraus geschlossen werden kann, dass das mit ruby ausgeführt werden soll und zwar mit dem Ruby, das unter /usr/bin/ruby steht. Wenn man sich lieber nicht festlegen will und das Ruby haben will, das zuerst im Pfad ( $PATH ) kommt, dann kann man stattdessen

#!/usr/bin/env ruby

verwenden. Das geht leider unter MS-Windows nur unter cygwin, wenn man das cygwin-Ruby verwendet, nicht aber mit dem nativen Win32-Ruby (oder Win64-Ruby).

Nun kommt aber der nächste Schritt und der ist einfach ärgerlich. Welches „encoding“ wird für diese Datei verwendet? Man kann sich auf UTF-8 oder ISO-8859-1 einigen, aber sobald einer im Team vergisst, seinen Editor entsprechend zu konfigurieren, ist Durcheinander abzusehen, weil dann Dateien entstehen, die UTF-8 und ISO-8859-1 (oder noch andere Encodings) miteinander mischen, was dann irgendwann zu obskuren Fehlern führt.

Es war ein großer Fehler, dass bei der Entwicklung von C, Unix und vor allem der libc ein Verständnis von Dateien definiert wurde, das keine Typ-Information für den Dateiinhalt erlaubt. Im Internet haben wir Mime-Header für EMail und WWW-Seiten und alles mögliche andere. Dadurch weiß der Empfänger der Kommunikation stets, wie die empfangen Daten zu interpretieren sind. Ich denke, dass Dateien solche Metainformationen haben sollte, die etwa dem Mime-Header entsprechen. Dann könnte man Dateien beliebig umbenennen, sogar die Endung, ohne dass die Datei dadurch unlesbar würde. Aber als Unix und C entwickelt wurde, wurde auch die libc und die Filesystemkonzepte definiert. Daran haben sich alle Unixe seither gehalten und auch Linux folgt diesen Vorgaben. Aber auch in der MS-Windows-Welt hat man die Betriebssysteme wahrscheinlich in C entwickelt und dabei diese Eigenschaften oder deren Fehlen geerbt. Ich weiß nicht, bis wann man versucht hat, MS-Windows-NT/2000/XP/Vista/7/8… noch auf FAT-Dateisystemen lauffähig zu halten, dabei hätte NTFS mit den multiplen Streams pro Datei eine Werkzeug geschaffen, mit dem man so einen Mime-Typ im zweiten Stream und den eigentlichen Inhalt im ersten Stream speichern könnte. Was aber fehlt ist ein allgemein anerkanntes Regelwerk, das die Nutzung des zweiten Streams für Typinformationen festlegt Aber man verwendet weiterhin Endungen, hofft auf gutes Glück bei Textdateien und analysiert magic-bytes innerhalb der Dateien, um den Typ der Datei und das Encoding zu raten. Linux hat Attribute, in denen man solche Information ablegen kann, aber das bringt nur etwas, wenn es ein Standard ist, den „alle“ kennen und der von jeder Software, die davon betroffen ist, eingehalten wird.

Natürlich haben XML und HTML die Möglichkeit, das Encoding innerhalb der Datei zu definieren. Dummerweise muss man aber das Encoding der Datei schon kennen, um die Zeilen zu lesen, wo drinsteht, welches Encoding die Datei hat. Das ist nicht so schlimm, denn letztlich steht diese Information jeweils am Anfang der Datei und man kann ein paar Encodings durchprobieren, und jeweils unter dieser Annahme anfangen, die Datei zu lesen, bis man es richtig weiß. UTF-16 erkennt man an den beiden Markerbytes am Anfang und dann kann man mit der Annahme UTF-16 lesen, bis man das Encoding, das nun UTF-16 sein muss findet. Andernfalls kann man mit der Annahme, dass es UTF-8 ist, anfangen und muss dann auf das, was man gefunden hat, umstellen. Wenn kein Encoding angegeben ist, ist UTF-8 sowieso der Defaultwert.

Am gefährlichsten ist es, UTF-8 und ISO-8859-1 (oder ähnliche Encodings) zu verwechseln. Da die unteren 128 Zeichen bei beiden gleich sind und zumindest im deutschen Sprachraum doch die überwältigende Mehrheit von Text-Inhalten darstellen, sticht das Problem nicht gleich ins Auge, sondern schleicht sich eher ein, wenn man nicht sauber arbeitet und Dateien mit verschiedenen Encodings zusammenkopiert oder die Dateien den falschen Konversionen aussetzt. Nun werden aber die Umlaute in UTF-8 durch zwei Zeichen codiert, in ISO-8859-1 durch eines. Beim Lesen der Datei unter der falschen Annahme bekommt man also irgendwann mal Zeichenfolgen, die in dem Encoding eigentlich gar nicht vorkommen dürften. Bei UTF-16 ist das einfacher, weil die Dateien dort jeweils mit FFFE oder FEFF anfangen, so dass man einigermaßen sicher UTF-16 an sich und die Bytefolge (niedriges zuerst oder hohes zuerst) erkennen kann. Es gäbe auch eine drei Byte lange Markierung für UTF-8. Obwohl das jede Software verstehen sollte, ist man in der Praxis meistens gezwungen, diese Byte-Sequenz zu entfernen, weil sie die meiste Software verwirrt, zu fehlerhaften Verhalten bringt oder gar zum Absturz bringt.

In der MS-Windows-Welt kommt noch als weiteres Ärgernis hinzu, dass zwar das ganze System mit modernen Encoding arbeiten kann, aber diese schwarzen CMD-Fenster kommen immer noch mit CP-850 oder CP-437 hoch, enthalten also in etwa dieselben Zeichen wie ISO-8859-1, aber an anderen Positionen. Da bekommt man dann schon einmal ein Sigma-Zeichen statt einem „ä“ zu sehen. Diese Incompatibilität innerhalb desselben Systems bringt natürlich Nachteile mit sich.

Links

Share Button

Die kleinen Hürden der Interoperabilität

English

Heute hat sich in der IT-Landschaft vieles vereinheitlicht, so dass Interoperabilität besser geworden ist als vor 20 Jahren.

Ein paar Beispiele:

  • Netzwerktechnologie: Heute hat sich TCP/IP als Netzwerktechnologie durchgesetzt. Sogar die Verkabelung mit RJ45/Ethernet und die Funknetze (WLAN) sind standardisiert und passen zwischen verschiedensten Geräten zusammen. Vor ein paar Jahren gab es beliebig viele proprietäre Netzwerktechnologiene, die nicht miteinander kompatible waren, z.B. BitNet (IBM), NetBios (MS), DecNet (DEC), IPX (Novell),….
  • Zeichensätze: Heute haben wir Unicode und ein paar standardisierte Zeichensätze und Codierungen und zumindest für Web und EMail Wege, diese Metainformation zur Verfügung zu stellen. Dieser Bereich ist noch nicht problemfrei, aber im Vergleich zu früheren Jahren, wo verschiedene EBCDIC-Codierungen regierten oder wo Zeichensätze üblich waren, die keine Umlaute enthielten, haben wir hier auch große Fortschritte in der Standardisierung erlebt.
  • Zahlen: Es hat sich für Fließkommazahlen und für Ganzzahlen eine gewisse, relativ kleine Menge von numerischen Typen etabliert, die immer wieder benutzt werden und die sich überall (fast) gleich verhalten. Problematisch bleibt der Integer-Überlauf.
  • Software: Früher hat man Software für eine spezifische Maschine entwickelt, also eine CPU-Architektur mit einem Betriebssystem. Heute hat man die Möglichkeit, einheitliche „Plattformen“ auf verschiedenester Hardware zu haben: Linux läuft auf fast jeder physikalischen und virtuellen Hardware vom Mobiltelefon bis zum Supercomputer und es ist praktisch derselbe Kernel, lässt sich also gleich nutzen. Java, Ruby, Perl, Scala und andere Programmiersprachen sind auf verschiedensten Plattformen vorhanden und bieten sozusagen ihre eigene abstrakte Plattform. Und das Web ist eine einfache und sinnvolle Möglichkeit, Applikationen für verschiedenste Geräte nur einmal zu entwickeln.
  • Dateisysteme: Es hat sich ein einigermaßen einheitliches Verständnis dafür, wie ein Dateisystem aussehen soll, entwickelt, mit einigen betriebssystemspezifischen Besonderheiten. Für Datenhaltung lassen sich Dateisysteme aber gemeinsam für verschiedene Betriebssyteme nutzen, zum Beispiel mit Samba.
  • GNU-Tools: Die GNU-Tools (bash, ls, cp, mv,……..) sind unter Linux zum Standard geworden und ihren traditionellen Unix-Pendents, wie man sie noch heute z.B. unter Solaris findet, haushoch überlegen. Man kann sie aber auf praktisch jedem Unix installieren und es gibt mit cygwin sogar eine Portierung für MS-Windows.

Interoperabilität ist heute für viele Interoperabilität zwischen Linux (oder anderen Posix-Systemen) und Win32/Win64 (MS-Windows).

Erfahrene Linux-Anwender sind es gewohnt, als Trennzeichen für Pfade diesen Schrägstrich „/“ (forward slash) zu verwenden. Der umgekehrte Schrägstrich „\“ wird benötigt, um Sonderzeichen zu „escapen“. In der MS-Windows-Welt sieht man häufig, dass der umgekehrte Schrägstrich „\“ (backslash) als Trennzeichen verwendet wird. Das ist nötig im CMD-Fenster, weil dieses den normalen Schrägstrich „/“ nicht durchlässt. Meine Erfahrung ist aber, dass die low-level-Win32-Bibliotheken beide Varianten verstehen. Sowieso werden die normalen Schrägstriche „/“ von cygwin, ruby, perl, java u.s.w. verstanden. Man kann sich also fast immer die Mühe sparen, hierfür Fallunterscheidungen zu machen, außer man schreibt cmd-Skripte. Und wer will sich das schon für mehr als fünf oder sechs Zeilen antun. Ich empfehle also für Java-, Perl- und Ruby-Entwickler auch unter MS-Windows ausdrücklich immer den normalen Schrägstrich „/“ als Trenungszeichen in Pfaden zu verwenden. Das ist lesbarer, schon weil man den umgekehrten Schrägstrich „\“ oft verdoppeln muss, und es erleichtert die Portablität auf Linux oder Posix.

Tückischer ist die Sache mit dem Zeilenwechsel. In der Linux- und Unix-Welt ist in Textdateien ein „Linefeed“ („\n“=Ctrl-J) als Zeilenwechsel üblich. In der MS-DOS und MS-Windows-Welt hat sich dagegen „Carrige-Return+Linefeed“ („\r\n“=Ctrl-M Ctrl-J) etabliert. Die meisten heutigen Programme stören sich nicht daran und kommen unter beiden Plattformen mit beidem klar. Wer unter MS-Windows Notepad verwendet, wird mit Linux-Zeilenenden keine Freude haben, aber Notepad muss man wirklich unter MS-Windows nicht benutzen, da es dort bessere Editoren (gvim, emacs, ultraedit, scite, …) gibt. Umgekehrt führt der MS-Windows-Zielenwechsel bei ausführbaren Skripten unter Linux zu Probleme. Skripte enthalten normalerweise in der ersten Zeile so etwas wie „#/usr/bin/ruby“. Das nimmt das Betriebssystem als Hinweis, dass man das Programm /usr/bin/ruby verwenden soll, um dieses Skript auszuführen. Wenn aber die Zeile mit Ctrl-M Ctrl-J endet, dann wird nach einem Programm „/usr/bin/ruby^M“ gesucht (^M = Ctrl-M = „\r“) gesucht, das es natürlich nicht gibt und man erhält eine unverständliche Fehlermeldung.

Ad hoc kann man die Umwandlung schnell so machen:

$ perl -i~ -p -e ’s/\r//g;‘ script

Oder für die Umgekehrte Richtung:

$ perl -i~ -p -e ’s/\n/\r\n/g;‘ textfile

Wer noch Subversion verwendet, sollte Skripte dort so einstellen, dass sie immer nur mit „LF“ als Zeichenwechsel gespeichert werden und Textdateien vielleicht jeweils mit der Konvention des Betriebsystems, unter dem der Client läuft.

Links

Share Button

neo4j

Da ich in dieser Woche einen Vortrag darüber gehört habe, schreibe ich mal einen kurzen Beitrag dazu.

Sicher haben viele schon von „NoSQL“-Datenbanken gehört.

In den guten alten Zeiten kam so etwa alle 10 Jahre ein neues Datenbank-Paradigma auf, bis die relationalen Datenbanken kamen. So etwa Mitte der 90er Jahre wäre nach diesem 10-Jahres-Rhytmus wieder etwas neues fällig gewesen und die objektorientierten Datenbanken waren ein recht offensichtlicher Kandidat. Letztlich blieben sie aber Nischenprodukte, ebenso wie einige andere Ideen, wie XML-Datenbanken.

Die relationalen Datenbanken und vor allem SQL waren zu gut oder zu gut etabliert und zu gut verstanden und statt objektorientierte Datenbanken einzusetzen verliebte man sich in verschiedene Technologien, um objektorientierte Software mit relationalen Datenbanken zu verbinden, zum Beispiel OR-Mapping wie Hibernate, JDO oder Eclipselink in der Java-Welt oder ActiveRecord in der Ruby-Welt. Diese Technologien, ihre Vor- und Nachteile und auch die grundsätzlichen konzeptionellen Fragen dazu sind sicher noch Stoff für viele Blog-Artikel in der Zukunft…

Letztlich scheint jetzt das Thema „NoSQL“-Datenbanken neben den weiterhin starken relationalen Datenbanken seinen Platz einzunehmen. Dabei steht „NoSQL“ angeblich für „not only SQL“. Letztlich sind es aber zwei Aspekte, an denen man schraubt. Die gängigen SQL-Datenbanken sind relational (oder zumindest unterstützen sie das relationale Modell) und transaktional. Das Thema Transaktionen ist sicher auch interessant genug für viele Blog-Beiträge und man kann problemlos allein darüber ein Buch von mehreren 100 Seiten schreiben, das nicht langweilig wird, wenn man sich mit verteilten Transaktionen und der Implementierung dieser Konzepte und der theoretischen und praktischen Zuverlässigkeit solcher Implementierungen gemessen an den Ansprüchen beschäftigt. Es gibt gegen Einwurf vieler großer Münzen mehrere gute Monographien dazu im Buchhandel.

Eine wichtige Motivation für die Entwicklung und Verbreitung der noSQL-Datenbanken war „Big Data“, also die Verarbeitung riesiger Datenmengen, die den Rahmen traditioneller relationaler transaktionaler Datenbanken wie Oracle, DB2, PostgreQL u.s.w. sprengen. Solche Fragestellungen findet man unter anderem bei Webapplikationen wie sie Google oder Facebook betreiben. Es gibt aber auch Fragestellungen mit Datenmengen, die noch gut für relationale Datenbanken handhabbar sind, die sich aber von ihrer Struktur nicht so gut für das relationale Modell eignen.

Nun muß eine SQL-Datenbank nicht transaktional sein. Zum Beispiel war es mySQL lange Zeit nicht und heute ist die für Data-Warhouses spezialisierte Datenbank Teradata unterstützt Transaktionen nur eingeschränkt.

NoSQL-Datenbanken weichen aber das relationale Prinzip auf und je nach Einzelfall eventuell außerdem die Transaktionalität. Es gibt verschiedene Arten von NoSQL-Datenbanken, zum Beispiel Key-Value-Stores wie Riak oder dokumentenorientierte Datenbanken wie MongoDB oder CouchDB, die sich eignen, wenn man eine gewisse Struktur der Daten kennt, aber die einzelnen Datensätze doch von Zeile zu Zeile (oder hier von Dokument zu Dokument) zu stark varieren oder zu stark strukturiert sind, um gut in eine normalisierte relationale Datenbank zu passen.

Graphendatenbanken speichern Daten in der Struktur eines Graphen. Man hat also Knoten mit gewissen Eigenschaften (Daten) und Verbindungen zwischen diesen Knoten mit gewissen Eigenschaften. Ein Beispiel ist eine IT-Landschaft, in der man Hardware, virtuelle Server, Basis-Software, Applikationen, Businessprozesse u.s.w. hat, zwischen denen verschiedene Arten von Abhängigkeiten bestehen können. Das war das Beispiel, das in dem Vortrag gebracht wurde. Das läßt sich eigentlich gut im relationalen Modell abbilden, ist aber in der Praxis sehr schwerfällig zu gebrauchen, weil die Queries um einen Teilgraphen zu laden, sehr schwerfällig sind und weil man letztlich durch fortgesetztes Verfolgen von Abhängigkeiten sehr schnell einen großen Teil des Systems im Speicher hat. Mit einer Graphendatenbank kann man diese Struktur allerdings ganz natürlich und direkt modellieren. neo4j ist zum Beispiel eine solche Graphendatenbank, die als Opensource-Software verfügbar ist. Sie enthält auch praktischerweise gleich noch Implementierungen einiger gängiger Graphenalgorithmen, die man direkt auf dem gespeicherten Graphen operieren lassen kann. So lassen sich gewisse Aufgabenstellungen sehr elegant lösen, die mit einer relationalen Datenbank zwar theoretisch korrekt, aber nicht praxistauglich umsetzbar sind, sobald der zu speichernde Graph eine gewisse Größe und Komplexität erreicht. Zur Aufweichung des Transaktionsprinzip ist noch zu sagen, daß neo4j transaktional ist.

Share Button