Lektion 20 - Dort, wo das Bit haust

Erstellt von Frithjof Thu, 31 Jul 2008 22:09:00 GMT

Zahlen bestehen aus Ziffern. Eine Ziffer ist ein einzelnes Symbol. Eine Zahl wird aus einer oder mehreren Ziffern gebildet. Nimm die 21, sie besteht aus den beiden Ziffern 2 und 1. Die Reihenfolge der Ziffern oder die Stelle, an der Ziffern stehen, ist bei Zahlen wichtig. Denn 21 ist nicht dasselbe wie 12. Daher nennt man unser gewöhnliches Zahlensystem auch ein Stellenwertsystem. Jeder Position in einer Zahl ist ein bestimmter Stellenwert zugeordnet und die Ziffer gibt an, wie oft dieser Stellenwert gezählt wird. Unser Zahlensystem ordnet jeder Stelle von rechts nach links ein bestimmtes Vielfache von Zehn zu (1er, 10er, 100er, 1000er, ...) und es besitzt auch zehn verschiedene Ziffern (Symbole) 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 zur Darstellung von Zahlen. Daher nennt man es auch das Zehnersystem oder (wegen lateinisch decem für zehn) Dezimalsystem.

Ein Bit ist eine Ziffer im Zahlensystem der Computerwelt, dem Dualsystem (oder Binärsystem). Hier gibt es nur zwei verschiedene Ziffern, 0 und 1. Jeder Position in einer Bitzahl ordnet man ein Vielfaches von zwei zu (1er, 2er, 4er, 8er, 16er, 32er, 64er, ...). Es scheint aber offensichtlich für einen Computer keine Einschränkung zu sein, dass er nur mit zwei verschiedenen Ziffern umgehen kann.

In dieser Lektion schauen wir uns an, wo die Bits im Computer wohnen, wie wir sie lesen und schreiben können und wie daraus Buchstaben und Zahlen werden, die du am Monitor siehst.

Dateien

Die Daten deines Computers werden auf der Festplatte dauerhaft gespeichert. So bleiben sie auch nach Ausschalten des Computers erhalten. Von der Eingabe bis zur Festplatte müssen sie dabei einen weiten Weg zurücklegen. Umgekehrt natürlich auch. Dabei passiert ziemlich viel, von dem du an der Oberfläche aber glücklicherweise nur wenig mitbekommst.

Zusammengehörige Daten werden in einer Datei zusammengehalten.

Zusammengehörige Dateien wiederum sind in einem “Verzeichnis” gruppiert. Und mehrere Verzeichnisse können wieder in einem Verzeichnis gruppiert werden. So baut sich eine Hierarchie von Verzeichnissen und Dateien auf, das sogenannte Dateisystem. Ganz oben wird das Dateisystem vom Laufwerk (in Windows) zusammengehalten.

Dateien lassen sich direkt auf der Festplatte mit Ruby anlegen. Du probierst das gleich mit einer einfachen Textdatei aus:

Die Datei soll “convolvulus_arvensis.txt” heißen und folgenden Inhalt haben:


Convolvulus arvensis L.
Familie: Convolvulaceae
Ordnung: Solanales
Deutscher Name: Acker-Winde
Blütenfarbe: weiß bis rosa oder gestreift
Blüten: blattachselständig, lang gestielt, Krone weit trichterförmig, 2-3 cm lang
Blätter: pfeil-/spießförmig, 3-4 cm lang
Stängel: dünn, kriechend, windend
Wurzel: bis 2 m tief
Vorkommen: Äcker, Weinberge, Gärten, Wegränder

Dateien schreiben

Hier ist das Rubyprogramm, das diese Datei anlegt.


# lektion_20.rb
File.open("convolvulus_arvensis.txt", "w") do |datei|
  datei.puts "Convolvulus arvensis L." 
  datei.puts "Familie: Convolvulaceae" 
  datei.puts "Ordnung: Solanales" 
  datei.puts "Deutscher Name: Acker-Winde" 
  datei.puts "Blütenfarbe: weiß bis rosa oder gestreift" 
  datei.puts "Blüten: blattachselständig, lang gestielt, Krone weit trichterförmig, 2-3 cm lang" 
  datei.puts "Blätter: pfeil-/spießförmig, 3-4 cm lang" 
  datei.puts "Stängel: dünn, kriechend, windend" 
  datei.puts "Wurzel: bis 2 m tief" 
  datei.puts "Vorkommen: Äcker, Weinberge, Gärten, Wegränder" 
end

