最初の方法

最初の方法

フォローアップこの答え\getValueセル値を検索する列を指定するコマンドのオプション引数を次の構文で指定するにはどうすればよいですか?

\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>}

たとえば、\getValue\SndParThrd[4]{Second Parameter}{mydata}(次のコードを調整した後)44は に格納されるはずです\SndParThrd

また、に\getValue\SndParFrst{Second Parameter}{mydata}保存する必要があります。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}

答え1

これは、Phelype Oleinik の回答質問でリンクした方法。最初の方法は短く、2 番目の方法の方が優れています。:-)

最初の方法

CSV ファイル内の列番号は 1 から始まります。新しいコマンドの構文は次のとおりです。

\ReadCSV[*] {label} [key column] {value columns} {file}

どこキー列デフォルトは1で、値列は、カンマで区切られた列番号のリストです (これらの列の値のみが によって保存され、後でおよび\ReadCSVによって取得できるようになります)。\getValue\CSVItem

\getValue {\macro} [value column] {key} {label}

これが質問で尋ねられた内容です。値列デフォルトは2です。値列指定されたものに対応するに格納されます\macro(expl3トークン リスト変数である可能性があります)。

\CSVItem {key} [value column] {label}

このコマンドは完全に拡張可能です。再帰的に以下の内容を展開します。値列指定されたものに対応する値列デフォルトは 2 です\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}

ここに画像の説明を入力してください

2番目の方法

これは前のコードに基づいていますが、次のようになります。

  • 空のセルを無視しないように、CSV 行の解析は関数\seq_set_split:Nnnではなくで実行されるようになりました。clist

  • メモリ効率が高くなります (l3prop選択されたすべての列を格納するために 1 つの列のみが使用されます)。

  • 列のアドレス指定が異なります。\getValueまたはを使用して値を取得する場合\CSVItem、列 1 は最初に選択された列、列 2 は 2 番目に選択された列、というようになります。したがって、これらの関数を使用して値を取得する場合のデフォルトの列番号は 1 (2 ではなく) になります。

  • \getValueグローバル割り当てを実行するスター バージョンが追加されました。

列数を自動検出したいので、構文を\ReadCSV次のように変更する必要がありました。

\ReadCSV[*] [key column] {label} [value columns] {file}

キー列デフォルトでは上記のように1になります。ただし、値列が空白の場合、ファイル数を決定するために読み取られる列数、値列は2から(両方を含む)。

ファイルの読み取り時に、列区切りのカンマの両側にあるスペースは無視され、各セルの内容を囲む外側の中括弧の 1 セットは削除されます (これは、foo, bar以下のサンプル CSV ファイルに含まれるセルで示されています)。

\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}

ここに画像の説明を入力してください

答え2

パッケージにはreadarray...

\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}

ここに画像の説明を入力してください

\readarray*デフォルトでは、入力データの先頭/末尾のスペースは保持されます。ただし、の代わりにを使用すると\readarray、先頭/末尾のスペースが削除されます (その場合、検索フィールドの指定でもそのようなスペースを削除する必要があります)。

\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}

関連情報