我有一个包含键值对的 CSV 文件,我希望能够将该文件读入 latex,并生成宏来访问这些值。这是我目前的尝试datatool
:
\documentclass{article}
\usepackage{datatool,filecontents}
\newcommand{\loadmap}[2][map]{%
\DTLloaddb{#1}{#2}%
\DTLforeach{#1}{\Key=Key,\Value=Value}{%
\expandafter\def\csname #1\Key\endcsname{\Value}}}
\newcommand{\loadmaptest}[2][map]{%
\DTLloaddb{#1}{#2}%
\DTLforeach{#1}{\Key=Key,\Value=Value}{\Key,\Value\\}}
\begin{document}
\begin{filecontents}{testdata.csv}
Key,Value
firstkey,0.002
secondkey,1.0
thirdkey,4
\end{filecontents}
\loadmap{testdata.csv}
\mapfirstkey,\mapsecondkey,\mapthirdkey\\
\loadmaptest[second]{testdata.csv}
\end{document}
您将看到如下输出:
4,4,4
firstkey,0.002
secondkey,1.0
thirdkey,4
因此,虽然DTLforeach
在直接显示结果时正确地遍历了 CSV 文件,但宏的生成是不正确的。特别是,它正确地生成了宏名称\mapfirstkey
,如 等,但它们的值都是键中的最后一个值。我怀疑这与我错误地扩展宏有关……
有任何想法吗?
答案1
您必须扩展\Value
,否则定义的宏将包含它而不是它的值。
如果您计划在键中使用非字母字符,我建议实施一个\usevar
命令。
\begin{filecontents*}{\jobname.csv}
Key,Value
firstkey,0.002
secondkey,1.0
thirdkey,4
\end{filecontents*}
\documentclass{article}
\usepackage{datatool}
\newcommand{\loadmap}[2][map]{%
\DTLloaddb{#1}{#2}%
\DTLforeach{#1}{\Key=Key,\Value=Value}{%
\expandafter\def\csname#1\Key\expandafter\endcsname\expandafter{\Value}%
}%
}
\newcommand{\usevar}[2]{\csname #1#2\endcsname}
\begin{document}
\loadmap{\jobname.csv}
\usevar{map}{firstkey}, \usevar{map}{secondkey}, \usevar{map}{thirdkey}
\end{document}
借助xparse
和expl3
,可以变得更加高效。
请注意,该\usevar
命令可以使用可选参数,但它仍然完全可扩展。
\begin{filecontents*}{\jobname.csv}
Key,Value
firstkey,0.002
secondkey,1.0
thirdkey,4
\end{filecontents*}
\documentclass{article}
\usepackage{datatool}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\loadmap}{O{map}m}
{
\jlperla_keyvalue_assign:nn { #1 } { #2 }
}
\NewExpandableDocumentCommand{\usevar}{O{map}m}
{
\prop_item:cn { l_jlperla_keyvalue_#1_prop } { #2 }
}
\tl_new:N \l__jlperla_keyvalue_key_tl
\tl_new:N \l__jlperla_keyvalue_Value_tl
\cs_new_protected:Nn \jlperla_keyvalue_assign:nn
{
\prop_clear_new:c { l_jlperla_keyvalue_#1_prop }
\DTLloaddb{#1}{#2}
\DTLforeach{#1}
{
\l__jlperla_keyvalue_key_tl = Key,
\l__jlperla_keyvalue_Value_tl = Value
}
{
\prop_put:cVV
{ l_jlperla_keyvalue_#1_prop }
\l__jlperla_keyvalue_key_tl
\l__jlperla_keyvalue_Value_tl
}
}
\cs_generate_variant:Nn \prop_put:Nnn { cVV }
\ExplSyntaxOff
\begin{document}
\loadmap{\jobname.csv}
\usevar{firstkey}, \usevar[map]{secondkey}, \usevar{thirdkey}
\end{document}
答案2
替换\def
为\edef
:
\documentclass{article}
\usepackage{datatool,filecontents}
\newcommand{\loadmap}[2][map]{%
\DTLloaddb{#1}{#2}%
\DTLforeach{#1}{\Key=Key,\Value=Value}{%
\expandafter\edef\csname #1\Key\endcsname{\Value}}}% <- changed
\newcommand{\loadmaptest}[2][map]{%
\DTLloaddb{#1}{#2}%
\DTLforeach{#1}{\Key=Key,\Value=Value}{\Key,\Value\\}}
\begin{document}
\begin{filecontents}{testdata.csv}
Key,Value
firstkey,0.002
secondkey,1.0
thirdkey,4
\end{filecontents}
\loadmap{testdata.csv}
\noindent
\mapfirstkey,\mapsecondkey,\mapthirdkey\\
\loadmaptest[second]{testdata.csv}
\end{document}
结果:
因为有一条评论:虽然可以在键中使用数字,但您必须在代码中使用 `\csname ...\endcsname。所以我不建议使用以下示例。也许您可以为您的键找到更好的名称。
\documentclass{文章} \usepackage{数据工具,文件内容}
\newcommand{\loadmap}[2][map]{%
\DTLloaddb{#1}{#2}%
\DTLforeach{#1}{\Key=Key,\Value=Value}{%
\expandafter\edef\csname #1\csname Key\endcsname\endcsname{\Value}}}
\newcommand{\loadmaptest}[2][map]{%
\DTLloaddb{#1}{#2}%
\DTLforeach{#1}{\Key=Key,\Value=Value}{\Key,\Value\\}}
\begin{document}
\begin{filecontents}{testdata.csv}
Key,Value
firstkey,0.002
2key,1.0
thirdkey,4
\end{filecontents}
\loadmap{testdata.csv}
\noindent
\mapfirstkey,\csname map2key\endcsname ,\mapthirdkey\\
\loadmaptest[second]{testdata.csv}
\end{document}
结果:
答案3
readarray
一种用 代替 的方法datatool
。
\documentclass{article}
\usepackage{readarray}[2016-11-07]
\usepackage{filecontents}
\begin{filecontents*}{myfile.dat}
Key,Value
firstkey,0.002
secondkey,1.0
thirdkey,4
4key, 77
\end{filecontents*}
\newcounter{mycount}
\newcommand\assigndat[1]{%
\readarraysepchar{,}%
\readdef{#1}\mydata%
\readarray*\mydata\myarray[-,\ncols]%
\setcounter{mycount}{0}%
\whiledo{\themycount<\nrows}{%
\stepcounter{mycount}%
\edef\tmp{\myarray[\themycount,1]}%
\expandafter\expandafter\expandafter\xdef\expandafter\csname\tmp\endcsname{%
\myarray[\themycount,2]}%
}%
}
\newcommand\theKey[1]{\myarray[#1,1]}
\newcommand\theValue[1]{\csname #1\endcsname}
\begin{document}
\assigndat{myfile.dat}
\theKey{1}: \theValue{\theKey{1}} or \theValue{Key}\par
\theKey{2}: \theValue{\theKey{2}} or \theValue{firstkey}\par
\theKey{3}: \theValue{\theKey{3}} or \theValue{secondkey}\par
\theKey{4}: \theValue{\theKey{4}} or \theValue{thirdkey}\par
\theKey{5}: \theValue{\theKey{5}} or \theValue{4key}
\end{document}
加载包后pgffor
,可以一次性循环遍历所有键,如下所示:
\foreach\x in {2,...,\myarrayROWS}{\theKey{\x}: \theValue{\theKey{\x}}\par}