使用键访问外部文件上的记录值

使用键访问外部文件上的记录值

我想使用密钥访问记录的值。

假设一个包含两个以逗号分隔的字段的外部文件如下:

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}

相关内容