后续行动这个很好的答案读取 CSV 文件并保存,我需要以下内容:
A编译停止错误当我尝试索引不存在的行或列时是必需的,因为当发生这种情况时它会经历无休止的编译。
我想获得警告在我的 Latex 编辑器中,当处理的宏
\getValue
具有空值时(即,CSV 文件中访问的单元格为空)。我希望警告和错误都显示相应的文件名和代码行当构建选项
-file-line-error
被启用时。同样的条件也适用于
\getRow
检索时发出警告完全空行,以及編輯中止尝试访问不存在的 CSV 数据或行时出错(例如Parameter
此处)。
\begin{filecontents*}{test.csv}
Third Parameter , 7 , 9 ,
First Parameter , 5 , {foo, bar} ,
Second Parameter , 3 , 6 , 44
Empty Parameter , , ,
\end{filecontents*}
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
% Step 1: reading the file
\ior_new:N \l__diaa_csv_ior
\bool_new:N \l__diaa_csv_str_bool
\seq_new:N \l__diaa_csv_tmp_seq
% str mode (bool/star), key column, label, value columns, file
\NewDocumentCommand \ReadCSV { s O{1} m O{} m }
{
\IfBooleanTF {#1}
{ \bool_set_true:N \l__diaa_csv_str_bool }
{ \bool_set_false:N \l__diaa_csv_str_bool }
\diaa_csv_read:nnnn {#3} {#2} {#4} {#5}
}
% label, key column, value columns, file
\cs_new_protected:Npn \diaa_csv_read:nnnn #1 #2 #3 #4
{
\tl_if_blank:nTF {#3} % Detect number of columns and use 2 to last
{
\ior_open:NnTF \l__diaa_csv_ior {#4}
{
\bool_if:NTF \l__diaa_csv_str_bool
{ \ior_str_get:NN }
{ \ior_get:NN }
\l__diaa_csv_ior \l_tmpa_tl
\ior_close:N \l__diaa_csv_ior
\seq_set_split:NnV \l_tmpa_seq { , } \l_tmpa_tl
\seq_clear:N \l__diaa_csv_tmp_seq
\int_step_inline:nnn { 2 } { \seq_count:N \l_tmpa_seq }
{ \seq_put_right:Nn \l__diaa_csv_tmp_seq {##1} }
}
{ \msg_error:nnn { diaa } { file-not-found } {#4} }
}
{ \seq_set_split:Nnn \l__diaa_csv_tmp_seq { , } {#3} } % explicit columns
\ior_open:NnTF \l__diaa_csv_ior {#4}
{
\prop_new:c { g__diaa_csv_#1_prop }
\__diaa_csv_read:nn {#1} {#2}
\ior_close:N \l__diaa_csv_ior
}
{ \msg_error:nnn { diaa } { file-not-found } {#4} }
}
\msg_new:nnn { diaa } { file-not-found }
{ File~`#1'~not~found. }
\cs_generate_variant:Nn \prop_put:Nnn { cxV }
% label, key column
\cs_new_protected:Npn \__diaa_csv_read:nn #1 #2
{
\bool_if:NTF \l__diaa_csv_str_bool
{ \ior_str_map_inline:Nn }
{ \ior_map_inline:Nn }
\l__diaa_csv_ior
{
\seq_set_split:Nnn \l_tmpa_seq { , } {##1} % split one CSV row
\tl_clear:N \l_tmpa_tl
\seq_map_inline:Nn \l__diaa_csv_tmp_seq
{
\tl_put_right:Nx \l_tmpa_tl { { \seq_item:Nn \l_tmpa_seq {####1} } }
}
\prop_put:cxV { g__diaa_csv_#1_prop }
{ \seq_item:Nn \l_tmpa_seq {#2} }
\l_tmpa_tl
}
}
% Step 2: getting the values
% star → global assignment, macro or tl var, value column, key, label
\NewDocumentCommand \getValue { s m O{1} m m }
{
\IfBooleanTF {#1} { \tl_gset:Nx } { \tl_set:Nx }
#2 { \diaa_csv_item:nnn {#4} {#3} {#5} }
}
% key, value column, label
\NewExpandableDocumentCommand \CSVItem { m O{1} m }
{ \diaa_csv_item:nnn {#1} {#2} {#3} }
\cs_generate_variant:Nn \tl_item:nn { f }
% key, value column, label
\cs_new:Npn \diaa_csv_item:nnn #1 #2 #3
{
\tl_item:fn { \prop_item:cn { g__diaa_csv_#3_prop } {#1} } {#2}
}
% star → global assignment, macro, key, label
\NewDocumentCommand \getRow { s m m m }
{
\prop_get:cnN { g__diaa_csv_#4_prop } {#3} \l_tmpa_tl
\IfBooleanTF {#1} { \cs_gset_nopar:Npx } { \cs_set_nopar:Npx } #2 [ ##1 ]
{
\exp_not:N \str_if_eq:nnTF {##1} { non-empty }
{
\exp_not:N \__diaa_nb_nonempty_items_in_row:nn { 0 }
\exp_not:V \l_tmpa_tl
\exp_not:n { \q_recursion_tail \q_recursion_stop }
}
{ \exp_not:N \tl_item:nn { \exp_not:V \l_tmpa_tl } {##1} }
}
}
\cs_new:Npn \__diaa_nb_nonempty_items_in_row:nn #1#2
{
\quark_if_recursion_tail_stop_do:nn {#2} { \int_eval:n {#1} }
\tl_if_empty:nTF {#2}
{ \__diaa_nb_nonempty_items_in_row:nn {#1} }
{ \__diaa_nb_nonempty_items_in_row:nn { #1 + 1} }
}
\ExplSyntaxOff
\parindent0pt
\begin{document}
\ReadCSV{mydata}{test.csv}
\verb|\getValue\myValue{Second Parameter}{NOTmydata}|\\
A \textbf{compilation-halting error} should show up stating that "NOTmydata" is undefined CSV data.\\
\verb|\getValue\myValue{Non-existent Parameter}{mydata}|\\
A \textbf{compilation-halting error} should show up stating that "Non-existent Parameter" is undefined CSV parameter.\\
\verb|\getValue\myValue{Empty Parameter}{mydata}|\\
A \textbf{Warning} should show up stating that the value stored in \verb|\myValue| is empty.
\end{document}
答案1
由于您需要以相同方式格式化的警告和错误消息,并且不需要可扩展性,因此可以以不可扩展的方式重写代码,这允许您\msg_error:nn(nnnn)
始终使用(而不是\msg_expandable_error:nn(nnnn)
)。(下面是可扩展的旧答案\diaa_csv_item:nnn
,仅供参考。)
我稍微改变了一下 的输出语法\getRow
。当你这样做时
\getRow [*] \<row macro> {<row ID>} {<CSV ID>}
其\<row macro>
定义语法如下:
\<row macro> \<return> [<col number>]
这样,将行、列\<row macro>
中的项目保存到中。以前的代码是可扩展的,因此会直接扩展到返回值,但现在它可能会抛出(不可扩展的)错误,因此受到保护并返回中的项目,然后可以使用该项目。<row ID>
<col number>
<CSV ID>
\<return>
\<row macro> [<col number>]
\<row macro>
\<return>
您请求的所有错误和警告都应该在那里。如果未定义 CSV 数据库,则会csv-undefined
引发错误。如果 CSV 存在,但没有请求的密钥,key-undefined
则会引发。这两个错误都在 中签入。这两个测试对于和\__diaa_csv_item_exist:nnN(T)
很常见。\getValue
\getRow
使用 时\getValue
,如果请求的列为零或大于 CSV 中的列数,out-of-range
则会出现错误。然后,如果请求了有效项目,则检查其是否为空。如果是,则item-empty
发出警告。
使用 时\getRow
,如果一行中的所有项都为空,row-empty
则会发出警告,并且 被\<row macro>
定义为improper-row
在使用时引发错误。否则,\<row macro>
被定义为执行与 相同的特定检查\getValue
:检查列号是否在范围内,然后检查返回的项是否不为空。
代码如下:
\begin{filecontents*}{test.csv}
Third Parameter , 7 , 9 ,
First Parameter , 5 , {foo, bar} ,
Second Parameter , 3 , 6 , 44
Empty Parameter , , ,
\end{filecontents*}
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
% Step 1: reading the file
\ior_new:N \l__diaa_csv_ior
\bool_new:N \l__diaa_csv_str_bool
\bool_new:N \l__diaa_empty_item_bool
\seq_new:N \l__diaa_csv_tmp_seq
\tl_new:N \l__diaa_tmpa_tl
% str mode (bool/star), key column, label, value columns, file
\NewDocumentCommand \ReadCSV { s O{1} m O{} m }
{
\IfBooleanTF {#1}
{ \bool_set_true:N \l__diaa_csv_str_bool }
{ \bool_set_false:N \l__diaa_csv_str_bool }
\diaa_csv_read:nnnn {#3} {#2} {#4} {#5}
}
% label, key column, value columns, file
\cs_new_protected:Npn \diaa_csv_read:nnnn #1 #2 #3 #4
{
\tl_if_blank:nTF {#3} % Detect number of columns and use 2 to last
{
\ior_open:NnTF \l__diaa_csv_ior {#4}
{
\bool_if:NTF \l__diaa_csv_str_bool
{ \ior_str_get:NN }
{ \ior_get:NN }
\l__diaa_csv_ior \l_tmpa_tl
\ior_close:N \l__diaa_csv_ior
\seq_set_split:NnV \l_tmpa_seq { , } \l_tmpa_tl
\seq_clear:N \l__diaa_csv_tmp_seq
\int_step_inline:nnn { 2 } { \seq_count:N \l_tmpa_seq }
{ \seq_put_right:Nn \l__diaa_csv_tmp_seq {##1} }
}
{ \msg_error:nnn { diaa } { file-not-found } {#4} }
}
{ \seq_set_split:Nnn \l__diaa_csv_tmp_seq { , } {#3} } % explicit columns
\ior_open:NnTF \l__diaa_csv_ior {#4}
{
\prop_new:c { g__diaa_csv_#1_prop }
\__diaa_csv_read:nn {#1} {#2}
\ior_close:N \l__diaa_csv_ior
}
{ \msg_error:nnn { diaa } { file-not-found } {#4} }
}
\msg_new:nnn { diaa } { file-not-found }
{ File~`#1'~not~found. }
\cs_generate_variant:Nn \prop_put:Nnn { cxV }
% label, key column
\cs_new_protected:Npn \__diaa_csv_read:nn #1 #2
{
\bool_if:NTF \l__diaa_csv_str_bool
{ \ior_str_map_inline:Nn }
{ \ior_map_inline:Nn }
\l__diaa_csv_ior
{
\seq_set_split:Nnn \l_tmpa_seq { , } {##1} % split one CSV row
\tl_clear:N \l_tmpa_tl
\seq_map_inline:Nn \l__diaa_csv_tmp_seq
{ \tl_put_right:Nx \l_tmpa_tl { { \seq_item:Nn \l_tmpa_seq {####1} } } }
\prop_put:cxV { g__diaa_csv_#1_prop }
{ \seq_item:Nn \l_tmpa_seq {#2} }
\l_tmpa_tl
}
}
% Step 2: getting the values
% star → global assignment, macro or tl var, value column, key, label
\NewDocumentCommand \getValue { s m O{1} m m }
{
\tl_clear:N \l__diaa_tmpa_tl
\diaa_csv_item:nnnN {#4} {#3} {#5} \l__diaa_tmpa_tl
\IfBooleanTF {#1}
{ \tl_gset_eq:NN } { \tl_set_eq:NN }
#2 \l__diaa_tmpa_tl
\tl_if_empty:NT #2
{ \msg_warning:nnnnn { diaa } { item-empty } {#3} {#4} {#5} }
}
% key, value column, label
\cs_new_protected:Npn \diaa_csv_item:nnnN #1 #2 #3 #4
{
\__diaa_csv_item_exist:nnNT {#3} {#1} #4
{ \exp_args:NV \__diaa_check_column_range:nn #4 {#2} }
}
\cs_new_protected:Npn \__diaa_check_column_range:nn #1 #2
{
\bool_lazy_or:nnTF
{ \int_compare_p:nNn {#2} = { 0 } }
{ \int_compare_p:nNn { \tl_count:n {#1} } < { \int_abs:n {#2} } }
{ \msg_error:nnn { diaa } { out-of-range } {#2} }
{ \tl_set:Nx \l__diaa_tmpa_tl { \tl_item:nn {#1} {#2} } }
}
\prg_new_protected_conditional:Npnn \__diaa_csv_item_exist:nnN #1 #2 #3 { T }
{
\prop_if_exist:cTF { g__diaa_csv_#1_prop }
{
\prop_get:cnNTF { g__diaa_csv_#1_prop } {#2} #3
{ \prg_return_true: }
{
\msg_error:nnnn { diaa } { key-undefined } {#2} {#1}
\prg_return_false:
}
}
{
\msg_error:nnn { diaa } { csv-undefined } {#1}
\prg_return_false:
}
}
\cs_new_protected:Npn \__diaa_check_empty:nn #1 #2
{
\tl_if_empty:nT {#1}
{ \msg_warning:nnn { diaa } { empty-row-item } {#2} }
#1
}
% star → global assignment, macro, key, label
\NewDocumentCommand \getRow { s m m m }
{
\IfBooleanTF {#1}
{ \__diaa_get_row:NNnn \cs_gset_protected:Npx }
{ \__diaa_get_row:NNnn \cs_set_protected:Npx }
#2 {#3} {#4}
}
\cs_new_protected:Npn \__diaa_get_row:NNnn #1 #2 #3 #4
{
#1 #2 ##1 [ ##2 ]
{ \msg_error:nnx { diaa } { improper-row } { \cs_to_str:N #2 } }
\__diaa_csv_item_exist:nnNT {#4} {#3} \l__diaa_tmpa_tl
{
\bool_set_true:N \l__diaa_empty_item_bool
\tl_map_inline:Nn \l__diaa_tmpa_tl
{
\tl_if_empty:nF {##1}
{ \bool_set_false:N \l__diaa_empty_item_bool }
}
\bool_if:NT \l__diaa_empty_item_bool
{ \msg_warning:nnnn { diaa } { row-empty } {#3} {#4} }
#1 #2 ##1 [ ##2 ]
{
\exp_not:N \__diaa_get_column:nnN
{ \exp_not:V \l__diaa_tmpa_tl } {##2} ##1
}
}
}
\cs_new_protected:Npn \__diaa_get_column:nnN #1 #2 #3
{
\str_if_eq:nnTF {#2} { non-empty }
{
\__diaa_nb_nonempty_items_in_row:nn { 0 } #1
\q_recursion_tail \q_recursion_stop
}
{ \__diaa_check_column_range:nn {#1} {#2} }
}
\cs_new:Npn \__diaa_nb_nonempty_items_in_row:nn #1#2
{
\quark_if_recursion_tail_stop_do:nn {#2} { \int_eval:n {#1} }
\tl_if_empty:nTF {#2}
{ \__diaa_nb_nonempty_items_in_row:nn {#1} }
{ \__diaa_nb_nonempty_items_in_row:nn { #1 + 1 } }
}
\msg_new:nnn { diaa } { csv-undefined } { CSV~database~`#1'~undefined! }
\msg_new:nnn { diaa } { key-undefined } { CSV~`#2'~has~no~key~`#1'! }
\msg_new:nnn { diaa } { out-of-range } { Index~#1~out~of~range! }
\msg_new:nnn { diaa } { item-empty }
{ Item~#1~from~`#2'~in~CSV~`#3'~is~empty! }
\msg_new:nnn { diaa } { row-empty }
{ Row~`#1'~in~CSV~`#2'~is~empty! }
\msg_new:nnn { diaa } { empty-row-item }
{ Empty~item~#1~\msg_line_context:! }
\msg_new:nnn { diaa } { improper-row }
{
Improper~row~macro~\iow_char:N \\#1~\msg_line_context:.\\
The~\iow_char:N \\getRow~command~did~not~succeed.
}
\ExplSyntaxOff
\parindent0pt
\begin{document}
\ReadCSV{mydata}{test.csv}
\verb|\getValue\myValue{Second Parameter}{NOTmydata}|\\
A \textbf{compilation-halting error} should show up stating that "NOTmydata" is undefined CSV data.\\
\getValue\myValue{Second Parameter}{NOTmydata}\\
\getRow\myValue{Second Parameter}{NOTmydata}; \myValue\myValuee[1]\\
\verb|\getValue\myValue{Non-existent Parameter}{mydata}|\\
A \textbf{compilation-halting error} should show up stating that "Non-existent Parameter" is undefined CSV parameter.\\
\getValue\myValue{Non-existent Parameter}{mydata}\\
\getRow\myValue{Non-existent Parameter}{mydata}; \myValue\myValuee[1]\\
\verb|\getValue\myValue[4]{Second Parameter}{mydata}|\\
An \textbf{error} because index 4 is out of range.\\
\getValue\myValue[4]{Second Parameter}{mydata}\\
\getRow\myValue{Second Parameter}{mydata}; \myValue\myValuee[4]\\
\verb|\getValue\myValue{Empty Parameter}{mydata}|\\
A \textbf{Warning} should show up stating that the value stored in \verb|\myValue| is empty.
\getValue\myValue{Empty Parameter}{mydata}\\
\getRow\myValue{Empty Parameter}{mydata}; \myValue\myValuee[1]\\
\end{document}
以前的答案,供参考:
从存储的 CSV 中提取数据的函数\diaa_csv_item:nnn
是可扩展的,因此您不会收到警告,只会收到可扩展的错误,而这些错误本身非常有限。可扩展错误的形式如下:
! Undefined control sequence.
<argument> \::error
! diaa: CSV database `NOTmydata' undefined!
l.161 ...alue\myValue{Second Parameter}{NOTmydata}
Undefined control sequence
即TeX 的低级错误(\::error
未定义的控制序列),以及其后的一行文本。消息不能太长,否则 TeX 会截断该消息。
我实现了警告作为对返回值的事后检查\getValue
。由于该函数不可扩展,因此可能会出现警告。 \CSVItem
不能出现警告:要么是可扩展错误,要么什么都没有。我留下了一个版本,其中有一个空项在代码中被注释掉了。
\getRow\myRow...
请注意,在检查检索到的行的元素是否为空值(即使用然后\myRow[<index>]
返回空值)的情况下,\__diaa_check_empty:nn
将使用宏。此宏不受保护,因此它将在仅扩展的上下文中扩展,但是不是因为它包含一个\msg_warning:nnn
(正如我之前所说,你不能有可扩展警告),所以如果检索到的项目为空,它可能会在扩展上下文中意外中断。如果不是,则一切正常。
除此之外,我还用来\prop_if_exist:cTF
检查 CSV 是否已定义,\prop_if_in:cnTF
检查请求的项目是否存在于 CSV 中,以及\tl_count:n
检查请求的列是否在范围内。
\begin{filecontents*}{test.csv}
Third Parameter , 7 , 9 ,
First Parameter , 5 , {foo, bar} ,
Second Parameter , 3 , 6 , 44
Empty Parameter , , ,
\end{filecontents*}
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
% Step 1: reading the file
\tl_new:N \l__diaa_tmpa_tl
\ior_new:N \l__diaa_csv_ior
\bool_new:N \l__diaa_csv_str_bool
\bool_new:N \l__diaa_empty_item_bool
\seq_new:N \l__diaa_csv_tmp_seq
% str mode (bool/star), key column, label, value columns, file
\NewDocumentCommand \ReadCSV { s O{1} m O{} m }
{
\IfBooleanTF {#1}
{ \bool_set_true:N \l__diaa_csv_str_bool }
{ \bool_set_false:N \l__diaa_csv_str_bool }
\diaa_csv_read:nnnn {#3} {#2} {#4} {#5}
}
% label, key column, value columns, file
\cs_new_protected:Npn \diaa_csv_read:nnnn #1 #2 #3 #4
{
\tl_if_blank:nTF {#3} % Detect number of columns and use 2 to last
{
\ior_open:NnTF \l__diaa_csv_ior {#4}
{
\bool_if:NTF \l__diaa_csv_str_bool
{ \ior_str_get:NN }
{ \ior_get:NN }
\l__diaa_csv_ior \l__diaa_tmpa_tl
\ior_close:N \l__diaa_csv_ior
\seq_set_split:NnV \l_tmpa_seq { , } \l__diaa_tmpa_tl
\seq_clear:N \l__diaa_csv_tmp_seq
\int_step_inline:nnn { 2 } { \seq_count:N \l_tmpa_seq }
{ \seq_put_right:Nn \l__diaa_csv_tmp_seq {##1} }
}
{ \msg_error:nnn { diaa } { file-not-found } {#4} }
}
{ \seq_set_split:Nnn \l__diaa_csv_tmp_seq { , } {#3} } % explicit columns
\ior_open:NnTF \l__diaa_csv_ior {#4}
{
\prop_new:c { g__diaa_csv_#1_prop }
\__diaa_csv_read:nn {#1} {#2}
\ior_close:N \l__diaa_csv_ior
}
{ \msg_error:nnn { diaa } { file-not-found } {#4} }
}
\msg_new:nnn { diaa } { file-not-found }
{ File~`#1'~not~found. }
\cs_generate_variant:Nn \prop_put:Nnn { cxV }
% label, key column
\cs_new_protected:Npn \__diaa_csv_read:nn #1 #2
{
\bool_if:NTF \l__diaa_csv_str_bool
{ \ior_str_map_inline:Nn }
{ \ior_map_inline:Nn }
\l__diaa_csv_ior
{
\seq_set_split:Nnn \l_tmpa_seq { , } {##1} % split one CSV row
\tl_clear:N \l__diaa_tmpa_tl
\seq_map_inline:Nn \l__diaa_csv_tmp_seq
{
\tl_put_right:Nx \l__diaa_tmpa_tl { { \seq_item:Nn \l_tmpa_seq {####1} } }
}
\prop_put:cxV { g__diaa_csv_#1_prop }
{ \seq_item:Nn \l_tmpa_seq {#2} }
\l__diaa_tmpa_tl
}
}
% Step 2: getting the values
% star → global assignment, macro or tl var, value column, key, label
\NewDocumentCommand \getValue { s m O{1} m m }
{
\IfBooleanTF {#1} { \tl_gset:Nx } { \tl_set:Nx }
#2 { \diaa_csv_item:nnn {#4} {#3} {#5} }
\tl_if_empty:NT #2
{ \msg_warning:nnnnn { diaa } { item-empty } {#3} {#4} {#5} }
}
% key, value column, label
\NewExpandableDocumentCommand \CSVItem { m O{1} m }
{ \diaa_csv_item:nnn {#1} {#2} {#3} }
% Version with error if empty
% \NewExpandableDocumentCommand \CSVItem { m O{1} m }
% {
% \exp_args:Nf \__diaa_check_empty_item:nnnn
% { \diaa_csv_item:nnn {#1} {#2} {#3} } {#2} {#1} {#3}
% }
% \cs_new:Npn \__diaa_check_empty_item:nnnn #1 #2 #3 #4
% {
% \tl_if_empty:nTF {#1}
% { \msg_expandable_error:nnnnn { diaa } { item-empty } {#2} {#3} {#4} }
% {#1}
% }
\cs_generate_variant:Nn \tl_item:nn { f }
% key, value column, label
\cs_new:Npn \diaa_csv_item:nnn #1 #2 #3
{
\prop_if_exist:cTF { g__diaa_csv_#3_prop }
{
\prop_if_in:cnTF { g__diaa_csv_#3_prop } {#1}
{
\exp_args:NNf \__diaa_check_column_range:Nnn \use_i:nn
{ \prop_item:cn { g__diaa_csv_#3_prop } {#1} } {#2}
}
{ \msg_expandable_error:nnnn { diaa } { key-undefined } {#1} {#3} }
}
{ \msg_expandable_error:nnn { diaa } { csv-undefined } {#3} }
}
\cs_new:Npn \__diaa_check_column_range:Nnn #1 #2 #3
{
\bool_lazy_or:nnTF
{ \int_compare_p:nNn {#3} = { 0 } }
{ \int_compare_p:nNn { \tl_count:n {#2} } < { \int_abs:n {#3} } }
{ \msg_expandable_error:nnn { diaa } { out-of-range } {#3} }
{ \exp_args:Nf #1 { \tl_item:nn {#2} {#3} } {#3} }
}
\msg_new:nnn { diaa } { csv-undefined } { CSV~database~`#1'~undefined! }
\msg_new:nnn { diaa } { key-undefined } { CSV~`#2'~has~no~key~`#1'! }
\msg_new:nnn { diaa } { out-of-range } { Index~#1~out~of~range! }
\msg_new:nnn { diaa } { item-empty }
{ Item~#1~from~`#2'~in~CSV~`#3'~is~empty! }
\msg_new:nnn { diaa } { empty-row-item }
{ Empty~item~#1~\msg_line_context:! }
\msg_new:nnn { diaa } { row-empty }
{ Row~`#1'~in~CSV~`#2'~is~empty! }
\cs_new:Npn \__diaa_check_empty:nn #1 #2
{
\tl_if_empty:nT {#1}
{ \msg_warning:nnn { diaa } { empty-row-item } {#2} }
#1
}
% star → global assignment, macro, key, label
\NewDocumentCommand \getRow { s m m m }
{
\prop_if_exist:cTF { g__diaa_csv_#4_prop }
{
\prop_get:cnNTF { g__diaa_csv_#4_prop } {#3} \l__diaa_tmpa_tl
{
\bool_set_true:N \l__diaa_empty_item_bool
\tl_map_inline:Nn \l__diaa_tmpa_tl
{
\tl_if_empty:nF {##1}
{ \bool_set_false:N \l__diaa_empty_item_bool }
}
\bool_if:NT \l__diaa_empty_item_bool
{ \msg_warning:nnnn { diaa } { row-empty } {#3} {#4} }
\IfBooleanTF {#1} { \cs_gset_nopar:Npx } { \cs_set_nopar:Npx } #2 [ ##1 ]
{
\exp_not:N \str_if_eq:nnTF {##1} { non-empty }
{
\exp_not:N \__diaa_nb_nonempty_items_in_row:nn { 0 }
\exp_not:V \l__diaa_tmpa_tl
\exp_not:n { \q_recursion_tail \q_recursion_stop }
}
{
\exp_not:N \__diaa_check_column_range:Nnn
\exp_not:N \__diaa_check_empty:nn
{ \exp_not:V \l__diaa_tmpa_tl } {##1}
}
}
}
{ \msg_error:nnnn { diaa } { key-undefined } {#3} {#4} }
}
{ \msg_error:nnn { diaa } { csv-undefined } {#4} }
}
\cs_new:Npn \__diaa_nb_nonempty_items_in_row:nn #1#2
{
\quark_if_recursion_tail_stop_do:nn {#2} { \int_eval:n {#1} }
\tl_if_empty:nTF {#2}
{ \__diaa_nb_nonempty_items_in_row:nn {#1} }
{ \__diaa_nb_nonempty_items_in_row:nn { #1 + 1 } }
}
\ExplSyntaxOff
\parindent0pt
\begin{document}
\ReadCSV{mydata}{test.csv}
\verb|\getValue\myValue{Second Parameter}{NOTmydata}|\\
A \textbf{compilation-halting error} should show up stating that "NOTmydata" is undefined CSV data.\\
\getValue\myValue{Second Parameter}{NOTmydata}\\
\verb|\getValue\myValue{Non-existent Parameter}{mydata}|\\
A \textbf{compilation-halting error} should show up stating that "Non-existent Parameter" is undefined CSV parameter.\\
\getValue\myValue{Non-existent Parameter}{mydata}\\
\verb|\getValue\myValue[4]{Second Parameter}{mydata}|\\
An \textbf{error} because index 4 is out of range.\\
\getValue\myValue[4]{Second Parameter}{mydata}\\
\verb|\getValue\myValue{Empty Parameter}{mydata}|\\
A \textbf{Warning} should show up stating that the value stored in \verb|\myValue| is empty.
\getValue\myValue{Empty Parameter}{mydata}\\
\getRow\myRow{Empty Parameter}{mydata}
\myRow[1]
\CSVItem{Second Parameter}[4]{mydata}
\end{document}