以下场景经常发生在我身上:我有几个输入文件,里面有我自己的宏,主要是数学相关的内容,比如定义等等\esssup
,这些在通常的 ams-math 文件中是没有的。写论文时,我会输入这些集合并使用我喜欢的东西。最后,我将内容剪切复制粘贴到一个文件中,因为出版商通常不喜欢多个 LaTeX 文件等,虽然丑陋,但可以避免许多错误和麻烦(糟糕的经历……)
现在的问题是:是否可以(当然可以)只提取某些巧妙的自动过程(shell 脚本、LaTeX 本身等)实际使用的宏,以便我可以只包含这些宏而不是整个宏集合。同样,这样做的原因是为了尽量减少我自己的定义与出版商预定义内容之间可能发生的冲突。
答案1
好的,这是第一次尝试。它对带有参数的命令有一些问题,可以规避这些问题,尽管这里没有提供。下面的方法适用于大多数情况。
大师们,如果您觉得可以改进的地方,我鼓励您概述出来!
好的,基本上每次调用命令时你都想保存。这让我要求你\savecommand
在第一次遇到命令时调用。这可以通过几种方式实现。我选择让你要求它在命令的声明中。它也可以事后添加。
这意味着您定义的所有命令都具有如下特点:
\newcommand\bfA{\savecommand\bfA%
\mathbf{A}%
}
然后你需要定义你的\savecommand
。一种让你轻松完成这一步的方法是将命令定义保存在文件中。这意味着类似这样:
\makeatletter
% Creta fID for writing the commands
\newwrite\command@file
% Save the commands in `mycommands.tex` (change to your liking)
\immediate\openout\command@file=mycommands.tex
% Define the actual saving command:
\def\savecommand#1{%
% This defines \tmp@ to the contents of the command by gobbling:
% \savecommand#1
\edef\tmp@{\expandafter\expandafter\expandafter\unexpanded\expandafter\expandafter\expandafter{\expandafter\@gobbletwo#1}}
% Check whether the command has been written
\expandafter\ifx\csname command@write\string#1\endcsname\relax\relax%
% Define variable which ensures that the command does not get written upon subsequent calls.
\expandafter\gdef\csname command@write\string#1\endcsname{Written}%
% Write the definition to the file, by the use of detokenize and zap space
\immediate\write\command@file{\string\def\string#1\string{\expandafter\expandafter\expandafter\zap@space\expandafter\detokenize\expandafter{\tmp@} \@empty\string}}%
\fi%
}
\makeatother
好的,这实际上会将命令保存在文件中mycommands.tex
。以及检查它是否已经写入。
上面有一个问题。那就是使用\detokenize
(如果宏中包含多个宏,则需要使用)。它在每个宏后插入一个空格。因此
\newcommand\bfA{\mathbf{A}}
\expandafter\detokenize\expandafter{\bfA} == \mathbf {A}
因此插入了 zap space,它将删除直到 的所有空格\@empty
。这意味着如果您定义带有空格的命令,则必须使用宏\space
。
好的,这是一个最小的完整示例:
\documentclass{article}
\usepackage{amsmath}
\begin{document}
\makeatletter
\newwrite\command@file
\immediate\openout\command@file=mycommands.tex
\def\savecommand#1{%
\edef\tmp@{\expandafter\expandafter\expandafter\unexpanded\expandafter\expandafter\expandafter{\expandafter\@gobbletwo#1}}
\expandafter\ifx\csname command@write\string#1\endcsname\relax\relax%
\expandafter\gdef\csname command@write\string#1\endcsname{Written}%
\immediate\write\command@file{\string\newcommand\string#1\string{\expandafter\expandafter\expandafter\zap@space\expandafter\detokenize\expandafter{\tmp@} \@empty\string}}%
\fi%
}
\makeatother
\newcommand\bfE{\savecommand\bfE%
\mathbf{E}%
}
\newcommand\bfA{\savecommand\bfA%
\mathbf{A}
}
\newcommand\with{\savecommand\with%
\mathbf{A}\text{\space}\mathbf{B}%
}
Use bfE:
$\bfE$
Use bfE again:
$\bfE$
Test with space:
$\with$
\makeatletter
% Close the file
\immediate\closeout\command@file
\makeatother
\end{document}
mycommands.tex
这将创建一个包含以下内容的输出文件:
\newcommand\bfE{\mathbf{E}}
\newcommand\with{\mathbf{A}\text{\space}\mathbf{B}}
应该注意的是,这不适用于带参数的命令。我不太确定如何在不添加大量代码的情况下做到这一点。但这至少是大多数单包含命令的起点。
请注意,\bfA
由于从未调用过,因此不会将其添加到文件中,也不会\bfE
在捕获命令时对其进行两次定义。
编辑
如果您希望通过其他方式执行命令,可以执行以下操作:
\newcommand\mynewcommand[2]{%
\newcommand#1{\savecommand#1#2}%
}
% Or fully independent on the \savecommand:
\newcommand\mynewcommand[2]{%
\expandafter\gdef\csname command@write\string#1\endcsname{%
\immediate\write\command@file{%
\string\newcommand\string#1\string{\expandafter\expandafter\expandafter\zap@space\expandafter\detokenize\expandafter{#2} \@empty\string}%
}%
\expandafter\gdef\csname command@write\string#1\endcsname{\relax}%
}%
\newcommand#1{\csname command@write\string#1\endcsname#2}%
}
这将允许您执行以下操作:
\mynewcommand\bfE{\mathbf{E}}
并执行完全相同的操作。仍然无法处理多个参数。:(
答案2
我编写了这个简单的 C++ 程序来删除文件中所有未使用的宏.tex
。要使用它,首先将所有宏定义放入文件 中a.txt
,然后将整个文档正文(从 开始\begin{document}
)放入 中b.txt
。现在运行该程序。它应该c.txt
只生成正文中使用的那些宏。
//C++11
#include <iostream>
#include <stdlib.h>
#include <fstream>
#include <vector>
using namespace std;
string trim_command(string s){
int a = s.find("\\",1);
int b = s.find("}");
int c = s.find("[");
if ((c < b) && (0 <= c)) b = c;
if((a < s.length()) && (b < s.length()) && (a < b))
return s.substr(a, b-a);
else return "";
}
string trim_def(string s){
int a = s.find("\\def",0);
int b = s.find("\{");
if((a < s.length()) && (b < s.length()) && (a < b))
return s.substr(a+4, b-a-4);
else return "";
}
int main() {
vector<string> v1,v2;
string s;
ifstream fin;
fin.open("a.txt");
while(getline(fin,s)){
string key = trim_def(s);
if (key != "") {
v1.push_back(s);
v2.push_back(key);
}
else{
key = trim_command(s);
if (key!=""){
v1.push_back(s);
v2.push_back(key);
}
}
}
fin.close();
fin.open("b.txt");
string t = "";
while(getline(fin,s)){
t += s;
}
fin.close();
ofstream out;
out.open("c.txt");
int l = t.length();
for(int i=0; i<v2.size(); ++i)
{
int pos = t.find(v2[i]);
if((0 <= pos) && (pos< l))
out << v1[i] << endl;
}
out.close();
return 0;
}