我想使用密钥访问记录的值。
假设一个包含两个以逗号分隔的字段的外部文件如下:
A, Cycle A Morning Summer
B, Cycle A2 Night
C, Not a defined 1st cycle
..., .....
Z, Last cycle
使用宏我想传递键值并打印第二列 \printcycle{A01} % 我应该得到“Cycle A morning summer”
我看到了这个例子(歌曲包:如何将 gtab 定义存储在外部文件中?),但我不知道如何从第二个字段中仅检索文本值。我同时获得了键和它的值
\begin{filecontents*}{tabs.txt}
A, Cycle A Morning Summer
B, Cycle A2 Night
Z, Not a defined 1st cycle
\end{filecontents*}
\documentclass{book}
\usepackage{xparse}
\ExplSyntaxOn
\ior_new:N \g_fedelibre_get_tabs_ior
\prop_new:N \g_fedelibre_tabs_prop
\cs_new_protected:Npn \fedelibre_add_to_prop:w #1,#2!!
{
\prop_gput:Nnn \g_fedelibre_tabs_prop {#1} {#2}
}
\ior_open:Nn \g_fedelibre_get_tabs_ior {tabs.txt}
\ior_str_map_inline:Nn \g_fedelibre_get_tabs_ior
{
\fedelibre_add_to_prop:w #1!!
}
\DeclareDocumentCommand{ \fedetab }{ v }
{
\prop_get:NnNTF \g_fedelibre_tabs_prop {#1} \l_tmpa_tl
{
\fedelibre_gtab:nV {#1} \l_tmpa_tl
}
{
\msg_term:n {Maybe~ you~ have~ mispelled~ your~ tab!}
}
}
\cs_set_eq:NN \fedelibre_gtab:nn \textit
\cs_generate_variant:Nn \fedelibre_gtab:nn { nV }
\ExplSyntaxOff
\begin{document}
\fedetab{A}
\end{document}
答案1
您可以使用 读取文件的内容\read
。如果您想将条目放入\pgfkeys
第一个问题中,则需要初始化 pgf 键。您可以通过定义未知处理程序来解决这个问题:
\usepackage{pgfkeys}
\pgfkeys{/cycles/.is family, cycles,
% allow arbitrary unknown keys and set with \pgfkeyssetvalue
.unknown/.code={\pgfkeyssetvalue{\pgfkeyscurrentpath/\pgfkeyscurrentname}{#1}},
}
事实上,这就是 的定义\pgfkeys{/cycles}
。它的作用是设置未知键的值。作为额外奖励,您可以使用 来\pgfkeysifdefined
测试键是否已定义,因此命令\printcycle
可以定义为:
\newcommand\printcycle[1]{% print the key if it is defined and ??? otherwise
\pgfkeysifdefined{/cycles/#1}{\pgfkeysvalueof{/cycles/#1}}{???}%
}
请注意,???
如果不知道密钥(即密钥不在数据文件中),则会打印。
现在剩下的就是读取文件并将数据放入\pgfkeys{/cycles}
。我使用\SplitList
了解析包将输入行拆分A01, Cycle A Morning Summer
为键和它的值,然后为了让键正常工作,会出现一些恼人的扩展问题 - 而且您还必须检查空行和由其组成的行\par
,所以这会使阅读稍微复杂一些。
下面的代码定义了一个\ReadCycles
用于读取数据的宏。MWe 生成:
完整代码如下:
\RequirePackage{filecontents}% write the data file
\begin{filecontents}{cycles.csv}
A01,Cycle A Morning Summer
A02,Cycle A2 Night
AC1,Not a defined 1st cycle
Z01,Last cycle
\end{filecontents}
\documentclass{article}
\usepackage{xparse}% mainly for \SplitList
\usepackage{pgfkeys}
\pgfkeys{/cycles/.is family, cycles,
% allow arbitrary unknown keys and set with \pgfkeyssetvalue
.unknown/.code={\pgfkeyssetvalue{\pgfkeyscurrentpath/\pgfkeyscurrentname}{#1}},
}
\newcommand\printcycle[1]{% print the key if it is defined and ??? otherwise
\pgfkeysifdefined{/cycles/#1}{\pgfkeysvalueof{/cycles/#1}}{???}%
}
% split input line into key-value pair
\NewDocumentCommand{\AddCycle}{ >{\SplitList{,}} m }{%
\AddCycleValue #1
}
% put a key-value pair into \pgfkeys{/cycles}
\newcommand\AddCycleValue[2]{\expandafter\pgfkeys\expandafter{/cycles,#1=#2}}
% \ReadCycles{filename} keys the keys in <filename> into \pgfkeys{/cycles}
\newread\cyclefile% file handler
\def\apar{\par}% \ifx\par won't work but \ifx\apar will
\newcommand\ReadCycles[1]{% read file into [\pgfkeys{/cycles}
\openin\cyclefile=#1% open file for reading
\loop\unless\ifeof\cyclefile% loop until end of file
\read\cyclefile to \cycleline% read line from file
\ifx\cycleline\apar% test for \par
\else%
\ifx\cycleline\empty\relax% skip over empty lines/comments
\else\expandafter\AddCycle\expandafter{\cycleline}%
\fi%
\fi%
\repeat% end of file reading loop
\closein\cyclefile% close input file
}
\ReadCycles{cycles.csv}% read the file
\begin{document}
A01 is \printcycle{A01}
A02 is \printcycle{A02}
Z01 is \printcycle{Z01}
Z02 is \printcycle{Z02}
\end{document}
顺便说一句,为了与 pgfkeys 保持一致,我将在数据文件中使用=
而不是:,
A01=Cycle A Morning Summer
A02=Cycle A2 Night
AC1=Not a defined 1st cycle
Z01=Last cycle
为此,您只需使用\SplitList{=}
上面的宏即可。更好的是,您可以用单个宏替换两个\AddCycle
宏\AddCycleValue
\newcommand\AddCycle[1]{\expandafter\pgfkeys\expandafter{/cycles, #1}}
所以您不再需要xparse
。
答案2
需要进行一些更改。文件应该用\ior_map_inline:Nn
而不是 来读取\ior_str_map_inline:Nn
;此外,读取应该在 的范围之外进行\ExplSyntaxOn
。
\begin{filecontents*}{\jobname.txt}
A, Cycle A Morning Summer
B, Cycle A2 Night
Z, Not a defined 1st cycle
\end{filecontents*}
\documentclass{book}
\usepackage{xparse}
\ExplSyntaxOn
\ior_new:N \g_hpcolos_get_tabs_stream
\prop_new:N \g_hpcolos_tabs_prop
\cs_new_protected:Nn \hpcolos_add_to_prop:n
{
\prop_gput:Nff \g_hpcolos_tabs_prop
{ \clist_item:nn { #1 } { 1 } }
{ \clist_item:nn { #1 } { 2 } }
}
\cs_generate_variant:Nn \prop_gput:Nnn { Nff }
\NewDocumentCommand{\readtitlefile}{m}
{
\ior_open:Nn \g_hpcolos_get_tabs_stream { #1 }
\ior_map_inline:Nn \g_hpcolos_get_tabs_stream
{
\hpcolos_add_to_prop:n { ##1 }
}
\ior_close:N \g_hpcolos_get_tabs_stream
}
\DeclareExpandableDocumentCommand{ \gettitle }{ m }
{
\prop_item:Nn \g_hpcolos_tabs_prop { #1 }
}
\ExplSyntaxOff
\readtitlefile{\jobname.txt}
\begin{document}
\gettitle{A}
\gettitle{B}
\gettitle{Z}
\end{document}
答案3
我倾向于使用datatool
这个:
\RequirePackage{filecontents}
\begin{filecontents}{cycles.csv}
A01, Cycle A Morning Summer
A02, Cycle A2 Night
AC1, Not a defined 1st cycle
..., ...
Z01, Last cycle
\end{filecontents}
\documentclass{article}
\usepackage{datatool}
\DTLloaddb[noheader, keys={key,value}]{cycles}{cycles.csv}
\newcommand{\printcycle}[1]{\DTLfetch{cycles}{key}{#1}{value}}
\begin{document}
A01: \printcycle{A01}
Z01: \printcycle{Z01}
AC1: \printcycle{AC1}
\end{document}