Seitenanfang

Warum ist 075 = 61?

Manche Fehler passieren, obwohl man es eigentlich besser wissen müsste - nur einfach in dem Moment nicht daran denkt. So auch dieses Problem: 75 und 61 sind doch verschiedene Zahlen, wie können sie gleich sein? Sind sie nicht. Aber 075 und 61 sind gleich - und haben mich einige Zeit beschäftigt, bis ich das Rätsel gelöst hatte.

Vorschaubild für zahlenDie gestellte Aufgabe war nun wirklich nicht schwer: Wir haben neue Kategorien eingeführt und alle Elemente mussten auf die neuen Kategorienummern umgestellt werden. Leider wurden diese nicht nur in der Datenbank-Tabellenspalte category_id, sondern auch in einem JSON das in einer Text-Spalte abgelegt ist, verwendet.

Trotzdem kein Problem: Ein kleines Script, das einen Eintrag läd, alt nach neu ändert und wieder zurückschreibt - fertig. Natürlich sind endlose if/elseif-Konstrukte nurnervig und schlecht pflegbar, also entstand ein kleines Hash mit den alten und neuen Werten:

my %cat_map = (
101 => 147,
102 => 368,
103 => 473,
104 => 262,
105 => 150,
106 => 001,
107 => 313,
108 => 207,
109 => 409,
110 => 155,
111 => 599,
112 => 474,
113 => 356,
114 => 075,
);

Ja, ich tendiere bei Miniscripten dazu, keine extrem eindeutigen Variablennamen zu benutzen, sonst hätte ich es %category_map genannt, aber das ist hier nicht relevant. Wichtiger ist die unerklärliche Tatsache, dass nach der Umstellung (glücklicherweise nur der Testdaten) die Kategorie 75 gar nicht vertreten war, statt dessen fand sich die 61 wieder.

Natürlich ist 61 auch eine gültige Kategorie, denn anstatt vorher 15 haben wir jetzt hunderte, aber sie dürfte im Datenbestand gar nicht vorkommen, weil sie nicht Teil der Konvertierung war. Manuell auswählen konnte sie auch niemand, weil ich der einzige war, der zu diesem Zeitpunkt mit dieser Testdatenbank gearbeitet hatte.

Nach einigem Suchen, Unverständnis, Verzweifelung und Ausprobieren fand ich die Lösung, die ich eigentlich schon kannte, aber so gut wie nie selbst benutzt hatte: Die führende 0 vor dem 75 - nur für eine optisch einheitliche Formatierung überhaupt dort vorhanden - führt dazu, dass Perl die folgende Zahl als Oktalzahl interpretiert, sie also im 8er- und nicht im 10er-System beheimatet sieht.

Ebenso führt 0b vor einer Zahl dazu, dass diese als Binärzahl angesehen wird. So weit ich mich erinnere, habe ich dieses Feature noch nie benutzt. Häufiger dagegen ist ein führendes 0x vor einer Hexadezimalen Zahl, das benutze ich sogar regelmäßig:

$ perl -le 'print 052; print 0x2A; print 0b101010;'
42
42
42

Ausführlicher steht das auch im Perl-Wikibook.

Die Kategorie 106 wurde übrigens anstandslos konvertiert, weil die 1 in jedem Zahlensystem identisch ist:

$ perl -le 'print 1; print 01; print 0x01; print 0b01;'
1
1
1
1

Perl hat hier tatsächlich das 001 von oktal zu dezimal konvertiert, aber das Ergebnis war wieder 1 und so wurde der Fehler dort kaschiert.

Solche Fehler kann man übrigens recht einfach mit dem Modul Leading::Zeros vermeiden. Es kann die Oktal-Erkennung von Perl abschalten. Wahlweise führen Oktalzahlen dann zu einer Fehlermeldung oder sie werden dezimal interpretiert. Letzteres finde ich allerdings ziemlich gefährlich, denn ein chmod 0644, $filename; würde dann dezimal 644 setzen. Das wäre okatal 1204 und würde etwas ganz anderes als das gewünschte Ergebnis erzeugen:

sewi@tp:~$ touch x
sewi@tp:~$ touch y
sewi@tp:~$ chmod 1204 x
sewi@tp:~$ chmod 0644 y
sewi@tp:~$ ls -l x y
--w----r-T 1 sewi sewi 0 Sep 5 07:28 x
-rw-r--r-- 1 sewi sewi 0 Sep 5 07:28 y

Irgendwann werde ich allerdings wieder auf oktale Zahlen reinfallen. Ich benutze sie einfach zu selten, um immer daran zu denken.

 

Noch keine Kommentare. Schreib was dazu

Schreib was dazu

Die folgenden HTML-Tags sind erlaubt:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>