Klaus Ethgen schrieb:
> Ulrich D i e z <
ud.usenetco...@web.de> schrieb:
>> \usepackage[Buchstabe={j, \unexpanded{\unexpanded{ö}}}]{Leseblatt_UD_2022_05_17}
>
> Selbst ein `\unexpanded` reicht bei mir und \lb@Buchstabe enthält das
> native ö (in dem Fall).
Ich habe jetzt nicht näher erforscht, wie bzw wie oft kvoptions die Werte/
Values von String-Optionen expandiert. Mein Beispiel läuft nur mit zwei
\unexpanded durch.
> Ich habe das nur versucht, mit `\expandafter` in den Griff zu bekommen,
> bin dabei jedoch gescheitert. `\unexpanded` kannte ich bisher garnicht.
>
>> \unexpanded{\unexpanded{ö}}
>>
>> kann man im Latin1/ISO-8859-1-Encoding auch schreiben:
>>
>> \noexpand\noexpand\noexpand ö
>
> Was ist der Unterschied zwischen `\unexpanded` und `\noexpand`? Und hast
> Du eine Erklärung, weshalb letzteres dreimal benötigt wird?
\noexpand selbst ist expandibel und verschwindet beim Expandieren. Sofern
das auf \noexpand nachfolgende Token expandierbar ist, verhindert \noexpand,
dass es durch den momentan laufenden Expansions-Trigger expandiert wird.
Stattdessen wird es durch den momentan laufenden Expansions-Trigger wie
das nicht expandierbare \relax-Primitive behandelt.
\unexpanded selbst ist expandibel. \unexpanded und die geschweiften Klammern
des Balanced Text, die sein "Argument" ausmachen, verschwinden beim
Expandieren. Bei allem, was zwischen den geschweiften Klammern steht, wird
verhindert, dass es durch den momentan laufenden Expansions-Trigger expandiert
wird.
Mit \noexpand kann man also das Expandieren nur eines einzigen Token
verhindern, nämlich des nachfolgenden Token.
Mit \unexpanded kann man das Expandieren vieler Token verhindern, nämlich
all derjenigen Token, die im Token-Stream zwischen den geschweiften Klammern
stehen.
Schauen wir uns mal \noexpand\noexpand\noexpand ö an und gehen dabei von
einem Encoding aus, bei dem das "ö" durch genau ein (aktives) Character-Token
repräsentiert wird. Weiter angenommen, es läuft als Expansions-Trigger eine
\edef-Expansion oder so etwas.
Das erste \noexpand verschwindet, verhindert aber, dass das zweite \noexpand
expandiert wird. Das zweite \noexpand bleibt also da -äh- und \relaxt. Das
dritte \noexpand verschwindert, verhindert aber, dass das aktive Character-Token
expandiert wird, welches das "ö" repräsentiert. Das "ö" "\relaxt" also auch.
Nach der ersten \edef-Expansion hat man also das zweite \noexpand und das "ö"
übrig, also die Sequenz \noexpand ö.
Wenn da jetzt eine zweite \edef-Expansion oder so etwas drüber läuft,
verschwindet das übrige \noexpand und verhindert, dass das ö expandiert wird.
Mit den drei \noexpand hat man also bei zwei aufeinanderfolgenden Expansions-
Triggern das Expandieren des ö verhindert.
>> In UTF-8 wird das Zeichen "ö" durch eine Sequenz aus mehreren Bytes codiert,
>> von denen die 8-Bit-Engine aber jedes für sich genommen als (aktives)
>> Character-Token tokenizen wird. In 8-Bit Engines werden die einzelnen Bytes
>> eines Multi-Byte-UTF-8-Character einzeln auf (aktive) Character-Token
>> abgebildet. Der Multi-Byte-Character wird also durch eine Sequenz an (aktiven)
>> Character-Token repräsentiert.
>
> War das schon immer so?
Wie man es nimmt. TeX und LaTeX gibt es länger als es Unicode und UTF-8 als
Variante, Unicode in Bytes zu codieren, gibt.
Sonst hätte Knuth vielleicht nicht ASCII als internes Character-Repräsentierungs-
Schema (im TeXbook heisst es "internal character representation scheme", weiss
auf die Schnelle nicht, wie man das adäquat übersetzen könnte) genommen. ;-)
Die Sache mit dem Abbilden der einzelnen Bytes eines Multi-Byte-UTF-8-Character
einzeln auf (aktive) Character-Token bei 8-Bit-Engines hat man in Hinblick
auf das Aktiv-Machen der jeweiligen Character dem inputenc-Paket zu verdanken.
Ob das im inputenc-Paket immer schon so implementiert war, kann ich nicht sagen,
aber als ich mich das erstenmal mit der UTF-8-Option des inputenc-Pakets näher
befasste, war das schon so. Ich kenne es also nur so. Mir fällt auf die Schnelle
auch nicht ein, wie man es auf einer 8-Bit-Engine anders effizient machen könnte.
Eine 8-Bit Engine fasst halt jedes Byte der TeX-Input-Datei als ein einzelnes
Zeichen des Input auf. Dabei, was TeX beim Einlesen der TeX-Input-Datei wegen
eines Zeichens macht, gibt es mehrere Möglichkeiten.
Im Prinzip folgende:
- Es kann sein, dass das Zeichen nicht getokenized wird, zB wenn es einen
Kategoriecode hat demuzfolge es ignoriert werden soll oder einen Kommentar
einleiten soll oder dergleichen.
- Wenn das Zeichen den Kategoriecode 0 hat (normalerweise ist nur dem
Backslash dieser Kategoriecode zugewiesen), fängt TeX an, Zeichen einzusammeln,
die den Namen eines Kontrollsequenz-Tokens bilden.
- Wenn TeX gerade Zeichen für den Namen eines Kontollsequenz-Token einsammelt,
kann es sein, dass das Zeichen als Teil des Namens des besagten Kontrollsequenz-
Tokens aufgefasst wird.
- Wenn TeX gerade kein Zechen für den Namen eines Kontollsequenz-Token einsammelt,
kann es sein, dass TeX ein explizites Character-Token an den Token-Stream
anfügt. Das ist dann ein Token, das in eine der für Character-Token möglichen
elf Kategorien gehört und einen Character-Code hat, der in den meisten Fällen
der Nummer des Zeichens im internes Character-Repräsentierungs-Schema der
TeX-Enine entspricht. In den meisten Fälllen, denn Input-Character mit
Kategoriecode 10(space) ergeben beim Tokenizen immer Character-Token der
Kategorie 10 mit Character-Code 32. (Kategoriecode 10 wird auch "space"
genannt, und 32 ist die ASCII-Nummer des Leerzeichens.)
Die elf möglichen Kategorien sind:
1 = Begin grouping, normally {
2 = End grouping, normally }
3 = Math shift, normally $
4 = Alignment tab, normally &
6 = Parameter, normally #
7 = Superscript, normally ^
8 = Subscript, normally _
10 = Space, normally <space> and <tab>
11 = Letter, normally only contains the letters a,...,z and A,...,Z. These characters can be used in command names
12 = Other, normally everything else not listed in the other categories
13 = Active character, for example ~
Die folgenden fünf Kategorie-Codes implizieren, dass Zeichen in der TeX-Input-Datei,
die diese Kategorie-Codes haben, nicht die Entstehung von expliziten Character-Token
bewirken, sondern sich anderweitig auf das Verhalten des Lese-Apparatus auswirken.
0 = Escape character, normally \
5 = End of line, normally <return>
9 = Ignored character, normally <null>
14 = Comment character, normally %
15 = Invalid character, normally <delete>
Im Gegensatz zu traditionellen 8-Bit-TeX-Engines, die jedes Byte einer
TeX-Input-Datei einzeln für ein (einziges) in der TeX-Input-Datei stehendes
Zeichen halten, gehen LuaTeX- und XeTeX-basierte Engines von vorneherein davon
aus, dass TeX-Inoput-Dateien in UTF-8 codiert sind, und fassen deshalb nicht
unbedingt nur ein Byte, sondern auch mal mehrere Bytes der .TeX-Input-
Datei, sofern sie zu einem Multi-Byte-UTF-8-Character gehören, als ein
einziges Zeichen auf.
>> ( Der Umstand, dass aktiv gemachte Zeichen beim Einlesen und Tokenizen einer
>> Eingabedatei nicht Teil des Namens eines Kontrollsequenz-Tokens sein können,
>> TeX aber zB beim Laden des inputenc-Pakets die .sty-Datei richtig in
>> Kontroll-Sequenz-Token und Character-Token aufdröseln muss, impliziert
>> unter anderem folgendes:
>>
>> 1. Im Gegensatz zu LuaTeX- oder XeTeX-basierten Engines, die von sich aus
>> davon ausgehen, dass Eingabedateien UTF-8-codiert sind, kann man bei
>> 8-Bit-Engines allenfalls solche Zeichen als Komponenten von Namen von
>> Kontroll-Sequenz-Token verwenden, die im ASCII-Encoding kodierbar sind.
>> Bei LuaTeX- oder XeTeX-basierten Engines gilt diese Einschränkung nicht.
>
> Gilt das auch, wenn ich `\csname ... \endcsname` verwende?
Da kommt es drauf an, wie man die Frage auffasst.
a)
\csname..\endcsname dient dazu, eine Sequenz von expliziten Character-_Token_(!)
zusammenzusammeln, also eine Sequenz von diesen beim Tokenizen entstehenden
Kategorie/Character-Code-Paaren, wobei expandierbare Token expandiert
werden, und die Character-Codes dieser Character-_Token_ als Komponenten
des Namens eines Kontollsequenz-Token aufzufassen, welches anstelle des
\csname...\endcsname-Ausdrucks in den Token-Stream platziert wird.
\csname..\endcsname wird in dem Stadium der Verarbeitung der Eingabe
ausgeführt, in dem das Expandieren stattfindet. Expandieren und Makros
und dergleichen bezieht sich immer auf bereits vorliegende Token.
Insofern als Einlesen und Tokenizen längst erledigt sind wenn
\csname..\encsname ausgeführt wird, ist die Frage also obsolet.
b)
Das variiert von Encoding zu Encoding.
Wenn man inputenc mit der latin1-Option verwendet, weil die Eingabedatei
in Latin1/ISO-8859-1 codiert ist, bekommt man mit dem in Latin1 codierten
"ö" innerhalb von \csname..\endcsname Probleme, weil da das Byte f6(hex)=
246(dec), welches in Latin1 das "ö" codiert, als aktives Character-Token
getokenized wird, welches wiederum zu Dingen expandiert, die innerhalb
von \csname..\endcsname nicht vorkommen dürfen und zu
"missing \encsname"-Fehlern führen.
Bein UTF-8-Encoding hat man sich Mühe gegeben, die "missing \encsname"-
Situation nicht aufkommen zu lassen:
Szenarium: UTF-8-codierte Eingabedatei wird unter Verwendung von inputenc
(mit der Option utf8) von einer 8-Bit-Engine verarbeitet.
Jedes Byte eines Multibyte-UTF-8-Character wird einzeln als (aktives)
Character-Token getokenized. Zum Beipiel das Zeichen "ö" besteht in
UTF-8 aus den Bytes c3(hex)=195(dec) und b6(hex)=182(dec). Diese Bytes
werden in 8-Bit-Engines einzeln zu (aktiven) Character-Token, die aber
offenbar alle nur zu Sachen expandieren, die in `\csname..\endcsname`-
Konstrukten erlaubt sind, als da zB wären die Catcode-11- oder -12-Pendants
dieser aktiven Character-Token, sodass die Byte-Sequenz erhalten bleibt.
Bei einer 8-Bit-Engine, die die einzelnen Bytes eines Multi-Byte-UTF-8-
Character einzeln als Character-Token interpretiert, hat man dann halt
im \csname..\endcsname-Konstrukt für einen Multi-Byte-UTF-8-Character
mehrere Character-Token im \csname..\endcsname-Konstrukt stehen,
entsprechend wird der Name des gebildeten Kontrollsequenz-Token nicht
aus einem sondern aus mehreren Zeichen bestehen.
Man kann die Anzahl an Zeichen, aus denen der Name eines
Kontrollsequenz-Tokens besteht, zählen lassen:
% \documentclass etc wird nicht gebraucht.
% Es keinerlei Text in eine .pdf- oder .dvi-Datei gesetzt.
% Es werden nur Sachen auf die Konsole ausgegeben.
% Beendet wird mit \stop statt \end{document}
\newcommand\firstoftwo[2]{#1}%
\newcommand\secondoftwo[2]{#2}%
\newcommand\countchars{\countcharsloop{0}}%
\newcommand\countcharsloop[2]{%
\ifx\relax#2\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
{\message{There are #1 characters}}%
{\expandafter\countcharsloop\expandafter{\the\numexpr#1+1}}%
}%
\expandafter\expandafter\expandafter\countchars
\expandafter\string\csname ö\endcsname\relax
\expandafter\show\csname ö\endcsname\relax
\stop
Hier wird zuerst aus \csname ö\endcsname ein Kontrollsequenz-Token
gebildet. Dann wird da \string drauf losgelassen. Dann wird gezählt,
wieviele Zeichen/Character-Token \string geliefert hat.
In einer UTF-8-Engine wie LuaTeX oder XeTeX bekommt man ein
Kontrollsequenz-Token \ö, sodass \string zwei Zeichen liefert, nämlich
den Backslash und das ö. Entsprechend bekommt man mit LuaTeX oder
XeTeX auf der Konsole die Meldung
"There are 2 characters"
In einer 8-Bit-Engine, bei der (heutzutage ohne dass man dafür noch
großartig eine \usepackage-Zeile braucht) inputenc mit der utf8-Option
verwendet wird, um mit UTF-8 fertig zu werden, sieht die Sache anders
aus:
Wenn die Sache UTF-8 codiert ist, muss man bedenken, dass in UTF-8
das ö aus zwei Byte gebildet wird. Jedes davon wird in der 8-Bit-Engine
einzeln zu einem Character-Token des \csname..\endcsname-Ausdrucks.
Der Name des entstehenden Kontrollsequenz-Tokens besteht also aus zwei
Zeichen.
\string liefert also den Backslash und diese zwei Zeichen, insgesamt
also drei Zeichen, und mit 8-Bit-Engines, die inputenc mit UTF-8-Option
nutzen, bekommt man die Meldung
"There are 3 characters"
Der Name des Kontrollsequenz-Tokens hat in 8-Bit-Engines mehr Zeichen
als in UTF-8-Engines.
Das durch \csname..\endcsname erzeugte Kontrollsequenz-Token heisst in
8-Bit-Engines streng genommen gar nicht \ö.
Da das "ö" in UTF-8 aus den Bytes c3(hex)=195(dec) und b6(hex)=182(dec)
besteht, während das Byte c3(hex)=195(dec) in Latin 1/ISO-8859-1 das Zeichen
à codiert und das Byte b6(hex)=182(dec) in Latin 1/ISO-8859-1 das Zeichen
¶ codiert, liefert die 8-Bit-Engine beim Befehl
\expandafter\show\csname ö\endcsname\relax
auch diese Bytes an die Shell/Konsole.
Wenn die Shell auf Latin1/ISO-8859-1 eingestelllt ist, also ihrerseits
die Bytes, die sie geliefert bekommt, interpretiert als etwas, das in
Latin1/ISO-8859-1 codiert ist, bekommt man auf der Konsole an der
entsprechenden Stelle folgendes zu sehen:
> \ö=\relax.
<recently read> \ö
l.15 \expandafter\show\csname ö\endcsname
\relax
Da ich grade eben beschrieben habe, welche Bytes in Latin1/ISO-8859-1 die
Zeichen à bzw ¶ codieren, kann man auf die von der 8-Bit-Engine an die
Shell gelieferte Byte-Sequenz schliessen.
Das Nette daran ist, dass der Ausgangspunkt ja ein UTF-8-codiertes "ö" war,
welches durch diese beiden Bytes codiert wird.
Stellt man also die Shell auf UTF-8 um, dann interpretiert sie die Bytes,
die von der 8-Bit-Engine geliefert werden als etwas, das in UTF-8 codiert ist
und zeigt einem für die Byte-Sequenz c3(hex)=195(dec) und b6(hex)=182(dec)
ein "ö" an:
> \ö=\relax.
<recently read> \ö
l.15 \expandafter\show\csname ö\endcsname
\relax
Der Umstand, dass die auf UTF-8 eingestellte Shell das entsprechende
Multi-Byte-Zeichen anzeigt, darf einen nicht zu der Annahme verleiten,
innerhalb der 8-Bit-Engine wären die einzelnen Bytes eines UTF-8-Multibyte-
Character auf Token-Ebene irgendwie zu einem einzelnen Character-Token oder
zu einem einzelnen zum Namen eines Kontrollsequenz-Token gehörenden Zeichen
zusammengefasst.
In der 8-Bit-Engine wird bei einem UTF-8-Multibyte-Character jedes Byte
für sich zu einem Character-Token oder zu einem Zeichen, das Teil des Namens
eines Kontrollsequenz-Tokens ist, und zwischen \csname..\endcsname wird
jedes einzelne aus einem Byte eines UTF-8-Multibyte-Character entstandene
Character-Token zu einem zum Namen des zu bildenden Kontrollsequenz-Token
gehörenden Zeichen.
> Also Hände weg von EBCDIC und Co. (Gut, das war schon immer ein guter
> Rat. ;-)
Knuth geht in seinem TeXBook davon aus, dass diejenigen,
die TeX auf einer Maschine installieren, dabei die Routinen fürs
Konvertieren vom Encoding der Computerplattform zum TeX-intern
verwendeten ASCII-Encoding an die lokalen Gegebenheiten
anpassen. ;-)
Unter MiKTeX und TeXLive könnte man das zB über die tcx-files zur
Character-Translation machen. Dann könnte man EBCDIC und Co ohne
inputenc einlesen und schreiben. ;-)
Aber dann hätte man ein Problem wenn man zB parallel zu den
Character-Translation-Files inputenc verwenden möchte, um
bei einer in EBCDIC codierten Haupt-Datei via \input Dateien
zu laden, die anders codiert sind. ;->
In TeXbook, Appendix C: Character Codes werden gewissermaßen
Minimalanforderungen für den Eingabezeichensatz formuliert:
| A person who implements TeX on computer systems that do not
| have 95 externally representable symbols should adhere to the
| following guidelines: (a) Stay as close as possible to the
| ASCII conventions. (b) Make sure that codes ́ 041 – ́ 046 ,
| ́ 060 – ́ 071 , ́ 141 – ́ 146 , and ́ 160 – ́ 171 are present
| and that each unrepresentable internal code < ́ 200 leads to
| a representable code when ́ 100 is added or subtracted;
| then all 256 codes can be input and output. (c) Cooperate
| with everyone else who shares the same constraints, so that
| you all adopt the same policy. (See Appendix J for
| information about the TeX Users Group.)
In diesem Anhang C schimmern alte Pionier-Zeiten durch, in
denen Computer noch was für Rechenzentren waren, in denen
Wissenschaftler/innen ihre Standards für ihr Rechenzentrum/ihre
Fakultät entwickelten, weil es noch nichts Einheitliches gab.
> [excelente Erklärung]
> Wäre das nicht ein Beitrag in der TeXnischen Komödie wert?
Nein. Mein Geschwafel ist noch viel zu Kuddel-Muddel-mäßig.
Um das so auf die Reihe zu bekommen, dass eine Veröffentlichung
in einer Zeitschrift vielleicht verantwortbar wäre, müsste ich
noch einiges an Zeit investieren.
Und dann gäbe es bei den Leser/inne/n vermutlich im Wesentlichen
zwei Zielgruppen:
1. Diejenigen, die das alles schon lange wissen.
2. Diejenigen, die es nicht interessiert.
Vielleicht eine kleine Randgruppe Interessierter, die das noch
nicht alles wissen. Aber da halte ich Kommunikation in einem
Medium wie de.comp.text.tex/comp.text.tex oder TeX LaTeX StackExchange
für besser, weil nachgefragt werden kann und außerdem die
Regulars der Leserschhaft ein schnell eingreifendes Korrektiv
darstellen können.
Mit freundlichem Gruß
Ulrich