- In ASCII ist
Z bei 90 und a bei 97 angeordnet; dank der 6 Zeichen dazwischen beträgt der Code-Unterschied zwischen Groß- und Kleinbuchstaben genau 32
- 32 ist
2^5, daher unterscheiden sich entsprechende Groß- und Kleinbuchstaben wie A 65 und a 97 immer nur in genau einem Bit: 00100000
- Durch diese Anordnung kann man mit dem bitweisen Inversen von
32 und AND in Großbuchstaben umwandeln, mit 32 und OR in Kleinbuchstaben und mit 32 und XOR die Groß-/Kleinschreibung umkehren
- Die Position im Alphabet erhält man, indem man auf den Zeichencode AND mit
31 anwendet und so nur die unteren 5 Bits behält; A/a wird zu 1, Z/z zu 26
- ASCII ist eine frühe 7-Bit-Zeichenkodierung, die nur 128 Codepunkte darstellen kann; die ersten 128 Codepunkte des heute verwendeten Unicode sind identisch mit ASCII
ASCII-Anordnung und die Differenz von 32
- In der ASCII-Tabelle hat der Großbuchstabe
Z den Codewert 90, während der Kleinbuchstabe a nicht direkt danach, sondern bei 97 steht
- Dazwischen liegen die 6 Zeichen
[ \ ] ^ _ `
- Das englische Alphabet hat 26 Buchstaben, und zusammen mit diesen 6 Zeichen ergibt das
26 + 6 = 32
32 entspricht dem Wert 2^5, wodurch die Zuordnung von Groß- zu Kleinbuchstaben so ausgerichtet ist, dass sie sich nur in einem bestimmten Bit unterscheidet
- Zum Beispiel ist
A 65 bzw. 01000001, a ist 97 bzw. 01100001, und die Differenz zwischen beiden Werten ist 32
Die Beziehung zwischen ASCII und Unicode
- ASCII ist eine der frühen Methoden zur Zeichenkodierung und verwendet nur 7 Bit, um
2^7 = 128 Codepunkte darzustellen
- 128 Codepunkte reichen nicht aus, um alle von Menschen verwendeten Schriftzeichen aufzunehmen, insbesondere nicht für Sprachen wie Chinesisch mit Zehntausenden von Zeichen
- Heute wird Unicode als standardisierter Zeichensatz verwendet und besitzt mehrere Kodierungen wie UTF-8 und UTF-16
- Die ersten 128 Codepunkte von Unicode sind identisch mit ASCII
Das 5. Bit unterscheidet Groß- und Kleinbuchstaben
- Vergleicht man einen Großbuchstaben und den entsprechenden Kleinbuchstaben binär, wird immer das Bit umgeschaltet, das
32 entspricht
65 = 01000001 = A
97 = 01100001 = a
66 = 01000010 = B
98 = 01100010 = b
67 = 01000011 = C
99 = 01100011 = c
32 ist in Binärdarstellung 00100000, und genau dieses eine Bit erzeugt den Unterschied zwischen Groß- und Kleinbuchstaben
- Die Anordnung der Buchstaben in ASCII ist so gestaltet, dass sich die Groß-/Kleinschreibung per Bitoperation leicht umwandeln lässt
Groß-/Kleinschreibung mit Bitoperationen verarbeiten
-
In Großbuchstaben umwandeln
- Um ein Zeichen in einen Großbuchstaben zu verwandeln, führt man ein bitweises AND mit dem bitweisen Inversen von
32 aus
0 1 1 0 0 0 0 1 (97 = 'a')
& 1 1 0 1 1 1 1 1 (mask)
-------------------
0 1 0 0 0 0 0 1 (65 = 'A')
- Auf
a angewendet wird 97 zu 65, also zu A
- Wendet man dieselbe Operation auf das bereits großgeschriebene
A an, bleibt es unverändert A
-
In Kleinbuchstaben umwandeln
- Um ein Zeichen in einen Kleinbuchstaben zu verwandeln, führt man ein bitweises OR mit
32 aus
0 1 0 0 0 0 0 1 (65 = 'A')
| 0 0 1 0 0 0 0 0 (32)
-------------------
0 1 1 0 0 0 0 1 (97 = 'a')
- Auf
A angewendet wird 65 zu 97, also zu a
- Wendet man dieselbe Operation auf das bereits kleingeschriebene
a an, bleibt es unverändert a
-
Groß-/Kleinschreibung umkehren
- Um Groß- und Kleinbuchstaben umzuschalten, führt man ein bitweises XOR mit
32 aus
0 1 1 0 0 0 0 1 (97 = 'a')
^ 0 0 1 0 0 0 0 0 (32)
-------------------
0 1 0 0 0 0 0 1 (65 = 'A')
0 1 0 0 0 0 0 1 (65 = 'A')
^ 0 0 1 0 0 0 0 0 (32)
-------------------
0 1 1 0 0 0 0 1 (97 = 'a')
a wird zu A und A wird zu a
Die Alphabetposition über die unteren 5 Bits erhalten
- Die Position im Alphabet lässt sich berechnen, indem man auf den Zeichencode ein bitweises AND mit
31 anwendet
0 1 0 0 0 0 0 1 (65 = 'A')
& 0 0 0 1 1 1 1 1 (31)
-------------------
0 0 0 0 0 0 0 1 (1)
0 1 1 1 1 0 1 0 (122 = 'z')
& 0 0 0 1 1 1 1 1 (31)
-------------------
0 0 0 1 1 0 1 0 (26)
31 ist in Binärdarstellung 00011111, löscht also die vorderen Bits und lässt nur die unteren 5 Bits übrig
- Bei ASCII stimmen die unteren 5 Bits der alphabetischen Zeichen genau mit ihrer Position im Alphabet überein
A/a endet auf 00001 und wird damit zu 1, B/b endet auf 00010 und wird zu 2, Z/z endet auf 11010 und wird zu 26
c & 31 ist bei ASCII-Zeichencodes dasselbe wie c % 32
- Weil
32 eine Zweierpotenz ist, entfernt das Maskieren mit 31 die Vielfachen von 32 und bewahrt nur den Rest
'A' = 65 → 65 % 32 = 1
'B' = 66 → 66 % 32 = 2
...
'Z' = 90 → 90 % 32 = 26
'a' = 97 → 97 % 32 = 1
'b' = 98 → 98 % 32 = 2
...
'z' = 122 → 122 % 32 = 26
1 Kommentare
Lobste.rs-Kommentare
Die Erklärung ist okay, aber ich finde, dass https://garbagecollected.org/2017/01/31/four-column-ascii/ es besser erklärt.
Es geht nicht nur um Shift, sondern auch um Ctrl. Tab ist zum Beispiel Ctrl-I, weil I
1001001ist und Ctrl das erste Bit maskiert, sodass für Tab0001001übrig bleibtWenn man wie die Hersteller in den 1960ern nicht mit elektronischer Logik, sondern mit elektromechanischer Logik arbeitet, ist eine bit paired keyboard viel leichter zu implementieren.
Die Shift-Taste muss im ASCII-Zeichen nur ein einziges Bit umschalten. Heute steckt in jeder Tastatur eine universelle CPU und Logik ist praktisch gratis, aber in einer Zeit, als Universalrechner noch raumgroß waren, waren solche Lösungen deutlich teurer
Der Artikel führt als Motivation für das ASCII-Layout an, dass sich die Umwandlung zwischen Groß- und Kleinschreibung effizient per Bitoperation implementieren lässt, aber dieser Nutzen wirkt heute, wenn auch historisch nicht, ziemlich dünn.
Die Umwandlung
a→Afunktioniert nur bei einfachem Text, und selbst wenn ISO-8859-1 oder Unicode dieses Muster teilweise auf Zeichen wieüoderçausdehnen, hängt Groß-/Kleinschreibung von Region, Kontext und Zeit ab. Die korrekte Zuordnung von Groß- und Kleinbuchstaben beißoderïwird von Sprache, Regulierungsbehörden, Nutzungsregion und Epoche beeinflusst.Unicode bietet Groß-/Kleinschreibungs-Mappings und Faltungs-Tabellen wie https://www.unicode.org/charts/case/, die für häufige Fälle ziemlich nah an die richtige Antwort herankommen, aber weil hier menschliche Regeln hineinspielen, ist die Komplexität praktisch unendlich und man braucht unter Umständen maßgeschneiderte Software, um es wirklich richtig zu machen.
Wenn man heute nicht garantieren kann, dass man auf die ersten 2⁷ Codepoints von Unicode beschränkt ist, bricht der Offset
0b0010_0000leicht auseinander, und teurere Codepfade als eine einzelne Bitoperation sind unvermeidlich. Selbst in CPython läuft''.upperzwar über den ASCII-Schnellpfad inunicode_upper{,_impl}, führt aber tatsächlich Verzweigungen, Inline-Funktionen, Strukturzugriffe, mehrere Funktionen und Funktionen, Makros und schließlich einen Tabellen-Lookup aus.Die Implementierung von
''.upperin modernen Sprachen dürfte meist ähnlich komplex sein, und selbst wenn der Compiler optimiert, scheint ein moderner Ansatz zwangsläufig teurer zu sein als eine einzelne Bitoperation. Der Vorteil dieses Designs dürfte vor allem in maßgeschneiderter Software sichtbar werden, etwa wenn man arithmetische Operationen auf rohen Binärdaten oder auf einemnumpy.ndarraymitdtype=uint8ausführt, das Unicode-Daten mit Codepoints unter 2⁷ enthält.Was mich interessiert, ist Folgendes: Wenn man annimmt, dass die einzige Motivation für diese Designentscheidung wirklich die im Artikel genannte war, dann hätte es damals doch auch die Alternative einer 128-Byte-Lookup-Tabelle gegeben. In welchem Zeitraum der Computergeschichte war eine einzelne Bitoperation effizienter oder praktikabler als ein Lookup in einer 128-Byte-Tabelle?
Eine Designentscheidung zu einem bestimmten Zeitpunkt fixiert die Form von etwas auf Basis von Annahmen, die später brechen, und macht spätere Erweiterungen schwierig oder unmöglich. Auch in neueren Artikeln zu IPv6 wird deutlich, dass Entscheidungen, die bei IPv4 und IPv6 damals richtig waren, heute enorme Lasten geworden sind.
Wohlwollend betrachtet wurden solche Entscheidungen nach Abwägung von Risiko und Nutzen getroffen. Etwa nach dem Muster: „Wenn ein einzelner Bitoperator das Umwandeln eines Zeichens in einen Großbuchstaben viel schneller macht, lohnt es sich, das Risiko einzugehen, dass das irgendwann kaputtgeht, sobald japanische Nutzer auftauchen.“
Aus heutiger Sicht macht eine Entscheidung von 1976 das Leben im Jahr 2026 schwerer, aber wenn man 1976 nie einen Computer hatte, hat man von den Vorteilen nie profitiert und nur die Nachteile geerbt. Wenn man diesen Egoismus einmal ausblendet, frage ich mich, wie man die Nachhaltigkeit solcher Entscheidungen besser vorhersagen kann, wenn man Software baut, die auch in 20 Jahren noch populär sein soll.
tolowernoch immer nützlich. https://dotat.at/@/2022-06-27-tolower-swar.htmlZumindest in ASCII sind die Buchstaben zusammenhängend angeordnet. EBCDIC hat einen Abstand von
0x40(64), und verglichen mit ASCII sieht es aus wie zwei Zeilen mit je 9 Zeichen und eine Zeile mit 8 Zeichen, die übereinander gestapelt sind.https://en.wikipedia.org/wiki/EBCDIC
Über den 5-Bit-Code http://www.quadibloc.com/crypto/images/tele38.gif und einige CDC-Peripheriegeräte, die meines Wissens einen 6-Bit-Code mit einer Menge Steuerzeichen für Seitenwechsel verwendeten, sage ich lieber nichts
Das erinnert mich daran, dass IRC-Nicknames ASCII-case-insensitive waren, sodass
foo{dasselbe wiefoo[undbar|dasselbe wiebar\\war.Es würde mich nicht wundern, wenn einige Clients deshalb bis heute durcheinanderkommen