Siguiendoesta respuesta, ¿cómo puedo tener un argumento opcional del comando \getValue
que especifique la columna en la que busco el valor de la celda para que tenga la siguiente sintaxis?
\getValue\<macro name>%
[<optional column number with default of 2, and the counting starts with 1 at the very first column>]%
{<search phrase>}%
{<data of the csv file>}
Por ejemplo, \getValue\SndParThrd[4]{Second Parameter}{mydata}
(después de modificar el siguiente código) debería almacenarse 44
en formato \SndParThrd
.
Además, \getValue\SndParFrst{Second Parameter}{mydata}
debe almacenarse 3
en \SndParFrst
.
\begin{filecontents*}{test.csv}
Third Parameter , 7 , 9 ,
First Parameter , 5 , 10 ,
Second Parameter , 3 , 6 , 44
\end{filecontents*}
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
% Step 1: reading the file
\ior_new:N \l__diaa_csv_ior
\ior_new:N \l__diaa_csv_str_bool
\NewDocumentCommand \ReadCSV { s m >{ \SplitArgument {1} {,} }O{ 1,2 } 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 {#2} #3 {#4}
}
\cs_new_protected:Npn \diaa_csv_read:nnnn #1 #2 #3 #4
{
\prop_new:c { g__diaa_csv_#1_prop }
\ior_open:NnTF \l__diaa_csv_ior {#4}
{ \__diaa_csv_read:cnn { g__diaa_csv_#1_prop } {#2} {#3} }
{ \msg_error:nnn { diaa } { file-not-found } {#4} }
}
\msg_new:nnn { diaa } { file-not-found }
{ File~`#1'~not~found. }
\cs_new_protected:Npn \__diaa_csv_read:Nnn #1 #2 #3
{
\bool_if:NTF \l__diaa_csv_str_bool
{ \ior_str_map_inline:Nn }
{ \ior_map_inline:Nn }
\l__diaa_csv_ior
{
\prop_put:Nxx #1
{ \clist_item:nn {##1} {#2} }
{ \clist_item:nn {##1} {#3} }
}
}
\cs_generate_variant:Nn \__diaa_csv_read:Nnn { c }
%
% Step 2: getting the values
\NewDocumentCommand \getValue { m m m }
{ \tl_set:Nx #1 { \diaa_csv_item:nn {#2} {#3} } }
\NewExpandableDocumentCommand \CSVItem { m m }
{ \diaa_csv_item:nn {#1} {#2} }
\cs_new:Npn \diaa_csv_item:nn #1 #2
{ \prop_item:cn { g__diaa_csv_#2_prop } {#1} }
\ExplSyntaxOff
\begin{document}
\ReadCSV{mydata}{test.csv}
\getValue\rdPar{Third Parameter}{mydata}
\rdPar
\edef\stPar{\CSVItem{First Parameter}{mydata}}%
\stPar
\end{document}
Respuesta1
Esta es una versión modificada deLa respuesta de Phelype Oleinikal que se vinculó en la pregunta. El primer método es más corto, el segundo es mejor. :-)
Primer método
Los números de columna en el archivo CSV comienzan en 1. Los nuevos comandos tienen la siguiente sintaxis:
\ReadCSV[*] {label} [key column] {value columns} {file}
dóndecolumna clavepor defecto es 1 ycolumnas de valores una lista de números de columnas separados por comas (solo se almacenan los valores de estas columnas \ReadCSV
para permitir su posterior recuperación mediante \getValue
y \CSVItem
).
\getValue {\macro} [value column] {key} {label}
Esto es lo que pediste en la pregunta.columna de valorEl valor predeterminado es 2. El contenido decolumna de valorcorrespondiente a lo especificadollavese almacena en \macro
(que podría ser una expl3
variable de lista de tokens).
\CSVItem {key} [value column] {label}
Este comando es completamente ampliable. Se expande recursivamente al contenido decolumna de valorcorrespondiente a lo especificadollave.columna de valorEl valor predeterminado es 2, como en \getValue
.
\begin{filecontents*}{test.csv}
Third Parameter , 7 , 9 ,
First Parameter , 5 , 10 ,
Second Parameter , 3 , 6 , 44
\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), label, key column, value columns, file
\NewDocumentCommand \ReadCSV { s m O{1} m 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 {#2} {#3} {#4} {#5}
}
% label, key column, value columns, file
\cs_new_protected:Npn \diaa_csv_read:nnnn #1 #2 #3 #4
{
\seq_set_split:Nnn \l__diaa_csv_tmp_seq { , } {#3}
\seq_map_inline:Nn \l__diaa_csv_tmp_seq
{ \prop_new:c { g__diaa_csv_#1_##1_prop } }
\ior_open:NnTF \l__diaa_csv_ior {#4}
{
\__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. }
% 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_map_inline:Nn \l__diaa_csv_tmp_seq
{
\prop_put:cxx { g__diaa_csv_#1_####1_prop }
{ \clist_item:nn {##1} {#2} }
{ \clist_item:nn {##1} {####1} }
}
}
}
% Step 2: getting the values
% macro or tl var, value column, key, label
\NewDocumentCommand \getValue { m O{2} m m }
{ \tl_set:Nx #1 { \diaa_csv_item:nnn {#3} {#2} {#4} } }
% key, column containing the desired value, label
\NewExpandableDocumentCommand \CSVItem { m O{2} m }
{ \diaa_csv_item:nnn {#1} {#2} {#3} }
% key, column containing the desired value, label
\cs_new:Npn \diaa_csv_item:nnn #1 #2 #3
{ \prop_item:cn { g__diaa_csv_#3_#2_prop } {#1} }
\ExplSyntaxOff
\begin{document}
% Use default column for the key (1)
\ReadCSV{mydata}{2,4}{test.csv} % discard the third column
\getValue\rdPar[4]{Second Parameter}{mydata}
\rdPar % 44
\getValue\rdPar{Second Parameter}{mydata}
\rdPar % 3
\getValue\rdPar{Third Parameter}{mydata}
\rdPar % 7
\edef\rdPar{\CSVItem{First Parameter}{mydata}}%
\rdPar % 5
\edef\rdPar{\CSVItem{First Parameter}[4]{mydata}}%
\ifx\rdPar\empty
\textlangle empty\textrangle
\else
\rdPar
\fi
\end{document}
Segundo método
Esto se basa en el código anterior, pero:
el análisis de las líneas CSV ahora se realiza con funciones en
\seq_set_split:Nnn
lugar declist
para no ignorar las celdas vacías;consume más memoria (solo
l3prop
se usa una para almacenar todas las columnas seleccionadas);el direccionamiento de columnas es diferente: cuando se recuperan valores con
\getValue
o\CSVItem
, la columna 1 es la primera columna seleccionada, la columna 2 es la segunda columna seleccionada, etc. En consecuencia, el número de columna predeterminado cuando se recuperan valores con estas funciones es 1 (en lugar de 2).\getValue
Ahora tiene una versión estrella que realiza una asignación global.
Como desea la detección automática del número de columnas, tuve que cambiar la sintaxis de \ReadCSV
la siguiente manera:
\ReadCSV[*] [key column] {label} [value columns] {file}
columna clavepor defecto es 1 como arriba. Sin embargo cuandocolumnas de valorestá en blanco, la primera línea dearchivose lee para determinar el númeronortede columnas, y las columnas de valor se definen como todas las columnas del 2 alnorte(ambos inclusive).
Los espacios a ambos lados de las comas delimitadoras de columnas se ignoran al leer el archivo y se elimina un conjunto de llaves exteriores alrededor del contenido de cada celda, si las hay (esto se ilustra en la celda que contiene foo, bar
el archivo CSV de muestra a continuación).
\begin{filecontents*}{test.csv}
Third Parameter , 7 , 9 ,
First Parameter , 5 , {foo, bar} ,
Second Parameter , 3 , 6 , 44
\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}
}
\ExplSyntaxOff
\begin{document}
% Use default column for the key (1). The second empty optional argument (list
% of value columns) means we want to autodetect the value columns; then, the
% first column is for keys and all other columns are used as value columns.
\ReadCSV{mydata}{test.csv}
\getValue\rdPar{Second Parameter}{mydata}
\rdPar % 3
\getValue\rdPar[2]{Second Parameter}{mydata}
\rdPar % 6
\getValue\rdPar[3]{Second Parameter}{mydata}
\rdPar % 44
\getValue\rdPar{Third Parameter}{mydata}
\rdPar % 7
\edef\rdPar{\CSVItem{First Parameter}{mydata}}%
\rdPar % 5
\edef\rdPar{\CSVItem{First Parameter}[2]{mydata}}%
\rdPar % foo, bar
\edef\rdPar{\CSVItem{First Parameter}[3]{mydata}}%
\ifx\rdPar\empty
\textlangle empty\textrangle
\else
\rdPar
\fi
\end{document}
Respuesta2
Con el readarray
paquete...
\begin{filecontents*}{test.csv}
Third Parameter , 7 , 9 ,
First Parameter , 5 , 10 ,
Second Parameter , 3 , 6 , 44
\end{filecontents*}
\documentclass{article}
\usepackage{readarray,pgffor}
\newcommand\getValue[3][2]{%
\def\tmpA{#2}%
\foreach\z in{1,...,\nrows}{%
\arraytomacro#3[\z,1]\tmpB
\ifx\tmpA\tmpB #3[\z,#1]\fi
}%
}
\begin{document}
\readarraysepchar{,}
\readdef{test.csv}\mydata
\readarray\mydata\myarray[-,\ncols]
\nrows{} rows in data
Cell(2,2): \myarray[2,2]
Cell(3,4): \myarray[3,4]
\getValue{Third Parameter }{\myarray}
and
\getValue[4]{Second Parameter }{\myarray}
\end{document}
Tenga en cuenta que los espacios iniciales y finales de los datos de entrada se conservan de forma predeterminada. Sin embargo, el uso de \readarray*
en lugar de \readarray
eliminará los espacios iniciales y finales (en cuyo caso, las especificaciones del campo de búsqueda también deben eliminar dichos espacios).
\begin{filecontents*}{test.csv}
Third Parameter , 7 , 9 ,
First Parameter , 5 , 10 ,
Second Parameter , 3 , 6 , 44
\end{filecontents*}
\documentclass{article}
\usepackage{readarray,pgffor}
\newcommand\getValue[3][2]{%
\def\tmpA{#2}%
\foreach\z in{1,...,\nrows}{%
\arraytomacro#3[\z,1]\tmpB
\ifx\tmpA\tmpB #3[\z,#1]\fi
}%
}
\begin{document}
\readarraysepchar{,}
\readdef{test.csv}\mydata
\readarray*\mydata\myarray[-,\ncols]
\nrows{} rows in data
Cell(2,2): \myarray[2,2]
Cell(3,4): \myarray[3,4]
\getValue{Third Parameter}{\myarray}
and
\getValue[4]{Second Parameter}{\myarray}
\end{document}