
Ich würde gerne wissen, ob es ein Skript gibt, das eine .tex
Datei liest und jede Instanz eines nicht standardmäßigen TeX-Befehls durch das ersetzt, was es ersetzt. Ich bin nicht sicher, ob klar ist, was ich will, aber lassen Sie mich ein Beispiel geben:
Angenommen, dieEingangIst:
\documentclass{amsart}
\usepackage{amsmath,amssymb}
\newcommand{\N}{\mathbb{N}}
\DeclareMathOperator{\End}{End}
\begin{document}
In this lecture we'll study the ring of Endomorphisms of an Abelian group $A$.
Let's denote this ring by $\End(A)$. Throughout the lecture, $\N$ will denote
the set of natural numbers.
\end{document}
Dann eine wünschenswerteAusgabeeines solchen Skripts ist:
\documentclass{amsart}
\usepackage{amsmath, amssymb}
\begin{document}
In this lecture we'll study the ring of Endomorphisms of an Abelian group $A$.
Let's denote this ring by $\operatorname{End}(A)$. Throughout the lecture,
$\mathbb{N}$ will denote the set of natural numbers.
\end{document}
PS:Ich glaube, ich habe etwas in dieser Art schon einmal gesehen, aber ich erinnere mich weder an den Ort noch an ein Schlüsselwort, das ich bei Google aufrufen könnte.
Das wollte ich eigentlich schreiben, alle Antworten hier sind super, aber ich habe 2 bei 4 verzählt. :(
Antwort1
Ich wurde gezwungen durch dieTeX.sx-Chatroom-Mafiaum meine schöne, fehlerhafte, schreckliche, traumatische, post-apokalyptische Arme-Leute-Implementierung eines Ersatzskripts zu posten.
:)
Nun, leider wird dies keine TeX-Antwort sein. :)
Hier ist mein bescheidener Versuch mit einer Skriptsprache, die ich schrecklich beherrsche.
(Ich schaue dich an, Python!)
import re
import sys
if len(sys.argv) != 3:
print('We need two arguments.')
sys.exit()
inputHandler = open(sys.argv[1], 'r')
mathDictionary = {}
commandDictionary = {}
print('Extracting commands...')
for line in inputHandler:
mathOperator = re.search('\\\\DeclareMathOperator{\\\\([A-Za-z]*)}{(.*)}', line)
if mathOperator:
mathDictionary[mathOperator.group(1)] = mathOperator.group(2)
newCommand = re.search('\\\\newcommand{\\\\([A-Za-z]*)}{(.*)}', line)
if newCommand:
commandDictionary[newCommand.group(1)] = newCommand.group(2)
inputHandler.seek(0)
print('Replacing occurrences...')
outputHandler = open(sys.argv[2],'w')
for line in inputHandler:
current = line
for x in mathDictionary:
current = re.sub('\\\\DeclareMathOperator{\\\\' + x + '}{(.*)}', '', current)
current = re.sub('\\\\' + x + '(?!\w)', '\\operatorname{' + mathDictionary[x] + '}', current)
for x in commandDictionary:
current = re.sub('\\\\newcommand{\\\\' + x + '}{(.*)}', '', current)
current = re.sub('\\\\' + x + '(?!\w)', commandDictionary[x], current)
outputHandler.write(current)
print('Done.')
inputHandler.close()
outputHandler.close()
Jetzt nennen wir es einfach:
$ python myconverter.py input.tex output.tex
Extracting commands...
Replacing occurrences...
Done.
input.tex
\documentclass{amsart}
\usepackage{amsmath,amssymb}
\newcommand{\N}{\mathbb{N}}
\DeclareMathOperator{\End}{End}
\begin{document}
In this lecture we'll study the ring of Endomorphisms of an Abelian group $A$.
Let's denote this ring by $\End(A)$. Throughout the lecture, $\N$ will denote
the set of natural numbers.
\end{document}
output.tex
\documentclass{amsart}
\usepackage{amsmath,amssymb}
\begin{document}
In this lecture we'll study the ring of Endomorphisms of an Abelian group $A$.
Let's denote this ring by $\operatorname{End}(A)$. Throughout the lecture, $\mathbb{N}$ will denote
the set of natural numbers.
\end{document}
Einschränkungen:
- Es ist mein Code, also Vorsicht!
:)
- Es funktioniert nur mit
\DeclareMathOperator{...}{...}
und\newcommand{...}{...}
. - Für werden keine optionalen Argumente unterstützt
\newcommand
. - Die Deklaration darf nur aus einer Zeile bestehen.
- Bitte gleichmäßige geschweifte Klammern.
:)
Ich weiß, dass reguläre Ausdrücke nicht zum Parsen von TeX geeignet sind, aber für sehr einfache Ersetzungen sollten sie funktionieren.
Hier ist eine schöne Lektüre über Regex. Viel Spaß.:)
Antwort2
Durch Zufall stieß ich aufde-macro
, ein Python-Skript für diesen Zweck. Es ist in TeX Live enthalten.
Einschränkung: Es betrifft nur \newcommand
, \renewcommand
, \newenvironment
und \renewenvironment
. Mit Sternchen markierte Versionen und optionale Argumente werden nicht berücksichtigt. Das folgende Zitat stammt ausdiese Antwort von Willie Wong auf eine andere Frageund enthält weitere Einzelheiten zu den Einschränkungen:
Auf Anregung von Torbjørn T. und cfr habe ich mir das
de-macro
Paket genauer angesehen. Es funktioniert bis zu einem gewissen Grad. Die folgenden Einschränkungen sind zu beachten:
Anders als in der Dokumentation vorgeschlagen, erstellt die von mir installierte Version die Datenbankdatei
<filename>
statt<filename>.db
. Allerdings testet sie offensichtlich nach<filename>.db
als Namen für die Definitionsdatenbank. In ihrer aktuellen Version wird die Definitionsdatenbank also bei jedem einzelnen Durchlauf von Grund auf neu erstellt. Bei kleinen Dokumenten ist das kein Problem. Bei größeren Dokumenten sollte man die Datenbank nach kopieren (nicht verschieben!), um<filename>.db
von einer möglichen Beschleunigung zu profitieren.Es müssen noch einige Fehler behoben werden. Gelegentlich verstümmelt es Präambeln, indem es falsche Elemente
}
in den Code einfügt. Ich habe den Grund dafür oder den Auslöser/MWE dafür noch nicht gefunden. Die kleinen Testfälle, an denen ich es ausprobiert habe, haben diesbezüglich alle gut funktioniert.Sehr wichtig: Wie in der Dokumentation beschrieben, müssen alle Definitionen, die Sie austauschen möchten, in einem separaten Paket mit der Endung enthalten sein
-private.sty
. In der Hauptdatei.tex
muss dieses Paket verwendet werden.Ebenfalls sehr wichtig: Das Programm verarbeitet
\newcommand
und\renewcommand
, aber nicht die mit Sternchen versehene Variante\newcommand*
(obwohl dies, denke ich, durch eine leichte Änderung des regulären Ausdrucks im Python-Code behoben werden kann). Aus diesem Grundmein erster Versuchfehlgeschlagen. (Ich verwende immerdie mit Sternchen markierte Variante, da ich gelernt habe, dass dies die beste Vorgehensweise ist.)Auch sehr wichtig: Nach dem Entfernen der Sterne hat das Programm einen Fehler ausgegeben. Wie ich schließlich herausfand, liegt das an meiner Angewohnheit,
\newcommand\cmdname{<replacement}
stattdessen zu schreiben\newcommand{\cmdname}{<replacement>}
. Dieses zusätzliche Klammernpaar ist für die Analyse wichtig!Schließlich, sehr enttäuschend für mich, das Programmkann optionale Argumente nicht verarbeiten.
\newcommand{\cmdname}[2]{blah #1 blah #2}
funktioniert einwandfrei,\newcommand{\cmdname}[2][nothing]{blah #1 blah #2}
löst aber eine Ausnahme aus.Das Problem mit dem Stern und den Klammern kann ich leicht beheben/umgehen, indem ich meine Makrodefinitionen (die, wie Sie sich erinnern, am Ende sowieso weggeworfen werden, da dies der Sinn dieser Übung ist) ohne die Sterne neu schreibe und die zusätzlichen Klammern hinzufüge.
Das Problem mit der Verarbeitung optionaler Argumente macht das Programm für mich derzeit allerdings etwas weniger nützlich. Ich kann es im Moment umgehen, indem ich die optionalen und nicht optionalen Befehle in zwei separate aufteile. Vielleicht werde ich, wenn ich in Zukunft etwas Zeit habe, versuchen, Unterstützung dafür hinzuzufügen, nachdem ich die Logik des ursprünglichen Python-Skripts herausgefunden habe.
Antwort3
Hier ist ein perl
Skript, das die gleiche Aufgabe erfüllt. Es hat die gleichen Einschränkungen wie Paulos Code, funktioniert aber in Ihrem Testfall gut. Ich bezweifle nicht, dass es verbessert werden könnte:)
So verwenden Sie es
perl replacenewcommands.plx myfile.tex
die an das Terminal ausgibt, oder
perl replacenewcommands.plx myfile.tex > outputfile.tex
die Ausgabe erfolgt anoutputfile.tex
replacenewcommands.plx
#!/usr/bin/perl
use strict;
use warnings;
# for newcommands
my @newcommandmacro=();
my %newcommandcontent=();
# for DeclareMathoperator
my @declaremathoperator=();
my %declaremathoperatorcontent=();
# for use as an index
my $macro;
# loop through the lines in the INPUT file
while(<>)
{
# check for
# \newcommand...
# and make sure not to match
# %\newcommand
# which is commented
if($_ =~ m/\\newcommand{(.*)}{(.*)}/ and $_ !~ m/^%/)
{
push(@newcommandmacro,$1);
$newcommandcontent{$1}=$2;
# remove the \newcommand from the preamble
s/\\newcommand.*//;
}
# loop through the newcommands in the
# main document
foreach $macro (@newcommandmacro)
{
# make the substitution, making sure to escape the \
# uinsg \Q and \E for begining and end respectively
s/\Q$macro\E/$newcommandcontent{$macro}/g;
}
# check for
# \DeclareMathOperator...
# and make sure not to match
# %\DeclareMathOperator
# which is commented
if($_ =~ m/\\DeclareMathOperator{(.*)}{(.*)}/ and $_ !~ m/^%/)
{
push(@declaremathoperator,$1);
$declaremathoperatorcontent{$1}=$2;
# remove the \DeclareMathOperator from the preamble
s/\\DeclareMathOperator.*//;
}
# loop through the DeclareMathOperators in the
# main document
foreach $macro (@declaremathoperator)
{
# make the substitution, making sure to escape the \
# uinsg \Q and \E for begining and end respectively
s/\Q$macro\E(\(.*\))/\\operatorname{$declaremathoperatorcontent{$macro}}$1/g;
}
print $_;
}
In Ihrem Testfall
meinedatei.tex (Original)
\documentclass{amsart}
\usepackage{amsmath,amssymb}
\newcommand{\N}{\mathbb{N}}
\newcommand{\mycommand}{something else}
\DeclareMathOperator{\End}{End}
\begin{document}
In this lecture we'll study the ring of Endomorphisms of an Abelian group $A$.
Let's $\N$ denote this ring by $\End(A)$. Throughout the lecture, $\N$ will denote
the set of natural numbers. \mycommand
and \mycommand again
\end{document}
outputfile.tex (neu)
\documentclass{amsart}
\usepackage{amsmath,amssymb}
\begin{document}
In this lecture we'll study the ring of Endomorphisms of an Abelian group $A$.
Let's $\mathbb{N}$ denote this ring by $\operatorname{End}(A)$. Throughout the lecture, $\mathbb{N}$ will denote
the set of natural numbers. something else
and something else again
\end{document}
Antwort4
Ich habe ein Javascript geschrieben, um Makros zu erweitern, die durch \def
, \gdef
, \edef
, \xdef
, \newcommand
, \newcommand*
, \renewcommand
, \renewcommand*
, \DeclareMathOperator
und definiert sind \DeclareMathOperator*
. Sie können es versuchenHier.
function expandMacros(tex) {
function nestBrackets(level) {
var level = level || 5, re = c = "(?:[^\\r\\n\\{\\}]|\\\\[\\{\\}]|\\r?\\n(?!\\r?\\n))*?";
while (level--) re = c + "(?:\\{" + re + "\}" + c + ")*?";
return " *(\\{" + re + "\\}|[^\\{])";
}
function getRegExp(name, macro) {
var num = macro.num, def = macro.def, re = "";
while (num--) re += nestBrackets();
re = "\\" + name + "(?![a-zA-Z\\}])" + re;
return new RegExp(re, "g");
}
function trimString(s) {
return s.replace(/^ +| +$/g, '').replace(/^\{|\}$/g, "");
}
function extractMacros() {
var cs = "\\\\\\w+", re;
// \def, \gdef, \edef and \xdef
re = new RegExp("\\\\[gex]?def\\*? *(" + cs + ") *(#\\d)*" + nestBrackets(), "g");
tex = tex.replace(re, function(match){
var m = arguments;
var macro = {
num: m[2] ? Math.min(m[2].length / 2, 9) : 0,
def: trimString(m[3])
};
macros[trimString(m[1])] = macro;
return "";
});
// \newcommand, \newcommand*, \renewcommand and \renewcommand*
re = new RegExp("\\\\(?:re)?newcommand\\*? *(" + cs + "|\\{" + cs + "\}) *(\\[(\\d)\\])?"
+ nestBrackets(), "g");
tex = tex.replace(re, function(match){
var m = arguments;
var macro = {
num: m[3] || 0,
def: trimString(m[4])
};
macros[trimString(m[1])] = macro;
return "";
});
// \DeclareMathOperator and \DeclareMathOperator* inside amsmath
re = new RegExp("\\\\DeclareMathOperator(\\*?) *(" + cs + "|\\{" + cs + "\}) *"
+ nestBrackets(), "g");
tex = tex.replace(re, function(match){
var m = arguments;
var macro = {
num: 0,
def: "\\operatorname" + m[1] + "{" + trimString(m[3]) + "}"
};
macros[trimString(m[2])] = macro;
return "";
});
}
function replaceMacros() {
var i = 0, m, re, num;
for (name in macros) {
m = macros[name];
re = getRegExp(name, m), num = m.num;
//console.log(re);
tex = tex.replace(re, function(match){
//console.log(arguments);
var args = [], result = m.def, k;
for (k = 1; k <= num; k++) {
args[k] = trimString(arguments[k]);
}
//console.log(args);
for (k = 1; k <= num; k++) {
result = result.replace(new RegExp("#" + k, "g"), args[k]);
}
return result;
});
}
}
var macros = {};
extractMacros();
replaceMacros();
return tex;
}
document.getElementById("run").onclick = function() {
var input = document.getElementById("input"),
output = document.getElementById("output");
output.value = expandMacros(input.value);
}