9

I have some thousands of special text lines specifically numbered.

001001=aaaaaaaa
001002=bbbbbbbb
001003=cccccccc

002001=dddddddd
002002=mmmmmmmm
002003=jjjjjjjj

003001=uuuuuuuu
003002=iiiiiiii
003003=vvvvvvvv

etc.

In my Latex file I want to insert that specific assigned text with a simple number entry such as 001001 or perhaps \001001 , which in return will show aaaaaaaa once compiled. How can I do this?

Please note that I've already studied following Q&A's but still couldn't come up with a good result, not could I use \catcode. I use xetex and polyglossia if that makes difference.

Nina
  • 123

3 Answers3

10

One way is to use the datatool package. An attempt to print the values of 002003, 001002, and 999999 yields:

enter image description here

Notes:

  • I used \IfStrEq from the the xstring package package as I prefer it's syntax, but this can probably be done without that pacakge if desired.
  • The filecontents package was used only to create a MWE to generate the data file foo.dat. It is not needed otherwise.

Code:

\documentclass{article}
\usepackage{datatool}
\usepackage{xstring}

\usepackage{filecontents} \begin{filecontents*}{foo.dat} 001001=aaaaaaaa 001002=bbbbbbbb 001002=cccccccc

002001=dddddddd 002002=mmmmmmmm 002003=jjjjjjjj

003001=uuuuuuuu 003002=iiiiiiii 003003=vvvvvvvv \end{filecontents*}

