我有一个自定义命令:
\register{address}{format}{shortdescription}{longdescription}
我现在想创建此命令的目录,但不是以正常方式,它应该是包含一些命令参数的真实表。
Address || shortdescription || format || reflink
这可能吗?我可以使用什么包来实现这一点?基本上,我希望有一个包含所有\register
已创建对象的概览表。我找到了tocloft
但没有看到访问监视命令的参数的选项。
答案1
\addtocontents
/ \@starttoc
LaTeX 2ε-kernel 可能是你的朋友:
\documentclass{article}
\usepackage{longtable}
\makeatletter
\newcommand\register[4]{%
\@bsphack
\addtocontents{rgs}{\string\registerline{#1}{#2}{#3}{#4}\protected@file@percent}%
\@esphack
}%
\AtEndDocument{\addtocontents{rgs}{\string\endinput}}%
\newcommand\registertable{%
\section*{Table of...}%
\IfFileExists{\jobname.rgs}{%
\begin{longtable}[l]{|c|c|c|c|}%
\hline Address&shortdescription&format&reflink/long\\\hline\endhead
\hline\endfoot
\@@input\jobname.rgs
\end{longtable}%
}{}%
\begingroup
\def\@input##1{}%
\@starttoc{rgs}%
\endgroup
\@nobreaktrue
}%
\newcommand\registerline[4]{#1\\}%
\makeatother
\begin{document}
\registertable
Some text
\register{Address 1}{Format 1}{Short 1}{Long 1}
\register{Address 2}{Format 2}{Short 2}{Long 2}
\register{Address 3}{Format 3}{Short 3}{Long 3}
\register{Address 4}{Format 4}{Short 4}{Long 4}
Some text
\register{Address 5}{Format 5}{Short 5}{Long 5}
\register{Address 6}{Format 6}{Short 6}{Long 6}
\register{Address 7}{Format 7}{Short 7}{Long 7}
\register{Address 8}{Format 8}{Short 8}{Long 8}
\register{Address 9}{Format 9}{Short 9}{Long 9}
\register{Address 10}{Format 10}{Short 10}{Long 10}
\register{Address 11}{Format 11}{Short 11}{Long 11}
\end{document}
或者,您可以将包数据工具与文档范围的 l3 属性列表结合使用,并将属性值保存到数据工具数据库中,该数据库在 latex 运行结束时将写入外部逗号分隔值文件,从中检索要显示的内容。
(实际上使用了两个数据库 - 一个用于获取源自前一次 latex 运行的数据,一个用于存储当前 latex 运行期间的数据并在 latex 运行结束时写入 .csv 文件。)
\documentclass{article}
\usepackage{datatool}
\usepackage{atveryend}
\usepackage{longtable}
\makeatletter
%========================================================================
% \immediategroup{<balanced text>}%
%------------------------------------------------------------------------
% A local scope wherein <balanced text> is carried out while \write,
% \closeout and \openout are patched to work in terms of \immediate.
%------------------------------------------------------------------------
\newcommand\immediategroup[1]{%
\begingroup
\@ifdefinable\MyStuffsavedwrite{\let\MyStuffsavedwrite\write}%
\@ifdefinable\MyStuffsavedopenout{\let\MyStuffsavedopenout\openout}%
\@ifdefinable\MyStuffsavedcloseout{\let\MyStuffsavedcloseout\closeout}%
\def\write{\immediate\MyStuffsavedwrite}%
\def\openout{\immediate\MyStuffsavedopenout}%
\def\closeout{\immediate\MyStuffsavedcloseout}%
#1%
\endgroup
}%
%========================================================================
% \DATABASEFILECreateIfNotExistent{<base-name of databases and of .csv-file>}%
% {<separator>}%
% {<delimiter>}%
% - Creates database of name: <base-name of databases and of .csv-file>DataFromPreviousLaTeXRun
% - Creates database of name: <base-name of databases and of .csv-file>DataToBeWrittenAtEndOfThisLaTeXRun
% These databases exist in TeX's memory during the LaTeX-run.
% - Triggers the writing of file <base-name of databases and of .csv-file>.csv
% at the end of the LaTeX-run from the content of the database
% <base-name of databases and of .csv-file>DataToBeWrittenAtEndOfThisLaTeXRun
% - Initializes Counter-macro \<base-name of databases and of .csv-file>DataToBeWrittenAtEndOfThisLaTeXRunCnt
% to 0. That counter-macro is to hold the value of the field "PrimaryKey" of the data-base
% <base-name of databases and of .csv-file>DataToBeWrittenAtEndOfThisLaTeXRun
% and is incremented always right before writing another line/row to the that database.
%
% The database <base-name of databases and of .csv-file>DataFromPreviousLaTeXRun is
% used for retrieving data. In case the file <base-name of databases and of .csv-file>.csv
% exists that database is initialized by reading that file. Otherwise it is
% initialized empty.It is not modified during the LaTeX-run.
% The data stems from the previous LaTeX-run.
%
% The <base-name of databases and of .csv-file>DataToBeWrittenAtEndOfThisLaTeXRun is
% used for writing/storing entries during the LaTeX-run. It is initialized empty.
% It is modified during the LaTeX-run. At the end of the LaTeX-run it is saved as
% file <base-name of databases and of .csv-file>.csv .
%
% At the end of the LaTeX-run both databases are compared. If they differ, then
% something has changed and you are informed that you need to do another LaTeX-run.
%
% <separator> is the separator of values in the .csv-file. Usually this is comma (,)
% , but in your question you specified that you wish to use semicolon (;).
%
% <delimiter>: In case a value contains <separator> it needs to be nested
% between two instances of <delimiter>. <delimiter> usually is the quotes-character (").
%------------------------------------------------------------------------
\newcommand\DATABASEFILECreateIfNotExistent[3]{%
\DTLifdbexists{#1DataFromPreviousLaTeXRun}{}{%
\begingroup
\DTLsetseparator{#2}%
\DTLsetdelimiter{#3}%
\IfFileExists{#1.csv}{%
\DTLloaddb{#1DataFromPreviousLaTeXRun}{#1.csv}%
}{%
\DTLgnewdb{#1DataFromPreviousLaTeXRun}%
}%
\endgroup
}%
\DTLifdbexists{#1DataToBeWrittenAtEndOfThisLaTeXRun}{}{%
\DTLgnewdb{#1DataToBeWrittenAtEndOfThisLaTeXRun}%
% --- Counter-macro for primary-key of database ---
\expandafter\@ifdefinable\csname#1DataToBeWrittenAtEndOfThisLaTeXRunCnt\endcsname{%
\expandafter\long\expandafter\gdef\csname#1DataToBeWrittenAtEndOfThisLaTeXRunCnt\endcsname{0}%
}%
% -------------------------------------------------
\AfterLastShipout{%
% This will save the ..DataToBeWrittenAtEndOfThisLaTeXRun-
% database to file after the last \shipout. The last \shipout
% is triggered by \end{document}/\enddocument.
\immediategroup{%
\DTLsetseparator{#2}%
\DTLsetdelimiter{#3}%
\DTLsavedb{#1DataToBeWrittenAtEndOfThisLaTeXRun}{#1.csv}%
}%
% Check if content of database-token-register stemming from
% database-file of previous LaTeX-run differs from content of
% database-token-register stemming from database-file of
% the current LaTeX-run.
% If so, something changed and you need to re-run LaTeX.
% Probably this should be omitted with large databases.
\begingroup
\DTLsetseparator{#2}%
\DTLsetdelimiter{#3}%
\DTLgcleardb{#1DataToBeWrittenAtEndOfThisLaTeXRun}%
\DTLnewdbonloadfalse
\DTLloaddb{#1DataToBeWrittenAtEndOfThisLaTeXRun}{#1.csv}%
\edef\tempa{%
\the\csname dtldb@#1DataFromPreviousLaTeXRun\endcsname
\the\csname dtlkeys@#1DataFromPreviousLaTeXRun\endcsname
\the\csname dtlrows@#1DataFromPreviousLaTeXRun\endcsname
\the\csname dtlcols@#1DataFromPreviousLaTeXRun\endcsname
}%
\edef\tempb{%
\the\csname dtldb@#1DataToBeWrittenAtEndOfThisLaTeXRun\endcsname
\the\csname dtlkeys@#1DataToBeWrittenAtEndOfThisLaTeXRun\endcsname
\the\csname dtlrows@#1DataToBeWrittenAtEndOfThisLaTeXRun\endcsname
\the\csname dtlcols@#1DataToBeWrittenAtEndOfThisLaTeXRun\endcsname
}%
\ifx\tempa\tempb
\endgroup
\else
\endgroup
\@latex@warning@no@line{%
Database `#1' may have changed.
Rerun to get listings of entries etc right%
}%
\fi
}%
}%
}%
%=========================================================================
% Infrastructure for triggering a warning in case the value of a document-
% wide property was requested at a moment in time when the property didn't
% exist (yet).
%-------------------------------------------------------------------------
\AfterLastShipout{\PropertyValuesNotFound}%
\newcommand*\PropertyValuesNotFound{}%
\newcommand*\PropertyValuesNotFoundTrue{%
\gdef\PropertyValuesNotFound{\@latex@warning@no@line {There were undefined properties}}%
}%
\ExplSyntaxOn
%=========================================================================
% Property-List for properties that are to be maintained document-wide
% during the LaTeX-run. Values can change during the LaTeX-run.
% If you want to store the values which some properties have at a specific
% moment in time during the LaTeX-run, you can use the macro
% \DatabaseRowFromSomeOfThisDocumentsProperties
% for storing these properties' current values as another row of a
% database of name <base-name of databases and of .csv-file>DataToBeWrittenAtEndOfThisLaTeXRun
%-------------------------------------------------------------------------
\prop_new:N {\g__MyStuff_ThisDocumentsProperties_prop}
%=========================================================================
% expl3-scratch-variables:
%-------------------------------------------------------------------------
\prop_gput:Nxx \g__MyStuff_ThisDocumentsProperties_prop{Primary-Key}{0}
\tl_new:N {\l__MyStuff_ExtractedProperty_tl}
\clist_new:N{\l__MyStuff_PropertyNames_clist}
\bool_new:N \g__MyStuff_NewDatabaseRow_bool
\cs_new:Nn \__MyStuff_ExpandedDatabaseName: {}
%=========================================================================
% Set document-wide properties' values _globally_ by providing a
% <property1>=<value1>, <property2>=<value2>, ..., <propertyN>=<valueN>-list:
% \SetSomeOfThisDocumentsPropertiesFromKeyValList{%
% <property1>=<value1>, <property2>=<value2>, ..., <propertyN>=<valueN>
% }%
%
% If a property doesn't exist it is created anew automatically.
% If a property does exist, its value is overridden.
%-------------------------------------------------------------------------
\NewDocumentCommand{\SetSomeOfThisDocumentsPropertiesFromKeyValList}{m}{
\keyval_parse:NNn \__MyStuff_GSetSomeOfThisDocumentsPropertiesFromKeyValList:n
\__MyStuff_GSetSomeOfThisDocumentsPropertiesFromKeyValList:nn
{#1}
}
\cs_new:Nn \__MyStuff_GSetSomeOfThisDocumentsPropertiesFromKeyValList:n {
\__MyStuff_GSetSomeOfThisDocumentsPropertiesFromKeyValList:nn {#1}{\DTLstringnull}
}
\cs_new:Nn \__MyStuff_GSetSomeOfThisDocumentsPropertiesFromKeyValList:nn {
\group_begin:
\cs_set:Npn \protect { \noexpand\protect\noexpand }
\prop_gput:Nxx \g__MyStuff_ThisDocumentsProperties_prop{#1}{#2}
\group_end:
}
%=========================================================================
% Create a row of a database
% <base-name of databases and of .csv-file>DataToBeWrittenAtEndOfThisLaTeXRun
% from current values of document-wide properties by specifying a comma-
% separated list of property-names.
% Names of properties will be used as names of fields/columns of the database.
% If a field/column doesn't yet exist in the database, it will be created automatically.
% If the database itself doesn't exist, it will be created automatically
% by \DATABASEFILECreateIfNotExistent. (That's why you nee to specify
% <separator> and <delimiter>.)
% If a property doesn't exist, an error-message is raised and datatool's
% null-value for strings is provided as value to the database.
%
% \DatabaseRowFromSomeOfThisDocumentsProperties{<base-name of databases and of .csv-file>}%
% {<separator>}%
% {<delimiter>}%
% {<Property1>, <Property2>,..., <PropertyN>}%
%
%-------------------------------------------------------------------------
\NewDocumentCommand{\DatabaseRowFromSomeOfThisDocumentsProperties}{mmmm}{
\bool_gset_true:N \g__MyStuff_NewDatabaseRow_bool
\cs_set:Nx \__MyStuff_ExpandedDatabaseName: {#1DataToBeWrittenAtEndOfThisLaTeXRun}
\exp_args:Nx \DATABASEFILECreateIfNotExistent{#1}{#2}{#3}
\clist_set:Nn \l__MyStuff_PropertyNames_clist {#4}
\cs_gset:cpx {\__MyStuff_ExpandedDatabaseName: Cnt}
{\number\numexpr\use:c{\__MyStuff_ExpandedDatabaseName: Cnt}+1\relax}
\prop_gput:Nxx \g__MyStuff_ThisDocumentsProperties_prop {PrimaryKey} {\use:c{\__MyStuff_ExpandedDatabaseName: Cnt}}
\__MyStuff_DatabaseEntryFromThisDocumentsProperty:n {PrimaryKey}
\clist_map_function:NN \l__MyStuff_PropertyNames_clist \__MyStuff_DatabaseEntryFromThisDocumentsProperty:x
}
\cs_new:Nn \__MyStuff_DatabaseEntryFromThisDocumentsProperty:n {
\prop_get:NnN \g__MyStuff_ThisDocumentsProperties_prop {#1} \l__MyStuff_ExtractedProperty_tl
\exp_args:NV \quark_if_no_value:nTF \l__MyStuff_ExtractedProperty_tl {
\protect\PropertyValuesNotFoundTrue
% I am too lazy to delve into expl3's l3msg
\@latex@warning{Macro~ \token_to_str:N\DatabaseRowFromSomeOfThisDocumentsProperties:\space
Property~`#1'~undefined~-~using~value~\DTLstringnull\space instead~-}
\tl_set:Nn \l__MyStuff_ExtractedProperty_tl {\DTLstringnull}
}{}
\__MyStuff_DatabaseRowFromPropertyAndValue:onV \__MyStuff_ExpandedDatabaseName: {#1} \l__MyStuff_ExtractedProperty_tl
}
\cs_generate_variant:Nn \__MyStuff_DatabaseEntryFromThisDocumentsProperty:n {x}
\cs_new:Nn \__MyStuff_DatabaseRowFromPropertyAndValue:nnn {
\DTLifhaskey{#1}{#2}{}{
\DTLaddcolumn{#1}{#2}
}
\bool_if:NTF \g__MyStuff_NewDatabaseRow_bool{
\DTLnewrow{#1}
}{}
\bool_gset_false:N \g__MyStuff_NewDatabaseRow_bool
\group_begin:
%\cs_set:Npn \protect { \noexpand\protect\noexpand }
\cs_set:Npn \protect { \token_to_str:N }
\exp_args:Nnx \use:n {\DTLnewdbentry{#1}{#2}} {#3}
\group_end:
}
\cs_generate_variant:Nn \__MyStuff_DatabaseRowFromPropertyAndValue:nnn{onV}
%=========================================================================
% Retrieve the current value of a document-wide property.
%
% \GetDocumentsPropertyValue{<Property>}
%
% If a property doesn't exist, an error-message is raised and the
% tokens \textsf{??} are provided.
%
% Not used in the following code. But I wasn't aware of that at the time
% of composing all this. Probably it may be useful in other scenarios.
%-------------------------------------------------------------------------
\NewDocumentCommand{\GetDocumentsPropertyValue}{m}{
\__GetDocumentsPropertyValue:x{#1}
}
\cs_new:Nn \__GetDocumentsPropertyValue:n {
\prop_get:NnN \g__MyStuff_ThisDocumentsProperties_prop {#1} \l__MyStuff_ExtractedProperty_tl
\exp_args:NV \quark_if_no_value:nTF \l__MyStuff_ExtractedProperty_tl {
\protect\PropertyValuesNotFoundTrue
% I am too lazy to delve into expl3's l3msg
\@latex@warning{Macro~\token_to_str:N\GetDocumentsPropertyValue:\space
Property~`#1'~undefined~-~using~value~\token_to_str:N\textsf{??}~instead~-}
\tl_set:Nn \l__MyStuff_ExtractedProperty_tl {\textsf{??}}
}{}
\tl_use:N \l__MyStuff_ExtractedProperty_tl
}
\cs_generate_variant:Nn \__GetDocumentsPropertyValue:n {x}
%=========================================================================
% Via datatool-package's `\DTLforeach` iterate on the rows
% of the database <base-name of databases and of .csv-file>DataFromPreviousLaTeXRun.
%
% If the database itself doesn't exist, it will be created automatically
% by \DATABASEFILECreateIfNotExistent. (That's why you nee to specify
% <separator> and <delimiter>.)
%
% \DoWithDatabaseProperties{<base-name of databases and of .csv-file>}%
% {<separator>}%
% {<delimiter>}%
% [<condition>]%
% {<assign list>}%
% {<text>}%
%
% <base-name of databases and of .csv-file>, <separator> and <delimiter> are
% the same as above.
% <condition>, <assign list> and <text> are the same as with \DTLforeach
% of the datatool-package.
%-------------------------------------------------------------------------
\NewDocumentCommand\DoWithDatabaseProperties{mmmo}{
\exp_args:Nx \DATABASEFILECreateIfNotExistent {#1}{#2}{#3}
\IfNoValueTF{#4}{\exp_args:Nnx \use:n {\DTLforeach*}}
{\exp_args:Nnx \use:n {\DTLforeach*[{#4}]}}
{#1DataFromPreviousLaTeXRun}
}
\ExplSyntaxOff
\makeatother
%=========================================================================
% Now the individual building blocks of the infrastructure for managing
% document-wide properties via databases and .csv files are in place.
% They can now be used for implementing the macros at user level, which
% is straightforward:
%=========================================================================
% \ListOf...-commands can easily be implemented in terms of
% \DoWithDatabaseProperties.
%
% A scratch-switch which can be initialized by the \ListOf...-command
% and toggled by \DoWithDatabaseProperties's <text>-argument. This
% switch is intended to indicate whether at least one row of the
% database already ended up as an entry of the list.
%
% E.g., only in this case a heading for the list in question is needed.
% Only in this case starting/ending some environment is needed, e.g.,
% description or tabular.
%
\newif\ifNoDatabaseEntryEndedUpInTheListYet
\global\NoDatabaseEntryEndedUpInTheListYettrue
%=========================================================================
% An example of how to implement \registertable by means of
% \DoWithDatabaseProperties (whose underlying macro is \DTLforeach):
%-------------------------------------------------------------------------
\newtoks\scratchtoks
\newcommand\registertable{%
\global\NoDatabaseEntryEndedUpInTheListYettrue
\global\scratchtoks{}%
\DoWithDatabaseProperties{register}{,}{"}%
{%
\registeraddress=address,%
\registerformat=format,%
\registershortdescription=shortdescription,%
\registerlongdescription=longdescription%
}%
{%
\ifNoDatabaseEntryEndedUpInTheListYet
\global\NoDatabaseEntryEndedUpInTheListYetfalse
\fi
\global\scratchtoks\expandafter{\the\expandafter\scratchtoks
\expanded{%
\registeraddress&\registershortdescription&\registerformat&\registerlongdescription
}\\%
}%
}%
\ifNoDatabaseEntryEndedUpInTheListYet\else
\section*{Table of ...}%
\begin{longtable}[l]{|c|c|c|c|}%
\hline Address&shortdescription&format&reflink/long\\\hline\endhead
\hline\endfoot
\the\scratchtoks
\end{longtable}%
\fi
}%
\begin{document}
\registertable
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
address=Address 1, format=Format 1, shortdescription=Short 1, longdescription=Long 1
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
address=Address 2, format=Format 2, shortdescription=Short 2, longdescription=Long 2
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
address=Address 3, format=Format 3, shortdescription=Short 3, longdescription=Long 3
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
address=Address 4, format=Format 4, shortdescription=Short 4, longdescription=Long 4
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
address=Address 5, format=Format 5, shortdescription=Short 5, longdescription=Long 5
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
address=Address 6, format=Format 6, shortdescription=Short 6, longdescription=Long 6
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
address=Address 7, format=Format 7, shortdescription=Short 7, longdescription=Long 7
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
address=Address 8, format=Format 8, shortdescription=Short 8, longdescription=Long 8
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
address=Address 9, format=Format 9, shortdescription=Short 9, longdescription=Long 9
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
address=Address 10, format=Format 10, shortdescription=Short 10, longdescription=Long 10
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
address, format, shortdescription, longdescription
}%
%------------------------------------------------------------------------------------------
\SetSomeOfThisDocumentsPropertiesFromKeyValList{%
address=Address 11, format=Format 11, shortdescription=Short 11, longdescription=Long 11
}%
\DatabaseRowFromSomeOfThisDocumentsProperties{register}{,}{"}{%
address, format, shortdescription, longdescription
}%
Some text
Some text
\end{document}
答案2
我认为,您需要一个新的“...列表”。阅读手册tocloft
以了解如何执行此操作。您的\register
宏需要将适当的信息添加到列表中...
然而,也许您正在考虑某种表格结构,在这种情况下,我会将其留给您决定。