在我的 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
三个\expandafter
s 用于通过扩展它们来删除两个s。然后,在我们的 s 之前\fi
只剩下 ,并且,正如我们已经看到的,扩展为(一步到位)。\@firstofone
{...}
\@firstofone{...}
...
在我们的文档中,...
代表以下内容:
\usection{\Kunde}%
\expandafter\def\expandafter\Test\expandafter{\Kunde}%