将 if 语句写入目录

将 if 语句写入目录

这是一个挑战:

这种构造工作得很好,因为 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

\elseTeX 在寻找条件的匹配或时不关心括号的平衡\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。(如果是,则第一个未嵌套在花括号中的此类标记应被替换为,%并且其后面的所有内容都应被删除。)

  1. 我不建议尝试写一个后面跟着感叹号的百分号,如下所示:
    \@writefile{toc}{something\protected@file@percent! This is a very important comment.}
  2. 如果\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}

相关内容