Mit File.open wird eine Datei geöffnet. Der erste Parameter gibt den Namen der zu öffnenden Datei an, der zweite Parameter (das “w”) legt fest, wie die Datei geöffnet wird, also in welchem Zustand (Modus) das geschehen soll. Das “w” steht für “write”, die Datei soll also für den Schreibzugriff geöffnet werden.

Die open Methode ist ein Iterator, der die geöffnete Datei an den Codeblock in der Variablen datei übergibt. Am Ende des Blocks kümmert sich der Iterator darum, die Datei wieder zu schließen.

Die Datei legt das Rubyprogramm im selben Verzeichnis an, von dem aus es gestartet wurde. Das muss nicht unbedingt dasselbe Verzeichnis sein, in dem sich das Rubyprogramm befindet. Die Datei wird hier also relativ zum Ausführungsverzeichnis angelegt.

Möchtest du die Datei zuverlässig in einem bestimmten Verzeichnis anlegen, dann teile das Ruby bei File.open genau mit. Der Methode open gibst du dazu nicht nur den Dateinamen, sondern den absoluten Pfad beginnend beim Laufwerk und über alle Verzeichnisse hinweg bis zur Datei an.


# lektion_20.rb
File.open("c:/herbarium/convolvulus_arvensis.txt", "w") do |datei|
  datei.puts "Convolvulus arvensis L." 
  ...
end

Als Trennzeichen beim Pfad kannst du sowohl den normalen Schrägstrich (slash) / verwenden, als auch den nach links gerichteten (back slash). Nimmst du den Backslash, musst du ihn aber zweimal hinschreiben—du erinnerst dich an unsere Häuserreihe?

Dann überprüfe noch schnell, ob die Datei wirklich im Dateisystem dort angekommen ist, wo du sie erwartest. Als nächstes möchten wir die Datei nämlich lesen.

Dateien lesen

Wenn Ruby eine Datei schreiben soll und sie existiert nicht, dann wird zunächst die Datei angelegt und dann der gewünschte Inhalt hineingeschrieben.

Beim Lesen einer Datei ist es etwas unangenehmer, wenn sie nicht existiert. Versuchen wir die zuvor angelegte Datei wieder in das Rubyprogramm einzulesen und auf der Kommandozeile auszugeben.


File.open("c:/herbarium/convolvulus_arvensis.txt", "r") do |datei|
  puts datei.read
end
Der Aufruf der open Methode unterscheidet sich kaum, nur der Modus ist auf “r” (read, d.h. lesen) eingestellt. Das Dateiobjekt in der Variablen datei, das der Codeblock vom Iterator erhält, bietet verschiedene Methoden zum lesen der Datei an. Mit read wird die gesamte Datei eingelesen. Und da wir nur mit Textdateien hier zu tun haben, wird der Inhalt natürlich auch als Text, d.h. als String, zurückgeliefert, wovon du dich mit einem einfachen puts datei.read.class selbst überzeugen kannst.

Das vollständige Lesen einer gesamten Datei ist nicht sinnvoll, wenn sie sehr groß ist. Dann könnte man zum Beispiel zeilenweise lesen mit der Methode each, die eine Zeile nach der anderen liest. So muss nicht die gesamte Datei in den Arbeitsspeicher geladen werden.


File.open("c:/herbarium/convolvulus_arvensis.txt", "r") do |datei|
  datei.each do |zeile|
    puts zeile
  end
end

Festplatte voller Buchstaben?

Alte Computer haben alte Festplatten. Findest du eine alte Festplatte, lass dir nicht die Gelegenheit entgehen, sie aufzuschrauben, um die feine Mechanik und glänzenden Magnetplatten zu bestaunen. Dort wirst du aber keine Buchstaben sehen. Du wirst wohl überhaupt nichts sehen, was irgendwie nach Daten oder Dateien oder Zeichen aussieht.

Ein Computer arbeitet in Wirklichkeit nicht mit Buchstaben oder Texten, sondern nur mit natürlichen Zahlen aus dem Dualsystem—also nur mit 0 und 1 als Ziffern. Egal was du an der Oberfläche eingibst (Texte, Bilder, natürliche Zahlen, Dezimalzahlen, ...), schließlich muss alles in diese Binärzahlen umgewandelt werden und hoffentlich, bevor der Strom ausfällt, werden die Binärzahlen dann in die Festplatte eingekerbt. Ein magnetischer Lese- und Schreibkopf der über die Scheiben der Festplatte saust, kann die Teilchen auf der Scheibe magnetisch in zwei Richtungen ausrichten. Eine der beiden Richtungen wird als die 0 interpretiert, die andere magnetische Teilchenausrichtung als die 1. Ohne Magnetfeld in der Nähe verbleiben die Teilchen auf der Scheibe so wie sie zuvor ausgerichtet wurden.

