我有一个 csv 表,我想输入特定单元格。例如,我的 csv 是 project.csv:
, obj, resources, wp
task1.1, 1 , 2k , 1
task2.1, 2 , 3k , 2
task3.1, 3 , 4k , 3
理想情况下,我希望从中输入特定元素,例如通过其列和行名称引用它。例如:
Resources for the first task from Work Package \input{project.csv[task1.1, wp]} are \input{project.csv[task1.1, resources]} USD
答案1
使用datatool
's\DTLfetch{<db name>}{<source col>}{<source val>}{<target col>}
返回与<target col>
其中<source col>
关联<source val>
的值<db name>
(它的作用类似于查找表)。
\documentclass{article}
\begin{filecontents*}[overwrite]{tasks.csv}
task,obj,resources,wp
task1.1,1,2k,1
task2.1,2,3k,2
task3.1,3,4k,3
\end{filecontents*}
\usepackage{datatool}
\begin{document}
% Load the database; later referenced as tasks
\DTLloaddb
[keys={task,obj,resources,wp}]% <options>
{tasks}% <db name>
{tasks.csv}% <filename>
Resources for the first task from Work Package
\DTLfetch{tasks}{task}{task1.1}{wp} %\input{project.csv[task1.1, wp]}
are
\DTLfetch{tasks}{task}{task1.1}{resources} %\input{project.csv[task1.1, resources]}
USD.
\end{document}
请注意,CSV 文件中的空格会被返回,因此这取决于您如何管理输出中的空格(来自\DTLfetch
)。我已删除它们,因为典型的 CSV 导出会排除它们。
答案2
以下是expl3
从头开始的实现。使用
\ReadCSV { <filename> } \command
将.csv
文件读取到<filename>
,并将其存储在 中以供使用\command
,然后使用:
\command{<row>,<column>}
获取与<row>
和对应的项目<column>
(通过名称访问)。
它\command
是可扩展的,因此您可以安全地在标题、章节标题等中使用它。
此外,您还可以使用
\IterateCSVRows \command {<code>}
% and
\IterateCSVCols \command {<code>}
迭代读取的 CSV 文件的行和列。在 中<code>
,#1
是当前迭代的行或列名称,因此您可以在 中使用\command{#1,col}
或。如果嵌套两者,则内部命令必须使用来表示行或列名称。下面的示例显示了如何使用这些命令将 重建为表格。\command{row,#1}
<code>
##1
.csv
\documentclass{article}
\begin{filecontents*}[overwrite]{project.csv}
task , obj, resources, wp
task1.1, 1 , 2k , 1
task2.1, 2 , 3k , 2
task3.1, 3 , 4k , 3
\end{filecontents*}
\ExplSyntaxOn
\tl_new:N \l__augusto_tmpa_tl
\tl_new:N \l__augusto_csv_name_tl
\ior_new:N \l__augusto_csv_ior
\seq_new:N \l__augusto_tmpa_seq
\int_new:N \l__augusto_csv_rows_int
\bool_new:N \l__augusto_csv_read_as_string_bool
\cs_generate_variant:Nn \seq_set_from_clist:Nn { NV }
\cs_generate_variant:Nn \prop_put_if_new:Nnn { cVx }
\cs_new_protected:Npn \augusto_read_csv:NnN #1 #2 #3
{
\bool_set_eq:NN \l__augusto_csv_read_as_string_bool #1
\ior_open:NnTF \l__augusto_csv_ior {#2}
{
\cs_if_exist:NTF #3
{ \msg_error:nnn { augusto } { command-exists } {#3} }
{ \exp_args:Nf \__augusto_read_csv:nN { \cs_to_str:N #3 } #3 }
}
{ \msg_error:nnn { augusto } { file-not-found } {#2} }
}
\msg_new:nnn { augusto } { file-not-found }
{ File~'#1'~not~found. }
\msg_new:nnn { augusto } { command-exists }
{ Command~'#1'~already~defined. }
\cs_new_protected:Npn \__augusto_read_csv:nN #1 #2
{
\NewExpandableDocumentCommand #2 { m }
{ \__augusto_get_csv_item:nn {##1} {#1} }
\tl_set:Nn \l__augusto_csv_name_tl {#1}
\bool_if:NTF \l__augusto_csv_read_as_string_bool
{ \ior_str_get:NN } { \ior_get:NN }
\l__augusto_csv_ior \l__augusto_tmpa_tl
\seq_set_from_clist:NV \l__augusto_tmpa_seq \l__augusto_tmpa_tl
\seq_pop:NN \l__augusto_tmpa_seq \l__augusto_tmpa_tl
\seq_new:c { g__augusto_csv_#1_cols_seq }
\seq_map_inline:Nn \l__augusto_tmpa_seq
{
\seq_new:c { g__augusto_csv_#1_##1_seq }
\seq_gput_right:cn { g__augusto_csv_#1_cols_seq } {##1}
}
\prop_new:c { g__augusto_csv_#1_rows_prop }
\int_zero:N \l__augusto_csv_rows_int
\bool_if:NTF \l__augusto_csv_read_as_string_bool
{ \ior_str_map_inline:Nn } { \ior_map_inline:Nn }
\l__augusto_csv_ior
{
\int_incr:N \l__augusto_csv_rows_int
\seq_set_split:Nnn \l__augusto_tmpa_seq { , } {##1}
\seq_pop:NN \l__augusto_tmpa_seq \l__augusto_tmpa_tl
\prop_put_if_new:cVx { g__augusto_csv_#1_rows_prop }
\l__augusto_tmpa_tl { \int_use:N \l__augusto_csv_rows_int }
\seq_mapthread_function:cNN
{ g__augusto_csv_#1_cols_seq } \l__augusto_tmpa_seq
\__augusto_add_csv_row:nn
}
}
\cs_new_protected:Npn \augusto_map_csv_rows:Nn #1 #2
{ \prop_map_inline:cn { g__augusto_csv_ \cs_to_str:N #1 _rows_prop } {#2} }
\cs_new_protected:Npn \augusto_map_csv_cols:Nn #1 #2
{ \seq_map_inline:cn { g__augusto_csv_ \cs_to_str:N #1 _cols_seq } {#2} }
\cs_new_protected:Npn \__augusto_add_csv_row:nn #1 #2
{
\seq_gput_right:cn
{ g__augusto_csv_ \l__augusto_csv_name_tl _#1_seq }
{#2}
}
\cs_new:Npn \__augusto_get_csv_item:nn #1
{ \__augusto_get_csv_item:wn #1 , \q_nil , \s_stop }
\cs_new:Npn \__augusto_get_csv_item:wn #1 , #2 , #3 \s_stop #4
{
\quark_if_nil:nTF {#2}
{ \msg_expandable_error:nnn { augusto } { invalid-item } {#4} }
{
\exp_args:Nff \__augusto_get_item:nnn
{ \tl_trim_spaces:n {#1} } { \tl_trim_spaces:n {#2} } {#4}
}
}
\cs_new:Npn \__augusto_get_item:nnn #1 #2 #3
{
\exp_args:Ncc \__augusto_get_item:NNnn
{ g__augusto_csv_#3_#2_seq } { g__augusto_csv_#3_rows_prop }
{#1} {#2}
}
\cs_new:Npn \__augusto_get_item:NNnn #1 #2 #3 #4
{
\seq_if_exist:NTF #1
{
\prop_if_in:NnTF #2 {#3}
{ \seq_item:Nn #1 { \prop_item:Nn #2 {#3} } }
{ \use_ii:nn }
}
{ \use_ii:nn }
\use_none:n
{
\msg_expandable_error:nnn
{ augusto } { invalid-item } {#3,#4}
}
}
\msg_new:nnn { augusto } { invalid-item }
{ Invalid~item~'#1'. }
\NewDocumentCommand \ReadCSV { s m m }
{
\IfBooleanTF {#1}
{ \augusto_read_csv:NnN \c_true_bool }
{ \augusto_read_csv:NnN \c_false_bool }
{#2} #3
}
\NewDocumentCommand \IterateCSVRows { m +m }
{ \augusto_map_csv_rows:Nn #1 {#2} }
\NewDocumentCommand \IterateCSVCols { m +m }
{ \augusto_map_csv_cols:Nn #1 {#2} }
\ExplSyntaxOff
\begin{document}
\ReadCSV{project.csv}\project
Resources for the first task from Work Package \project{task1.1, wp}
are \project{task1.1, resources} USD
\begin{tabular}{*4c}
\IterateCSVCols \project { & #1 } \\
\IterateCSVRows \project
{%
#1
\IterateCSVCols \project
{ & \project{#1,##1} }
\\
}
\end{tabular}
\end{document}