请参见以下示例。我以为这会非常简单。但处理似乎被延迟(或分块进行),从而产生了错误的结果。
\documentclass{article}
\usepackage{filecontents}
\usepackage{datatool}
\begin{document}
\begin{filecontents}{\jobname.csv}
a, A, 100
b, B, 200
c, C, 300
d, D, 400
e, E, 500
f, F, 600
g, G, 700
h, H, 800
i, I, 900
\end{filecontents}
% Load database
\DTLloaddb[noheader]{\jobname}{\jobname.csv}
% Define macros
\DTLforeach{\jobname}{\Tag=Column1, \N=Column2, \V=Column3}{%
\expandafter\def\csname\Tag\endcsname{\N}
\expandafter\def\csname\Tag Value\endcsname{\V}}
\begin{enumerate}
\item \b, \bValue % Should give B, 200
\item \d, \dValue % D, 400
\item \i, \iValue % I, 900
\end{enumerate}
\end{document}
查看产生的结果。
我怎样才能立即处理?
答案1
您需要使用\edef
,否则\N
和\V
将打印分配给它们的最后一个值。
无论如何,使用时应非常小心\def
;例如,您正在重新定义\a
、\b
和\c
,它们是排版重音字母的重要 LaTeX 命令。
我建议采用不同的策略:首先在组内处理数据库并使用\xdef
(全局扩展定义),但使用可以使用“外部”命令恢复的“内部命令”。
\begin{filecontents}{\jobname.csv}
a, A, 100
b, B, 200
c, C, 300
d, D, 400
e, E, 500
f, F, 600
g, G, 700
h, H, 800
i, I, 900
\end{filecontents}
\documentclass{article}
\usepackage{datatool}
\newcommand{\getrowN}[1]{\csname masroor@row@#1@N\endcsname}
\newcommand{\getrowV}[1]{\csname masroor@row@#1@V\endcsname}
% Load database
\DTLloaddb[noheader]{\jobname}{\jobname.csv}
% Define macros
\begingroup
\DTLforeach{\jobname}{\Tag=Column1, \N=Column2, \V=Column3}{%
\expandafter\xdef\csname masroor@row@\Tag@N\endcsname{\N}%
\expandafter\xdef\csname masroor@row@\Tag@V\endcsname{\V}%
}
\endgroup
\begin{document}
\begin{enumerate}
\item \getrowN{b}, \getrowV{b} % Should give B, 200
\item \getrowN{d}, \getrowV{d} % D, 400
\item \getrowN{i}, \getrowV{i} % I, 900
\end{enumerate}
\end{document}
通过使用组,您使用 或 的事实不会\def
在组外传播,并且如果这些命令已经定义,它们将在 处恢复其行为。\Tag
\N
\V
\endgroup
\b
或 则有所不同\c
,因为您在文档中需要它们。这就是使用\getrowN
和\getrowV
来检索值的原因。
答案2
你几乎就成功了。
但...
问题 1:
\Tag
、\N
和的定义\V
将随着 -loop 的每次迭代而被覆盖\DTLforeach
。
因此这些宏需要在循环的每次迭代中展开。
\csname
触发扩展事物(因此在您的示例中触发扩展\Tag
),直到找到匹配的\endcsname
。
但是需要\N
一些\V
额外的步骤来确保在循环的每次迭代中没有标记\N
/没有标记\V
但是有标记的扩展/在定义文本内\N
有标记的扩展。\V
\csname
我稍微修改了你的例子,以表明在触发扩展的基础上,你可以使用\expandafter
它来“欺骗”LaTeX 进行\N
扩展\V
:
\documentclass{article}
\usepackage{filecontents}
\usepackage{datatool}
\begin{document}
\begin{filecontents}{\jobname.csv}
a, A, 100
b, B, 200
c, C, 300
d, D, 400
e, E, 500
f, F, 600
g, G, 700
h, H, 800
i, I, 900
\end{filecontents}
% Load database
\DTLloaddb[noheader]{\jobname}{\jobname.csv}
% Define macros
\DTLforeach{\jobname}{\Tag=Column1, \N=Column2, \V=Column3}{%
\expandafter\def\csname\Tag\expandafter\endcsname\expandafter{\N}%
\expandafter\def\csname\Tag Value\expandafter\endcsname\expandafter{\V}%
}%
\begin{enumerate}
\item \a, \aValue
\item \b, \bValue
\item \c, \cValue
\item \d, \dValue
\item \e, \eValue
\item \f, \fValue
\item \g, \gValue
\item \h, \hValue
\item \i, \iValue
\end{enumerate}
\end{document}
问题 2:
\a
、、和已在 LaTeX 2ε 内核中定义。 - loop可能\b
会覆盖已存在的定义。\c
\d
\i
\DTLforeach
因此我再次修改了你的例子:
- 每个要定义的命令都有名称前缀
MY
。 - 而不是使用
\def
now\newcommand*
。如果覆盖了已经定义的内容,则会引发错误消息。 该宏的用处
\name
如下所示:\name\newcommand{macro}...
→\newcommand\macro...
\name\string{macro}
→\string\macro
\name\name\let{macroA}={macroB}
→\name\let\macroA={macroB}
→\let\macroA=\macroB
\documentclass{article}
\newcommand\exchange[2]{#2#1}
\csname @ifdefinable\endcsname\name{\long\def\name#1#{\romannumeral0\innername{#1}}}%
\newcommand\innername[2]{%
\expandafter\exchange\expandafter{\csname#2\endcsname}{ #1}%
}%
\usepackage{filecontents}
\usepackage{datatool}
\begin{document}
\begin{filecontents}{\jobname.csv}
a, A, 100
b, B, 200
c, C, 300
d, D, 400
e, E, 500
f, F, 600
g, G, 700
h, H, 800
i, I, 900
\end{filecontents}
% Load database
\DTLloaddb[noheader]{\jobname}{\jobname.csv}
% Define macros
\DTLforeach{\jobname}{\Tag=Column1, \N=Column2, \V=Column3}{%
\name\expandafter\newcommand\expandafter*\expandafter{MY\Tag}\expandafter{\N}%
\name\expandafter\newcommand\expandafter*\expandafter{MY\Tag Value}\expandafter{\V}%
}%
\begin{enumerate}
\item \MYa, \MYaValue
\item \MYb, \MYbValue
\item \MYc, \MYcValue
\item \MYd, \MYdValue
\item \MYe, \MYeValue
\item \MYf, \MYfValue
\item \MYg, \MYgValue
\item \MYh, \MYhValue
\item \MYi, \MYiValue
\end{enumerate}
\end{document}
除此之外,我还可以提供一个宏\ExtractKthArg
,如果列表中存在第 K 个参数,则从非分隔参数列表中提取第 K 个参数。
您可以\DTLforeach
为数据库的每一行定义一个宏,该宏扩展为表示该行列中条目的非分隔参数列表,并用于\ExtractKthArg
检索该行的单个元素/列条目:
\documentclass{article}
\makeatletter
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Code for \ExtractKthArg
%%=============================================================================
%% Paraphernalia:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond,
%% \UD@CheckWhetherNull,
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@PassFirstBehindThirdToSecond[3]{#2{#3}{#1}}%
%%-----------------------------------------------------------------------------
%% 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\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%=============================================================================
%% Extract K-th inner undelimited argument:
%%
%% \ExtractKthArg{<integer K>}%
%% {<list of non-delimited args>}%
%% {<tokens in case K is not positive>}%
%% {<tokens in case list has less than K args>}%
%%
%% In case K is not positive:
%% Does deliver <tokens in case K is not positive>.
%% In case K is positive but there is no K-th argument in <list of non-delimited args> :
%% Does deliver <tokens in case list has less than K args>.
%% In case there is a K-th argument in <list of non-delimited args> :
%% Does deliver that K-th argument with one level of braces removed.
%%
%% Due to \romannumeral0-expansion the result is delivered after two
%% expansion-steps/after two "hits" with \expandafter.
%%
%% Examples:
%%
%% \ExtractKthArg{0}{ABCDE}{K not positive}{No Kth Element in list} yields: K not positive
%%
%% \ExtractKthArg{3}{ABCDE}{K not positive}{No Kth Element in list} yields: C
%%
%% \ExtractKthArg{3}{AB{CD}E}{K not positive}{No Kth Element in list} yields: CD
%%
%% \ExtractKthArg{4}{{001}{002}{003}{004}{005}}{K not positive}{No Kth Element in list} yields: 004
%%
%% \ExtractKthArg{6}{{001}{002}{003}}{K not positive}{No Kth Element in list} yields: No Kth Element in list
%%
%%=============================================================================
\newcommand\ExtractKthArg[1]{%
% #1: <integer number K>
\romannumeral0%
\expandafter\UD@ExtractKthArgCheck
\expandafter{\romannumeral\number\number#1 000}%
}%
\newcommand\UD@ExtractKthArgCheck[4]{%
% #1: <Amount of letters "m" corresponding to number K>
% #2: <list of non-delimited args>
% #3: <tokens in case K is not positive>
% #4: <tokens in case list has less than K args>
\UD@CheckWhetherNull{#1}{ #3}{%
\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}{#2}{#4}%
}%
}%
\newcommand\UD@ExtractKthArgLoop[3]{%
% #1: <remaining amount of letters "m">
% #2: <(remaining) list of non-delimited args>
% #3: <tokens in case list has less than K args>
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo#2{}.}{ #3%
}{%
\UD@CheckWhetherNull{#1}{%
\UD@ExtractFirstArgLoop{#2\UD@SelDOm}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#2}%
{\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}}%
{#3}%
}%
}%
}%
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
% #1: <(remaining) list of non-delimited args>
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{\UD@firstoftwo{\expandafter}{} \UD@secondoftwo{}#1}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%% End of code for \ExtractKthArg.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\makeatother
\usepackage{filecontents}
\usepackage{datatool}
\begin{document}
\begin{filecontents}{\jobname.csv}
a, A, 100
b, B, 200
c, C, 300
d, D, 400
e, E, 500
f, F, 600
g, G, 700
h, H, 800
i, I, 900
\end{filecontents}
% Load database
\DTLloaddb[noheader]{\jobname}{\jobname.csv}
\makeatletter
% Define macro for each row:
\DTLforeach{\jobname}{\Tag=Column1, \N=Column2, \V=Column3}{%
\expandafter\@gobble\string{%
\expandafter\UD@PassFirstBehindThirdToSecond\expandafter{\V}{%
\expandafter\UD@PassFirstBehindThirdToSecond\expandafter{\N}{%
\expandafter\UD@PassFirstBehindThirdToSecond\expandafter{\Tag}{%
\UD@secondoftwo{}%
}%
}%
}%
{\expandafter\newcommand\csname Row\Tag%
\expandafter\expandafter\expandafter\endcsname
\expandafter\expandafter\expandafter{\expandafter\@gobble\string}}%
}%
%\expandafter\show\csname Row\Tag\endcsname
}%
\newcommand\GenericGetElementOfRow[4]{%
% #1 = Entry in field "\Tag=Column 1" (, the primary key for rows)
% #2 = Column-Number (beginning with 1)
% #3 = <Tokens in case row is undefined>
% #4 = <Tokens in case row is defined but does not have an element in column #2>
\@ifundefined{Row#1}{%
#3%
}{%
\expandafter\expandafter\expandafter\UD@PassFirstToSecond
\expandafter\expandafter\expandafter{\csname Row#1\endcsname}%
{\ExtractKthArg{#2}}%
{}{#4}%
}%
}%
\newcommand\GetTagofRow[1]{%
% #1 = Entry in field "\Tag=Column 1" (, the primary key for rows)
\GenericGetElementOfRow{#1}{1}{%
% Tokens in case row is undefined:
\nfss@text{\reset@font\bfseries??}%
}{%
% Tokens in case row defined but does not have K-th element:
\nfss@text{\reset@font\bfseries??}%
}%
}%
\newcommand\GetNofRow[1]{%
% #1 = Entry in field "\Tag=Column 1" (, the primary key for rows)
\GenericGetElementOfRow{#1}{2}{%
% Tokens in case row is undefined:
\nfss@text{\reset@font\bfseries??}%
}{%
% Tokens in case row defined but does not have K-th element:
\nfss@text{\reset@font\bfseries??}%
}%
}%
\newcommand\GetVofRow[1]{%
% #1 = Entry in field "\Tag=Column 1" (, the primary key for rows)
\GenericGetElementOfRow{#1}{3}{%
% Tokens in case row is undefined:
\nfss@text{\reset@font\bfseries??}%
}{%
% Tokens in case row defined but does not have K-th element:
\nfss@text{\reset@font\bfseries??}%
}%
}%
\makeatother
\begin{itemize}
\item[\GetTagofRow{a}:] \GetNofRow{a}, \GetVofRow{a}
\item[\GetTagofRow{b}:] \GetNofRow{b}, \GetVofRow{b}
\item[\GetTagofRow{c}:] \GetNofRow{c}, \GetVofRow{c}
\item[\GetTagofRow{d}:] \GetNofRow{d}, \GetVofRow{d}
\item[\GetTagofRow{e}:] \GetNofRow{e}, \GetVofRow{e}
\item[\GetTagofRow{f}:] \GetNofRow{f}, \GetVofRow{f}
\item[\GetTagofRow{g}:] \GetNofRow{g}, \GetVofRow{g}
\item[\GetTagofRow{h}:] \GetNofRow{h}, \GetVofRow{h}
\item[\GetTagofRow{i}:] \GetNofRow{i}, \GetVofRow{i}
\item[\GetTagofRow{j}:] \GetNofRow{j}, \GetVofRow{j}
\end{itemize}
\end{document}