blog · git · desktop · images · contact & privacy · gopher


Für zwischendurch: ASCII-Steuerzeichen

Warum ist an einem gängigen Terminal „^I“, also „<Ctrl + i>“, dasselbe wie ein Druck auf die Tab-Taste? Oder „^M“ dasselbe wie Return?

Wenn man ein bisschen damit rumspielt, kommt man schnell auf die Idee: „Ah, der n-te Buchstabe im Alphabet produziert ein Byte mit dem Wert n“. „I“ ist der neunte Buchstabe und ein Tab hat den Wert „0x09“. Passt auch großteils, die Wikipedia-Erklärung schlägt das auch vor. Ist aber unvollständig und wirkt irgendwie gekünstelt, wenn man es sich näher anschaut.

Man nehme eine ASCII-Tabelle und teile sie in vier Teile:

+--------------------------------------------------------------------------------------+
|   0  NUL     1  SOH     2  STX     3  ETX     4  EOT     5  ENQ     6  ACK     7  BEL|
|   8  BS      9  HT     10  LF     11  VT     12  FF     13  CR     14  SO     15  SI |
|  16  DLE    17  DC1    18  DC2    19  DC3    20  DC4    21  NAK    22  SYN    23  ETB|
|  24  CAN    25  EM     26  SUB    27  ESC    28  FS     29  GS     30  RS     31  US |
+--------------------------------------------------------------------------------------+
   32  SP     33  !      34  "      35  #      36  $      37  %      38  &      39  '
   40  (      41  )      42  *      43  +      44  ,      45  -      46  .      47  /
   48  0      49  1      50  2      51  3      52  4      53  5      54  6      55  7
   56  8      57  9      58  :      59  ;      60  <      61  =      62  >      63  ?    <---+
+--------------------------------------------------------------------------------------+     |
|  64  @      65  A      66  B      67  C      68  D      69  E      70  F      71  G  |     |
|  72  H      73  I      74  J      75  K      76  L      77  M      78  N      79  O  |     |
|  80  P      81  Q      82  R      83  S      84  T      85  U      86  V      87  W  |     |
|  88  X      89  Y      90  Z      91  [      92  \      93  ]      94  ^      95  _  |     |
+--------------------------------------------------------------------------------------+     |
   96  `      97  a      98  b      99  c     100  d     101  e     102  f     103  g        |
  104  h     105  i     106  j     107  k     108  l     109  m     110  n     111  o        |
  112  p     113  q     114  r     115  s     116  t     117  u     118  v     119  w        |
  120  x     121  y     122  z     123  {     124  |     125  }     126  ~     127  DEL  <---+

Die Zeichen 0 bis 31 und die 127 sind Steuerzeichen und keine druckbaren Zeichen. Daran sieht man schon, dass „A-Z“ gar nicht ausreicht, um alle Steuerzeichen zu erwischen.

Manche Tasten produzieren direkt ein Steuerzeichen: Die Tabulator-Taste zum Beispiel, Escape oder Enter. Aber nicht alle. Wie kann ich zum Beispiel ein NUL-Byte direkt eingeben? Oder ein BEL für die Terminal-Glocke? Kann ich das überhaupt?

Ja, kann ich. Die Zuordnung funktioniert so: Man nehme den Wert des Steuerzeichens und invertiere das siebte Bit (gleichbedeutend mit XOR mit „0x40“). So wird zum Beispiel aus der 9 des Tabulators eine 73. Das Zeichen Nummer 73 ist dann ein „I“ und so entsteht „<Ctrl + i>“.

So geht das mit allen Steuerzeichen, auch mit DEL, was außerhalb des ersten Blocks liegt. Hier kommt man bei „127 ^ 0x40 = 63“ heraus, also bei „^?“.

Der Rückweg geht natürlich genauso: Lese ich irgendwo „^L“, dann komme ich über „76 ^ 0x40 = 12“ beim Form Feed heraus.

Dieser Weg ist also das, was in der Wikipedia in einem kleinen Satz als „alternative formulation“ kurz erwähnt wird. Ich finde das jedenfalls deutlich konsistenter und einleuchtender als die Erklärung mit dem Alphabet. Naja, ich war nicht bei der Erstellung von ASCII dabei, womöglich ist genau diese Anordnung des Alphabets, die den „Trick“ mit XOR ermöglicht, auch Absicht. :-) Noch schöner wäre es natürlich gewesen, wenn man eines der ersten 32 Steuerzeichen durch DEL ersetzt und somit alle Steuerzeichen im ersten Block gehabt hätte. Dann wäre auch noch Platz für einen Akut gewesen, damit sich der Gravis nicht so alleine fühlen muss.

Die Teilung der Tabelle oben in vier Teile hatte auch einen Grund: So kann man nämlich alles direkt ablesen, ohne rechnen zu müssen. Man muss einfach nur zwei Blöcke nach oben oder unten springen. Auch ganz praktisch ist die Tabelle in „man 7 ascii“ bei den gängigen GNU/Linux-Distributionen (für die initiale Erklärung der Thematik ist sie nur etwas zu unübersichtlich):

Oct   Dec   Hex   Char                        Oct   Dec   Hex   Char
------------------------------------------------------------------------
000   0     00    NUL '\0'                    100   64    40    @
001   1     01    SOH (start of heading)      101   65    41    A
002   2     02    STX (start of text)         102   66    42    B
003   3     03    ETX (end of text)           103   67    43    C
004   4     04    EOT (end of transmission)   104   68    44    D
005   5     05    ENQ (enquiry)               105   69    45    E
006   6     06    ACK (acknowledge)           106   70    46    F
007   7     07    BEL '\a' (bell)             107   71    47    G
...   ...   ...   ...                         ...   ...   ...   ...

Hier steht sogar alles in derselben Zeile und man sieht sofort, dass man ein NUL-Byte über „^@“ eingeben kann.

Auf dieses Thema bin ich heute zufällig gestoßen, als ich auf Sven Maschecks großartiger Seite stöberte. :-) Dort wird auch noch einmal darauf hingewiesen, dass man zur Eingabe der meisten Steuerzeichen am Terminal vorher ein „^V“ braucht. Das NUL-Byte im folgenden Beispiel habe ich also über „<Ctrl + v><Ctrl + @>“ eingegeben:

$ ed
i
^@
.
w foo
2
q
$ od -t x1 foo
0000000 00 0a
0000002
$