Nachverfolgungdiese Antwort, wie kann ich ein optionales Argument des Befehls haben \getValue
, das die Spalte angibt, in der ich den Zellenwert nachschlage, sodass es die folgende Syntax hat?
\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>}
Beispielsweise \getValue\SndParThrd[4]{Second Parameter}{mydata}
sollte (nach dem Optimieren des folgenden Codes) 44
in gespeichert werden \SndParThrd
.
Sollte außerdem in \getValue\SndParFrst{Second Parameter}{mydata}
gespeichert werden .3
\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}
Antwort1
Dies ist eine modifizierte Version vonPhelype Oleiniks Antwortauf die du in der Frage verwiesen hast. Die erste Methode ist kürzer, die zweite ist besser. :-)
Erste Methode
Die Spaltennummern in der CSV-Datei beginnen bei 1. Die neuen Befehle haben die folgende Syntax:
\ReadCSV[*] {label} [key column] {value columns} {file}
WoSchlüsselspalteDer Standardwert ist 1 undWertespaltenist eine durch Kommas getrennte Liste von Spaltennummern (nur Werte aus diesen Spalten werden gespeichert, \ReadCSV
um ein späteres Abrufen durch \getValue
und zu ermöglichen \CSVItem
).
\getValue {\macro} [value column] {key} {label}
Dies ist, was Sie in der Frage gefragt haben.WertespalteDer Standardwert ist 2. Der Inhalt vonWertespalteentsprechend der angegebenenSchlüsselgespeichert ist (was eine Token-Listenvariable \macro
sein könnte ).expl3
\CSVItem {key} [value column] {label}
Dieser Befehl ist vollständig erweiterbar. Er erweitert rekursiv den Inhalt vonWertespalteentsprechend der angegebenenSchlüssel.WertespalteDer Standardwert ist 2, wie in \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}
Zweite Methode
Dies basiert auf dem vorherigen Code, aber:
Das Parsen der CSV-Zeilen erfolgt jetzt mit
\seq_set_split:Nnn
statt mitclist
Funktionen, um leere Zellen nicht zu ignorieren;es ist speichereffizienter (nur eine
l3prop
wird zum Speichern aller ausgewählten Spalten verwendet);Die Spaltenadressierung ist unterschiedlich: Beim Abrufen von Werten mit
\getValue
oder\CSVItem
ist Spalte 1 die erste ausgewählte Spalte, Spalte 2 die zweite ausgewählte Spalte usw. Dementsprechend ist die Standard-Spaltennummer beim Abrufen von Werten mit diesen Funktionen 1 (statt 2).\getValue
verfügt jetzt über eine Star-Version, die eine globale Zuweisung durchführt.
Da Sie eine automatische Erkennung der Spaltenanzahl wünschen, musste ich die Syntax wie \ReadCSV
folgt ändern:
\ReadCSV[*] [key column] {label} [value columns] {file}
Schlüsselspalteist standardmäßig 1 wie oben. Wenn jedochWertespaltenist leer, die erste Zeile vonDateiwird gelesen, um die Zahl zu bestimmenNvon Spalten, und Wertspalten sind definiert als alle Spalten von 2 bisN(beide inklusive).
Beim Lesen der Datei werden Leerzeichen auf beiden Seiten der Spaltentrennzeichen-Kommas ignoriert und ein Satz äußerer Klammern um jeden Zelleninhalt wird (sofern vorhanden) entfernt (dies wird durch die Zelle foo, bar
in der folgenden CSV-Beispieldatei veranschaulicht).
\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}
Antwort2
Mit dem readarray
Paket...
\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}
Beachten Sie, dass die führenden/nachgestellten Leerzeichen in den Eingabedaten standardmäßig beibehalten werden. Die Verwendung von \readarray*
anstelle von \readarray
entfernt jedoch führende/nachgestellte Leerzeichen (in diesem Fall müssen diese Leerzeichen auch aus den Suchfeldspezifikationen entfernt werden).
\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}