Bleibt uns nur noch die Frage zu klären, wie aus den Binärzahlen dann die Buchstaben werden und umgekehrt? Dafür gibt es in jedem Betriebssystem eine Tabelle, in der jedem an der Oberfläche darstellbarem Zeichen ein Zahlenwert zugeordnet wird. Diese Tabellen nennt man auch Codepage oder Zeichensatztabelle.

Je nach Größe dieser Tabellen gibt es unterschiedlichste Bezeichnungen. Die wohl umfangreichste ist der Unicode, die bekannteste dürfte aber immer noch der ASCII Zeichensatz sein.

Zum Beispiel ist dem Buchstaben A in der ASCII Tabelle der Zahlenwert 65 im Dezimalsystem (Zehnersystem) zugeordnet. Die 65 wiederum lautet im Dualsystem 1000001 (das kannst du übrigens leicht mit dem Taschenrechner von Windows umrechnen, schalte ihn dazu über “Ansicht” auf “Wissenschaftlich” um). Also für jedes zu speichernde A in einer Textdatei wird auf der Festplatte die binäre Zahlenfolge 1000001 durch den Magnetisierungskopf eingekerbt!

Folgendes kleine Rubyprogramm gibt uns den Inhalt der obigen Textdatei in den verschiedenen Codierungen aus. Zuerst das Zeichen selbst in lesbarer Form, dann den zugehörigen Zahlenwert im Dezimalsystem, im Hexadezimalsystem, im Oktalsystem und letztlich im Dualsystem.


File.open("c:/herbarium/convolvulus_arvensis.txt", "rb") do |file|
  puts "\tDez\tHex\tOkt\tBin" 
  puts "\t---\t---\t---\t---" 
  while(b = file.getc)
    c = b.chr
    puts "#{Regexp.escape(c)},\t#{b},\t0x#{b.to_s(16).upcase},\to#{b.to_s(8).upcase},\t#{b.to_s(2)}" 
  end
end

[31.07.2008, 23:51]:> ruby lektion_20.rb
        Dez     Hex     Okt     Bin
        ---     ---     ---     ---
C,      67,     0x43,   o103,   1000011
o,      111,    0x6F,   o157,   1101111
n,      110,    0x6E,   o156,   1101110
v,      118,    0x76,   o166,   1110110
o,      111,    0x6F,   o157,   1101111
l,      108,    0x6C,   o154,   1101100
v,      118,    0x76,   o166,   1110110
u,      117,    0x75,   o165,   1110101
l,      108,    0x6C,   o154,   1101100
u,      117,    0x75,   o165,   1110101
s,      115,    0x73,   o163,   1110011
\ ,     32,     0x20,   o40,    100000
a,      97,     0x61,   o141,   1100001
r,      114,    0x72,   o162,   1110010
v,      118,    0x76,   o166,   1110110
e,      101,    0x65,   o145,   1100101
n,      110,    0x6E,   o156,   1101110
s,      115,    0x73,   o163,   1110011
i,      105,    0x69,   o151,   1101001
s,      115,    0x73,   o163,   1110011
\ ,     32,     0x20,   o40,    100000
L,      76,     0x4C,   o114,   1001100
\.,     46,     0x2E,   o56,    101110
\r,     13,     0xD,    o15,    1101
\n,     10,     0xA,    o12,    1010
F,      70,     0x46,   o106,   1000110
a,      97,     0x61,   o141,   1100001
m,      109,    0x6D,   o155,   1101101
i,      105,    0x69,   o151,   1101001
l,      108,    0x6C,   o154,   1101100
i,      105,    0x69,   o151,   1101001
...

Weiterführende Literatur

Der Umgang mit Dateien und den verschiedenen Zeichencodierungen ist ziemlich komplex. Daher möchte ich dir ein paar gute Bücher empfehlen, die dir weiterhelfen. Was wir in diesem Artikel besprochen haben, ist nicht mehr als ein kleiner Nunatak.

  • Ruby Cookbook, Lucas Carlson, Leonard Richardson, O’Reilly. Hier gibt es ein umfangreiches Kapitel mit ausführlichen Rezepten rund um Dateien (Anlegen, Lesen, Verzeichnisse anlegen, Binärdateien, ...).
  • The Ruby Way: Solutions and Techniques in Ruby Programming, Hal Fulton, Addison-Wesley

Mit Dateien werden wir noch viel zu tun bekommen in den nächsten Lektionen. Nicht nur mit Textdateien, sondern insbesondere mit Binärdateien (Bilder).