\newcommand{\LocateDataRow}[3]{% % #1 = csname in which to store the required data % #2 = database to search % #3 = word to search for % \global\expandafter\def\csname#1\endcsname{UNDEFINED}% Default in case it is not found % \DTLforeach{#2}{% \NumValue=NumValue,% \DataValue=DataValue% }{% \IfStrEq{#3}{\NumValue}{% % Found an exact match!! Yeah \global\expandafter\def\csname#1\endcsname{\DataValue}% \dtlbreak% Break out of for loop }{% % Did not find match -- continue searching }% }% }%

\begin{document} \DTLsetseparator{=}% Define separator of the data \DTLloaddb[noheader,keys={NumValue,DataValue}]{myDB}{foo.dat}

\LocateDataRow{RequiredData}{myDB}{002003}\RequiredData

\LocateDataRow{RequiredData}{myDB}{001002}\RequiredData

\LocateDataRow{RequiredData}{myDB}{999999}\RequiredData \end{document}

Peter Grill
  • 223,288
7

An approach without external packages can be as follows:

\documentclass{article}

\usepackage{filecontents}
\begin{filecontents*}{\jobname.dat}
001001=aaaaaaaa
001002=bbbbbbbb
001002=cccccccc

002001=dddddddd
002002=mmmmmmmm
002003=jjjjjjjj

003001=uuuuuuuu
003002=iiiiiiii
003003=vvvvvvvv
\end{filecontents*}

\makeatletter
\newread\nina@read
\newcommand{\grabdata}[1]{%
  \begingroup\endlinechar=\m@ne
  \openin\nina@read=#1\relax
  \loop\ifeof\nina@read\else
    \read\nina@read to \@tempa
    \nina@convert
  \repeat
  \closein\nina@read
  \endgroup}
\def\nina@convert{%
  \if\relax\detokenize\expandafter{\@tempa}\relax\else
    \expandafter\nina@convert@i\@tempa\relax
  \fi}
\def\nina@convert@i#1=#2\relax{%
  \global\@namedef{nina@data@#1}{#2}}
\newcommand{\numentry}[1]{\@nameuse{nina@data@#1}}
\makeatother

\grabdata{\jobname.dat}


\begin{document}

\numentry{001001}

\numentry{001002}

\numentry{001003}

\numentry{002001}

\numentry{002002}

\numentry{002003}

\numentry{003001}

\numentry{003002}

\numentry{003003}

\end{document}

I've used \jobname.dat as the file name just not to clobber my existing files.

If the data file is named foo.xyz you'll use

\grabdata{foo.xyz}

and then the data will be available in the form

\numentry{001001}

The data file is read line by line and each line is split at the = token to get the key and the value. So if the line is

001001=aaaaaaaa

we essentially do

\@namedef{nina@data@001001}{aaaaaaaa}

(globally, as we use \endlinechar=-1 to avoid spurious spaces and get rid of empty lines).

egreg
  • 1,121,712
7

Here is a solution using expl3 for parsing the data file. It uses a property list to keep the data in memory. Usage then means looking up the the key in this property list and displaying its stored value.

\documentclass{article}

\usepackage{filecontents}
\begin{filecontents*}{foo.dat}
001001=aaaaaaaa
001002=bbbbbbbb
001002=cccccccc

002001=dddddddd
002002=mmmmmmmm
002003=jjjjjjjj
foo bar
003001=uuuuuuuu
003002=iiiiiiii
003003=vvvvvvvv
\end{filecontents*}

\usepackage{expl3,xparse}

\ExplSyntaxOn

\tl_new:N \l_ndata_line_tl
\tl_new:N \l_ndata_key_tl
\tl_new:N \l_ndata_value_tl

\prop_new:N \g_ndata_prop

% loading and parsing the data file

\cs_new:Npn \ndata_load_file:n #1 {
% opening a read stream
   \ior_open:Nn \l_ndata_ior {#1}
% loop until we see "eof"
   \bool_until_do:nn { \ior_if_eof_p:N \l_ndata_ior }
      {
% read one line of data into a variable
        \ior_to:NN  \l_ndata_ior \l_ndata_line_tl
% if the line has no "=" we skip it
        \tl_if_in:NnTF \l_ndata_line_tl {=}
            {
% if it has we parse (low-level, strange input is usually strange
               \exp_after:wN \ndata_parse:w \l_ndata_line_tl \q_stop 
               \typeout {line~ processed~ (\l_ndata_line_tl)}
            }
           {  \typeout {line~ without~ =~ ignored~ (\l_ndata_line_tl)}  }
      }
% close the stream as it is no longer needed
  \ior_close:N \l_ndata_ior
% showing the resulting property list (comment out in real use)
   \prop_show:N \g_ndata_prop
}

\cs_new:Npn \ndata_parse:w #1=#2\q_stop {
   \tl_set:Nn \l_ndata_key_tl {#1}
   \tl_set:Nn \l_ndata_value_tl {#2}
% remove any space to the left and right of key and value
   \tl_trim_spaces:N \l_ndata_key_tl
   \tl_trim_spaces:N \l_ndata_value_tl
% put the data into the property list
   \prop_gput:Noo \g_ndata_prop  \l_ndata_key_tl \l_ndata_value_tl
}

% using the data (instead of error handling for nonexistent keys I just used typeout)
\cs_new:Npn \ndata_use:n #1 {
   \prop_get:NnNTF \g_ndata_prop {#1} \l_ndata_value_tl
            { \l_ndata_value_tl }
            { ??? 
               \typeout{Warning:~ key~`#1`~ not~ known}
            }
}

% document level commands:

\DeclareDocumentCommand \ndataload {m} { \ndata_load_file:n {#1} }
\DeclareDocumentCommand \ndata     {m} { \ndata_use:n {#1} }

\ExplSyntaxOff

\begin{document}

\ndataload{foo.dat}

\ndata{002003}
\ndata{001002}
\ndata{999999}

\end{document}
  • Is there a specific reason for loading both xparse and expl3 explicitly? If not, then \usepackage{xparse} should be sufficient as it loads expl3 for you I believe. – Scott H. Jun 15 '12 at 18:57
  • 2
    @ScottH. There is no technical reason. However, I like to load it as I want to emphasis that I use the programming layer of LaTeX3 AND (one of) its document user layers. In fact, given such a trivial user interface I could have shortened everything considerably by calling \ndata_load_file:n directly \ndataload thereby avoiding xparse altogether. But I wanted to show the general principle (and best practice) keeping the layers completely separate. – Frank Mittelbach Jun 15 '12 at 19:29
  • 1
    10-4, I just wanted to make sure I wasn't missing anything. Thanks. – Scott H. Jun 15 '12 at 19:33