宏\mytest​

宏\mytest​

在我的 MWE 中,我想通过 更新命令\renewcommand,但实际上datatool却将更新后的命令写入输出。此外,我的\ifthenelse似乎始终为真,因此执行“then”指令,而从不执行(空的)“else”指令。

\documentclass[ngerman]{scrreprt}

\usepackage{babel}
\usepackage{booktabs}
%\usepackage{eurosym}
%\usepackage{isodate}
\usepackage{datatool}
\usepackage{ifthen}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{lmodern}
\usepackage{hyperref}
\usepackage{cleveref}

\newcommand{\usection}[1]{% unnummeriertes Unterkapitel mit Eintrag ins Inhaltsverzeichnis
    \phantomsection % fix für hyperref
    \section*{#1} % unnummeriertes Unterkapitel
    \addcontentsline{toc}{section}{#1} % Eintrag ins Inhaltsverzeichnis
}

\newcommand{\Test}{}

\begin{filecontents}[overwrite]{testfile.csv}
    
    Projekt,    Kunde
    P1,         K1
    P2,         K2
    P3,         K2
    P4,         K1
    
\end{filecontents}

\begin{document}
    
\tableofcontents
    
\DTLloaddb{data}{testfile.csv}

\chapter{Kunden}

\DTLsort[Error]{Kunde}{data}

\DTLforeach%
{data}% database name
{\Kunde=Kunde, \Projekt=Projekt}% assignments
{% what to do at each iteration
    \ifthenelse{ % Conditions
        \equal{\Test}{} % Condition 1
        \OR % ODER Schaltung
        {\NOT{\equal{\Test}{\Kunde}}}} % Condition 2
    {\usection{\Kunde} % then
        {}} % else
    \renewcommand{\Test}{\Kunde} % Test neu definieren
    
    \begin{table}[!h]
        \begin{tabular}{lr}
            \toprule
            Kriterium & Wert\\
            \midrule
            Projekt & \Projekt \\
            \bottomrule
        \end{tabular}
    \end{table}
}
    
\end{document}

答案1

第一个问题是你的花括号没有正确放置:然后条款是\usection{\Kunde} {}别的子句为\renewcommand。因此,\ifthenelse始终执行然后条款和吃别的子句;输入流中剩余内容,对 (空) 和(客户端名称){\Test}{\Kunde}的扩展进行排版。\Test\Kunde

一旦修复了这个问题,你就会遇到第二个问题:在 TeX 执行之后\renewcommand{\Test}{\Kunde},的替换文本\Test一个代币,即控制序列标记\Kunde。这对于“记住”最后看到的客户端毫无用处,因为当下一次迭代开始时,\Test仍然包含唯一的控制序列标记\Kunde(其含义可能已更改),而不是先前看到的客户端的名称,而该客户端现在已无法访问。

我删除了 的使用,ifthen因为\equal测试会递归扩展标记,如果某些客户端名称包含不可扩展的标记,这可能会导致问题。相反,我使用了\ifx

而且,由于\DTLforeach循环不会尝试修改底层数据库,\DTLforeach*因此可以使用,这比更快\DTLforeach

\documentclass{scrreprt}
\usepackage{booktabs}
\usepackage{datatool}

\newcommand{\usection}[1]{% unnummeriertes Unterkapitel mit Eintrag ins Inhaltsverzeichnis
    \csname phantomsection\endcsname % fix for hyperref, if loaded
    \section*{#1}% unnummeriertes Unterkapitel
    \addcontentsline{toc}{section}{#1}% Eintrag ins Inhaltsverzeichnis
}

\newcommand{\Test}{}

\begin{filecontents}[overwrite]{testfile.csv}

    Projekt,    Kunde
    P1,         K1
    P2,         K2
    P3,         K2
    P4,         K1

\end{filecontents}

\makeatletter
\newcommand*{\mytest}{%
  \ifx\Test\empty
    \expandafter\@firstofone
  \else
    \ifx\Test\Kunde
      \expandafter\expandafter\expandafter\@gobble
    \else
      \expandafter\expandafter\expandafter\@firstofone
    \fi
  \fi
}
\makeatother

\begin{document}

\tableofcontents

\DTLloaddb{data}{testfile.csv}

\chapter{Kunden}

\DTLsort[Error]{Kunde}{data}

\DTLforeach*
{data}% database name
{\Kunde=Kunde, \Projekt=Projekt}% assignments
{%
  \mytest{%
    \usection{\Kunde}%
    \expandafter\def\expandafter\Test\expandafter{\Kunde}%
  }%
%
  \begin{table}[!h]
    \begin{tabular}{lr}
        \toprule
        Kriterium & Wert\\
        \midrule
        Projekt & \Projekt \\
        \bottomrule
    \end{tabular}
  \end{table}
}

\end{document}

在此处输入图片描述

\mytest

这个宏的用法是\mytest{...},我们希望它扩展为:

  • ...当这是一个新客户端时(即,要么\Test是空的,要么与不同\Kunde);

  • 否则为空的标记列表。

最简单的方法是将在第一种情况下\mytest扩展为,在第二种情况下扩展为 。请注意, 的扩展将删除 中的外括号,这很重要(否则, 的重新定义不会持续足够长的时间,因为它是局部的)。那么,让我们看看它是如何进行的:\@firstofone\@gobble\@firstofone{...}\Test

\ifx\Test\empty
  \expandafter\@firstofone
\else
  \ifx\Test\Kunde
    \expandafter\expandafter\expandafter\@gobble
  \else
    \expandafter\expandafter\expandafter\@firstofone
  \fi
\fi

如果\Test为空,则第一个测试为真,并且在确定测试结果之后输入流中剩余的内容(在 之前没有任何空格\expandafter):

  \expandafter\@firstofone
\else
  ⟨contents of the else clause⟩
\fi

当 TeX 扩展 时\expandafter,这会扩展\else,从而删除直到最后一个(包括最后一个)的所有内容\fi(因为\ifx测试为真)。然后,在 之前输入流中剩下的{...}\@firstofone,这正是我们想要的,因为在一个扩展步骤之后\@firstofone{...}变成了...

现在,让我们看看当第一个\ifx测试为假时会发生什么。TeX 会跳过标记(尊重\iffoobar...\fi嵌套),直到找到匹配的\else,然后删除此\else。这会在输入流中留下以下内容( 之前没有任何空格\ifx):

  \ifx\Test\Kunde
    \expandafter\expandafter\expandafter\@gobble
  \else
    \expandafter\expandafter\expandafter\@firstofone
  \fi
\fi

我们想要的是,根据是真还是假,保留或\@gobble。假设测试为真(与之前看到的客户端相同)。我们还假设我们没有将第一个放入代码中。然后 TeX 会在输入流中看到在之前的内容:\@firstofone\ifx\Test\Kunde\expandafter\expandafter\expandafter{...}

                                        \@gobble
  \else
    \expandafter\expandafter\expandafter\@firstofone
  \fi
\fi

这根本行不通,因为\@gobble会吃掉下一个标记,也就是\else!然后 TeX 会继续处理剩余的\expandafter\expandafter\expandafter\@firstofone\fi\fi,这很快就会留\@firstofone在输入流中,而不是所需的\@gobble(你马上就会明白为什么)。

因此,我将 放在\expandafter\expandafter\expandafter之前\@gobble。第一个\expandafter跳过第二个并扩展第三个,然后消失 — 这是它的工作。由于第三个\expandafter正在扩展,它跳过\@gobble,扩展以下标记(一个\else),然后消失。 的扩展\else将删除所有标记(考虑\iffoobar...\fi嵌套),直到并包括匹配的\fi: 之后的标记\@firstofone。第一个和第三个\expandafter现在已经扩展,因此已经消失。因此,输入流之前剩余的内容{...}是:

\expandafter\@gobble
\fi

这很简单:TeX 扩展了\expandafter,这导致它在扩展之后消失\fi,而扩展也导致它消失。因此,只剩下\@gobble,然后是我们的{...}代码。这正是我们想要的,因为\@gobble{...}扩展为空标记列表(一步到位)。

让我们退后一步,考虑一个由 表示的任意标记流。如果你仔细想想,经过一个扩展步骤后\expandafter\expandafter\expandafter\onetoken◆变成,其中是 的第一级扩展。经过两个扩展步骤后,因此在输入流中留下,其中是 的第二级扩展。\expandafter\onetoken❶\expandafter\expandafter\expandafter\onetoken◆\onetoken❷

因此,经过两次扩展步骤后,\expandafter\expandafter\expandafter\onetoken◆变为\onetoken,然后是 的第二级扩展。这非常有用,你应该记住它。

\mytest我们的宏的最后一种情况是类似的:如果\ifx\Test\Kunde测试为假,则在跳过直到匹配的 在内的标记之后\else,TeX 在我们的输入流中具有以下标记{...}( 之前没有任何空格\expandafter):

    \expandafter\expandafter\expandafter\@firstofone
  \fi
\fi

三个\expandafters 用于通过扩展它们来删除两个s。然后,在我们的 s 之前\fi只剩下 ,并且,正如我们已经看到的,扩展为(一步到位)。\@firstofone{...}\@firstofone{...}...

在我们的文档中,...代表以下内容:

\usection{\Kunde}%
\expandafter\def\expandafter\Test\expandafter{\Kunde}%

相关内容