这是一个挑战:
这种构造工作得很好,因为 if 包含在单个写入语句中
\documentclass[a4paper]{book}
\newcommand\test{\noindent test\par}
\let\testA\iffalse
\let\testB\fi
\begin{document}
\addtocontents{toc}{\protect\testA test\par \protect\testB}
\tableofcontents
\chapter{test}
\end{document}
另一方面,下面的代码将无法工作,因为\testA
或者\testB
可能在语句内部执行\@writefile{toc}{...}
,最终导致! Extra }, or forgotten \endgroup
错误。
\documentclass[a4paper]{book}
\newcommand\test{\noindent test\par}
\let\testA\iffalse
\let\testB\fi
\begin{document}
\addtocontents{toc}{\protect\testA}
\addtocontents{toc}{\protect\test}
\addtocontents{toc}{\protect\testB}
\tableofcontents
\chapter{test}
\end{document}
那么有没有一个好的方法可以将这两个不完整的if语句偷运到toc文件中呢?
答案1
\documentclass[a4paper]{book}
\newcommand\test{\noindent test\par}
\DeclareRobustCommand\activateif{%
\let\testA\iffalse
\let\testB\fi}
\DeclareRobustCommand\deactivateif{%
\let\testA\relax
\let\testB\relax}
\begin{document}
\addtocontents{toc}{
\activateif
\protect\testA test\par
\protect\testB
\deactivateif}
\tableofcontents
\chapter{test}
\end{document}
这会使目录中的章节条目消失
\documentclass[a4paper]{book}
\newcommand\test{\noindent test\par}
\DeclareRobustCommand\activateif{%
\let\testA\iffalse
\let\testB\fi}
\DeclareRobustCommand\deactivateif{%
\let\testA\relax
\let\testB\relax}
\begin{document}
\addtocontents{toc}{\activateif}
\tableofcontents
\addtocontents{toc}{\protect\testA}
\chapter{test}
\addtocontents{toc}{\protect\testB}
\addtocontents{toc}{\deactivateif}
\end{document}
答案2
编辑:
此修复加入到 LaTeX 2ε 并且自 2020-02-02 版本起在内核中可用,因此自 TeXLive 2019 起。
此问题在 LaTeX 发布时出现2018/12/01
。在此之前,宏\@writefile
(执行从 到.aux
的写入).toc
基本上执行了\write\tocfile{\unexpanded{<stuff>}}
,因此您编写的不平衡条件没有任何问题。
在上述版本发布后,修复了这个问题被引入,问题归结为:
\let\testB\fi
\iftrue\else
\toks0{\testB}
\fi
\else
TeX 在寻找条件的匹配或时不关心括号的平衡\fi
。在这种情况下,\testB
被视为\fi
,而您只剩下}\fi
。
我认为可以稍微改变一下定义\add@percent@to@temptokena
以避免此类问题:
\documentclass[a4paper]{book}
\newcommand\test{\noindent test\par}
\let\testA\iffalse
\let\testB\fi
\makeatletter
\long\def\add@percent@to@temptokena
#1\protected@file@percent#2\add@percent@to@temptokena
{\ifx!#2!\expandafter\dont@add@percent@to@temptokena\else
\expandafter\do@add@percent@to@temptokena\fi{#1}}
\long\def\dont@add@percent@to@temptokena#1{%
\@temptokena\expandafter{#1}}
\begingroup
\catcode`\%=12
\catcode`\^^A=9
\long\gdef\do@add@percent@to@temptokena#1{%
\@temptokena\expandafter{#1%^^A
}}
\endgroup
\makeatother
\begin{document}
\addtocontents{toc}{\protect\testA}
\addtocontents{toc}{\protect\test}
\addtocontents{toc}{\protect\testB}
\tableofcontents
\chapter{test}
\end{document}
答案3
似乎在较新的 LaTeX2e 版本中,\add@percent@to@temptokena
添加了一些机制\@writefile
:
> \@writefile=\long macro:
#1#2->\@ifundefined {tf@#1}\relax {\add@percent@to@temptokena \@empty #2\protec
ted@file@percent \add@percent@to@temptokena \immediate \write \csname tf@#1\end
csname {\the \@temptokena }}.
l.19 \show\@writefile
> \add@percent@to@temptokena=\long macro:
#1\protected@file@percent #2\add@percent@to@temptokena ->\ifx !#2!\@temptokena
\expandafter {#1}\else \@temptokena \expandafter {#1% }\fi .
l.20 \show\add@percent@to@temptokena
我认为这\ifx !#2!...
是一个空检查,用于检测的\@writefile
第二个参数是否包含标记\protected@file@percent
。(如果是,则第一个未嵌套在花括号中的此类标记应被替换为,%
并且其后面的所有内容都应被删除。)
- 我不建议尝试写一个后面跟着感叹号的百分号,如下所示:
\@writefile{toc}{something\protected@file@percent! This is a very important comment.}
- 如果
\add@percent@to@temptokena
的第一个参数包含不平衡的\else
/\fi
(就像您的情况一样),这些将错误地\ifx
匹配
\ifx !#2!\@temptokena\expandafter{#1}
。
我预见到这个\add@percent@to@temptokena
东西会破坏许多依赖于能够编写无与伦比的或通过的用户编写\if..
的\else
自制\fi
代码\@writefile
。
我可能会建议如下:
\documentclass[a4paper]{book}
\begingroup
\makeatletter
\catcode`\&=14 %
\catcode`\%=12 &
\@firstofone{&
\endgroup
&&-----------------------------------------------------------------------------
&& Change \add@percent@to@temptokena:
&&.............................................................................
\long\def\add@percent@to@temptokena#1\protected@file@percent#2\add@percent@to@temptokena{&
\ifcat A\detokenize{#2}A\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{\@temptokena\expandafter{#1}}{\@temptokena\expandafter{#1% }}&
}&
}%
\newcommand\test{\noindent test\par}
\let\testA\iffalse
\let\testB\fi
\begin{document}
\addtocontents{toc}{\protect\testA}
\addtocontents{toc}{\protect\test}
\addtocontents{toc}{\protect\testB}
\tableofcontents
\chapter{test}
\end{document}
如果你不喜欢 e-TeX- 这个\detokenize
东西:
\documentclass[a4paper]{book}
\begingroup
\makeatletter
\catcode`\&=14 %
\catcode`\%=12 &
\@firstofone{&
\endgroup
&&-----------------------------------------------------------------------------
&& Check whether argument is empty:
&&.............................................................................
&& \UD@CheckWhetherNull{<Argument which is to be checked>}
&& {<Tokens to be delivered in case that argument
&& which is to be checked is empty>}
&& {<Tokens to be delivered in case that argument
&& which is to be checked is not empty>}
&&
&& The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
&& <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{&
\romannumeral0\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}&
{\@firstoftwo\expandafter{} \@firstoftwo}&
}&
&&-----------------------------------------------------------------------------
&& Change \add@percent@to@temptokena:
&&.............................................................................
\long\def\add@percent@to@temptokena#1\protected@file@percent#2\add@percent@to@temptokena{&
\UD@CheckWhetherNull{#2}&
{\@temptokena\expandafter{#1}}&
{\@temptokena\expandafter{#1% }}&
}&
}%
\newcommand\test{\noindent test\par}
\let\testA\iffalse
\let\testB\fi
\begin{document}
\addtocontents{toc}{\protect\testA}
\addtocontents{toc}{\protect\test}
\addtocontents{toc}{\protect\testB}
\tableofcontents
\chapter{test}
\end{document}
顺便一提:
也可以重新定义,\@writefile
只替换第一个 \protected@file@percent
,%
同时保留其他所有内容:
\documentclass[a4paper]{book}
\makeatletter
%%-----------------------------------------------------------------------------
%% Change \@writefile to replace the first non-brace-nested
%% \@protected@file@percent in #2 by %
%%.............................................................................
\@ifdefinable\RemoveTo@protected@file@percent{%
\long\def\RemoveTo@protected@file@percent#1\protected@file@percent{}%
}%
\begingroup
\catcode`\&=14 %
\catcode`\%=12 &
\@firstofone{&
\endgroup
\@ifdefinable\Replace@protected@file@percent{&
\long\def\Replace@protected@file@percent#1\protected@file@percent{&
\@firstoftwo{ }#1%&
}&
}&
}%
\long\def\@writefile#1#2{%
\@ifundefined{tf@#1}\relax{%
\immediate\write\csname tf@#1\endcsname{%
\unexpanded\expandafter{\romannumeral0%
\ifcat A\detokenize\expandafter{%
\RemoveTo@protected@file@percent#2\protected@file@percent
}A\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
{ }%
{\Replace@protected@file@percent.}%
#2%
}%
}%
}%
}%
\makeatother
\newcommand\test{\noindent test\par}
\let\testA\iffalse
\let\testB\fi
\begin{document}
\makeatletter
\addtocontents{toc}{\protect\testA\protected@file@percent{This is my nice comment.}}
\makeatother
\addtocontents{toc}{\protect\test}
\addtocontents{toc}{\protect\testB}
\tableofcontents
\chapter{test}
\end{document}
有无:\detokenize
\unexpanded
\documentclass[a4paper]{book}
\makeatletter
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\@secondoftwo\string{\expandafter
\@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
\@secondoftwo\string}\@firstoftwo\expandafter{} \@secondoftwo}%
{\@firstoftwo\expandafter{} \@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Change \@writefile to replace the first non-brace-nested
%% \@protected@file@percent in #2 by %
%%.............................................................................
\@ifdefinable\RemoveTo@protected@file@percent{%
\long\def\RemoveTo@protected@file@percent#1\protected@file@percent{}%
}%
\begingroup
\catcode`\&=14 %
\catcode`\%=12 &
\@firstofone{&
\endgroup
\@ifdefinable\Replace@protected@file@percent{&
\long\def\Replace@protected@file@percent#1\protected@file@percent{&
\@firstoftwo{}#1%&
}&
}&
}%
\long\def\@writefile#1#2{%
\@ifundefined{tf@#1}\relax{%
\expandafter\UD@CheckWhetherNull\expandafter{\RemoveTo@protected@file@percent#2\protected@file@percent}%
{%
\@temptokena{#2}%
}{%
\@temptokena\expandafter\expandafter\expandafter{\Replace@protected@file@percent.#2}%
}%
\immediate\write\csname tf@#1\endcsname{\the\@temptokena}%
}%
}%
\makeatother
\newcommand\test{\noindent test\par}
\let\testA\iffalse
\let\testB\fi
\begin{document}
\makeatletter
\addtocontents{toc}{\protect\testA\protected@file@percent{This is my nice comment.}}
\makeatother
\addtocontents{toc}{\protect\test}
\addtocontents{toc}{\protect\testB}
\tableofcontents
\chapter{test}
\end{document}