
Den Großteil des Textes in UTF-8 statt in TeX-Strings zu bearbeiten, hat viele Vorteile. Es ist nicht nur angenehm für die Augen, Finger und Redakteure, sondern es wird auch sehr einfach, Ihren Text Rechtschreib- und Grammatikprüfungen und anderen Analyseprogrammen zuzuführen. Stellen Sie sich einfach vor, Sie schreiben diesen String
ĄąĆćĘę£łŃńÓóŚś-źŻż
Die Verwendung einfacher Such-/Ersetzungsstrategien kann aus folgenden Gründen katastrophale Folgen haben:
\def\L{\matbb{L}}
Fehler bleiben tief in konvertierten Dateien verborgen. Ganz zu schweigen davon, wie tief die Definition des Zeichens verborgen sein könnte.
Ich verstehe, dass man die Charaktere eindeutig identifizieren kann (wie inHier) und daspandoc,tex4htUndHyperrefBeschäftigen Sie sich auf irgendeiner Ebene mit diesem Problem. Meine Frage ist: Wie machbar wäre es, einen solchen Konverter in TeX selbst zu implementieren?
(hinzugefügt von David)
Eingang:
\documentclass{article}
\newcommand\zzz{hello}
\begin{document}
\L\"{o}\"{o}\c{k} \zzz
\renewcommand\L{LLL}
\renewcommand\"[1]{#1#1}
\renewcommand\c{c}
\L\"{o}\"{o}\c{k} \zzz
\end{document}
In ein Formular mit UTF-8-Textläufen konvertiert, aber Makroverwendung vorhanden:
\documentclass{article}
\newcommand\zzz{hello}
\begin{document}
Łööķ \zzz
\renewcommand\L{LLL}
\renewcommand\"[1]{#1#1}
\renewcommand\c{c}
\L\"{o}\"{o}\c{k} \zzz
\end{document}
Antwort1
Erstens, wenn Sie Eingaben wie ĄąĆćĘę£łŃńÓóŚś-źŻż
in Ihrer .tex
Datei bevorzugen, können Sie diese einfach eintippen (oder einfügen). Sie müssen entweder \usepackage[utf8]{inputenc}
nur pdfTeX verwenden, oder nicht einmal das, wenn Sie eine Unicode-fähige Engine (XeTeX oder LuaTeX) verwenden. Beispielsweise funktioniert das Folgende einfach (wenn es mit kompiliert wurde xelatex
):
\documentclass{article}
\begin{document}
ĄąĆćĘę£łŃńÓóŚś-źŻż
\end{document}
Wenn das Problem darin besteht, dass Sie kein bequemes (oder einprägsames) Tastaturlayout haben, um das einzugeben, und Sie deshalb lieber die TeX-Makros verwenden möchten (aber trotzdem möchten, dass die Datei Zeichen wie die oben genannten enthält), dann ist dies einfach eine Frage der Einrichtung Ihres Editors oder Eingabesystems. Zum Beispiel (vorgeschlagen imKommentarevon Benutzer Loop Space), Emacs kann dies mit M-x set-input-method RET TeX
: Wenn Sie die Tasten \=o
auf Ihrer Tastatur drücken, wird in die Datei Folgendes eingegeben ō
: . Sie müssen nicht unbedingt Emacs verwenden; diese Art von Funktion ist auch in Eingabemethoden wie UIM verfügbar (Beispiel).
Wenn Sie die Datei erstellen, sehe ich daher keinen Grund, TeX selbst für eine solche Konvertierung zu verwenden .tex
: Es wäre besser, von vornherein eine Möglichkeit zu finden, Ihre bevorzugten Zeichen einzufügen.
Die Frage kann jedoch sinnvoll sein, wenn Sie mit einer .tex
Datei arbeiten, die von jemand anderem erstellt wurde (und Sie dürfen die Datei ändern) oder die Sie selbst erstellt haben, bevor Sie diese Präferenz hatten.
Der Hauptvorteil der Verwendung von TeX (anstelle von einfachem Suchen und Ersetzen beispielsweise in Ihrem Editor) ist die Möglichkeit zu erkennen, wann sich die Definitionen von Makros \L
geändert \O
haben. Dies ist auch das in der Frage dargestellte Problem.
Um dieses Problem zu lösen, habe ich die folgende Lösung unter Verwendung der introspektiven (auch bekannt alsreflektierend) Fähigkeiten, die mit LuaTeX kommen: Insbesondere token.get_macro
können wir damit die Definitionen von Makros sehen und den process_input_buffer
Rückruf, mit dem wir jede Eingabezeile untersuchen (und bei Bedarf ändern) können. Die Idee ist:
- Notieren Sie vor Beginn des Textes die „ursprünglichen“ Definitionen aller bekannten Zeichenersetzungsmakros (
\L
,\"
,\c
, usw.). So wissen wir, wann sie neu definiert wurden. - Suchen Sie für jede Zeile der Eingabe nach den Makros, die in der Zeile vorkommen.Überprüfen Sie, ob sich ihre Definitionen nicht geändert haben, und (falls ja) ersetzen Sie sie und ihre Argumente durch die entsprechenden Ersetzungen.
Im Beispiel aus der Frage würde beispielsweise in einer Datei mit dem Namen Folgendes erscheinen mwe.tex
:
\documentclass{article}
\directlua{dofile('rewrite.lua')}
\newcommand\zzz{hello}
\begin{document}
\L\"{o}\"{o}\c{k} \zzz
\renewcommand\L{LLL}
\renewcommand\"[1]{#1#1}
\renewcommand\c{c}
\L\"{o}\"{o}\c{k} \zzz
\end{document}
(beachten Sie die \directlua{dofile(...)}
hinzugefügte Zeile), Sie können Folgendes ausführen lualatex mwe.tex
(einige Zeilen wurden entfernt):
9:41:29:~/tmp% lualatex mwe.tex
This is LuaTeX, Version 1.0.4 (TeX Live 2017)
...
The original definition of #\L# is \TU-cmd \L \TU\L
The original definition of #\c# is \TU-cmd \c \TU\c
The original definition of #\"# is \TU-cmd \"\TU\"
...
Processing line: \begin{document}
--> Rewrote line to \begin{document}
...
Processing line: \L\"{o}\"{o}\c{k} \zzz
--> Rewrote line to Łööķ \zzz
Processing line:
--> Rewrote line to
Processing line: \renewcommand\L{LLL}
^ This line contains a \def or \newcommand or \renewcommand. Not rewriting.
...
Processing line: \L\"{o}\"{o}\c{k} \zzz
--> Rewrote line to \L\"{o}\"{o}\c{k} \zzz
Und Sie finden eine mwe.rewritten.tex
Datei mit:
\newcommand\zzz{hello}
\begin{document}
\relax
Łööķ \zzz
\renewcommand\L{LLL}
\renewcommand\"[1]{#1#1}
\renewcommand\c{c}
\L\"{o}\"{o}\c{k} \zzz
\end{document}
\relax
wo Sie sehen können, dass nur die Ersetzungen stattgefunden haben, die hätten stattfinden sollen. Die Lua-Datei (siehe rewrite.lua
oben), die dies bewirkt, ist:
print('')
rewritten_file = io.open(tex.jobname .. '.rewritten.tex', 'w')
funny_noarg = {
["\\L"] = "Ł",
-- Define similarly for \oe \OE \ae \AE \aa \AA \o \O \l \i \j
}
funny_nonletter = {
['\\"'] = function(c) return c .. "̈" end,
-- Define similarly for \` \' \^ \~ \= \.
}
funny_letter = {
["\\c"] = function(c) return c .. "̧" end,
-- Define similarly for \u \v \H \c \d \b \t
}
orig_defs = {}
function populate_orig_defs()
function set_def(s)
definition = token.get_macro(s:sub(2))
orig_defs[s] = definition
print('The original definition of #' .. s .. '# is ' .. definition)
end
for s, v in pairs(funny_noarg) do set_def(s) end
for s, v in pairs(funny_letter) do set_def(s) end
for s, v in pairs(funny_nonletter) do set_def(s) end
end
populate_orig_defs()
function literalize(s)
-- The string s, with special characters escaped, in a format safe for using inside gsub.
-- https://stackoverflow.com/questions/1745448/lua-plain-string-gsub#comment18401212_1746473
return s:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%0")
end
function replace(s)
print('Processing line: ' .. s)
if s:find([[\def]]) ~= nil or s:find([[\newcommand]]) ~= nil or s:find([[\renewcommand]]) ~= nil then
print(' ^ This line contains a \\def or \\newcommand or \\renewcommand. Not rewriting.')
rewritten_file:write(s .. '\n')
return nil
end
for k, v in pairs(funny_noarg) do
-- followed by a nonletter. TODO: Can use the catcode tables.
if token.get_macro(k:sub(2)) == orig_defs[k] then
s = s:gsub(literalize(k) .. '([^a-zA-Z])', function(capture) return v .. capture end)
end
end
for k, v in pairs(funny_letter) do
-- followed by a letter inside {}. TODO: Can use the catcode tables, also can support \c c, for example.
if token.get_macro(k:sub(2)) == orig_defs[k] then
s = s:gsub(literalize(k) .. '{(.)}', v)
end
end
for k, v in pairs(funny_nonletter) do
-- followed by a letter inside {}. TODO: We could also support \"o for example.
if token.get_macro(k:sub(2)) == orig_defs[k] then
s = s:gsub(literalize(k) .. '{(.)}', v)
end
end
print(' --> Rewrote line to ' .. s)
rewritten_file:write(s .. '\n')
return nil
end
luatexbase.add_to_callback('process_input_buffer', replace, 'Replace some macros with UTF-8 equivalents')
Da es sich hierbei lediglich um einen Proof of Concept und nicht um ein System in Produktionsqualität handelt, habe ich einige Abkürzungen gewählt, die Sie ausfüllen können, wenn Sie diesen Ansatz weiterverfolgen möchten:
- Es wurden nur die Unicode-Äquivalente für einige der Akzent- oder Sonderzeichenmakros von TeX aufgelistet.
\documentclass{article}
Sie müssen die Zeile (und eigentlich alles, was Sie vor der Zeile haben ) erneut einfügen\directlua{dofile(…)}
. (Zum Spaß können Sie versuchen, die Zeile zu verschiebenVor\documentclass
und sehen Sie, was passiert.)- Wahrscheinlich möchten Sie diese Zeile nach allen Zeilen haben
\usepackage
, vielleicht am Anfang von\begin{document}
. (Wenn Sie das oben genannte ausprobiert haben, wissen Sie, warum.) - Sie müssen die
\relax
Zeile am Ende entfernen (wir könnten es wahrscheinlich schaffen, dass diese nicht angezeigt wird …) - Es wird angenommen, dass die Eingabedatei die LaTeX-Konvention enthält
\={o}
und nicht\=o
; mit ein paar zusätzlichen Zeilen könnten wir auch Letzteres unterstützen. Gleiches gilt, wenn wir statt oder usw.\c{k}
haben .\c k
\c {k}
\def
Es ignoriert Zeilen, die oder enthalten, vollständig (ersetzt darin nichts)\newcommand
. Stattdessen könnten wir, wenn wir wollten (wenn die Eingabedatei so schlecht geschrieben wäre!), einfach bis zum Ende von\def
oder was auch immer springen und den Rest verarbeiten.- Es wird davon ausgegangen, dass (um zu wissen, wann eine Steuersequenz wie
\o
endet) die „Buchstaben“ sinda-zA-Z
; Sie können diese Liste ergänzen@
, und tatsächlich könnten wir die genaue Definition von „Buchstabe“ unter dem zu diesem Zeitpunkt aktiven Catcode-Regime verwenden – LuaTeX stellt das auch zur Verfügung.
Beachten Sie, dass Sie, auch wenn Sie Ihre Datei normalerweise mit pdfTeX oder XeTeX kompilieren, LuaTeX nur für diese Konvertierung verwenden und für die konvertierte Datei wieder zu pdfTeX/XeTeX zurückkehren können.