Я хочу создать базу данных, состоящую из пронумерованных блоков TeX-кода. В частности, я хочу сделать что-то вроде \mycommand{counterName}{Some text}
и сохранить новую запись где-то, чтобы использовать ее Some Text
позже в другой части документа. CounterName
Это счетчик, на который я могу сослаться, чтобы получить текст, вставленный в документ:
\newcounter{a}
\mycommand{a}{The first line}
\stepcounter{a}
\mycommand{a}{The second line}
.........
print{2}
А TeX создаст «Вторую линию».
Я ищу что-то вроде glossaries
пакета, но с возможностью выдавать не весь список сразу, а конкретную запись, чтобы выглядело так, будто команда \print{n}
просто заменяется текстом в команде \mycommand{n}{TEXT}
.
Просматривая TeX.SX, я нашел некоторые ответы о datatool
пакетах и Lua
кодах, но не нашел решения, которое я мог бы применить.
Я был бы очень признателен за краткое объяснение того, как использовать datatool
, glossaries
или что-то подобное для этого материала.
решение1
Поскольку вы упомянули datatool
и glossaries
, вот несколько альтернатив.
С datatool
, самый простой метод требует определения всех записей в порядке возрастания (без использования счетчика). Номер строки обеспечивает индексацию.
\documentclass{article}
\usepackage{datatool}
\DTLnewdb{data}
\newcommand{\addline}[1]{%
\DTLnewrow{data}%
\DTLnewdbentry{data}{Text}{#1}%
}
\newcommand{\print}[1]{%
\DTLgetvalue{\thisval}{data}{#1}{1}%
\thisval
}
\addline{The first line}
\addline{The second line}
\begin{document}
Line 2: \print{2}.
All lines:
\DTLforeach*{data}{\Text=Text}{\DTLcurrentindex. \Text.\par}
\end{document}
Это производит:
Строка 2: Вторая строка.
Все строки:
1. Первая строка.
2. Вторая строка.
Если вы хотите определить записи в неправильном порядке с помощью счетчика, это можно сделать с помощью дополнительного столбца:
\documentclass{article}
\usepackage{datatool}
\DTLnewdb{data}
\newcommand{\addline}[2]{%
\DTLnewrow{data}%
\dtlexpandnewvalue
\DTLnewdbentry{data}{Index}{\the\value{#1}}%
\dtlnoexpandnewvalue
\DTLnewdbentry{data}{Text}{#2}%
}
\newcommand{\print}[1]{%
\dtlgetrowindex{\thisrowidx}{data}{1}{#1}%
\ifx\thisrowidx\dtlnovalue
Not found!%
\else
\DTLgetvalue{\thisval}{data}{\thisrowidx}{2}%
\thisval
\fi
}
\newcounter{a}
\setcounter{a}{2}
\addline{a}{The second line}
\setcounter{a}{1}
\addline{a}{The first line}
\begin{document}
Line 2: \print{2}.
All lines:
\DTLforeach*{data}{\theIndex=Index,\Text=Text}{\theIndex. \Text.\par}
\end{document}
Это производит:
Строка 2: Вторая строка.
Все строки:
2. Вторая строка.
1. Первая строка.
Список теперь не имеет числового порядка, но соответствует порядку, в котором были определены блоки. Вы можете отсортировать их перед отображением списка:
\DTLsort{Index}{data}
\DTLforeach*{data}{\theIndex=Index,\Text=Text}{\theIndex. \Text.\par}
Строка 2: Вторая строка.
Все строки:
1. Первая строка.
2. Вторая строка.
Вот glossaries
подход:
\documentclass{article}
\usepackage{glossaries-extra}
\glssetexpandfield{name}
\newcommand{\addline}[2]{%
\edef\thisidx{\the\value{#1}}%
\newglossaryentry{\thisidx}{name={\thisidx},description={#2}}%
}
\newcommand{\print}[1]{%
\glsentrydesc{#1}%
}
\newcounter{a}
\setcounter{a}{2}
\addline{a}{The second line}
\setcounter{a}{1}
\addline{a}{The first line}
\begin{document}
Line 2: \print{2}.
All lines:
\renewcommand{\glstreenamefmt}[1]{#1}
\renewcommand{\glossarysection}[2][]{}
\printunsrtglossary[style=index]
\end{document}
Это производит:
Опять же, это списки в порядке определения. Если вы хотите, чтобы список был отсортирован, вы можете использовать следующее:
\documentclass{article}
\usepackage[automake,nopostdot]{glossaries}
\makeglossaries
\glssetexpandfield{name}
\newcommand{\addline}[2]{%
\edef\thisidx{\the\value{#1}}%
\newglossaryentry{\thisidx}{name={\thisidx},description={#2}}%
}
\newcommand{\print}[1]{%
\glsentrydesc{#1}\glsadd{#1}%
}
\newcounter{a}
\setcounter{a}{2}
\addline{a}{The second line}
\setcounter{a}{1}
\addline{a}{The first line}
\begin{document}
Line 2: \print{2}.
All lines:
\renewcommand{\glstreenamefmt}[1]{#1}
\renewcommand{\glossarysection}[2][]{}
\printglossary[style=index,nonumberlist]
\end{document}
Это производит:
Строка 2: Вторая строка.
Все строки:
2 Вторая строка
Это выводит только запись, которая была проиндексирована (с \glsadd
). Если вы хотите, чтобы были перечислены все записи, используйте \glsaddall
(после того, как все записи были определены).
\documentclass{article}
\usepackage[automake,nopostdot]{glossaries}
\makeglossaries
\glssetexpandfield{name}
\newcommand{\addline}[2]{%
\edef\thisidx{\the\value{#1}}%
\newglossaryentry{\thisidx}{name={\thisidx},description={#2}}%
}
\newcommand{\print}[1]{%
\glsentrydesc{#1}%
}
\newcounter{a}
\setcounter{a}{2}
\addline{a}{The second line}
\setcounter{a}{1}
\addline{a}{The first line}
\glsaddall
\begin{document}
Line 2: \print{2}.
All lines:
\renewcommand{\glstreenamefmt}[1]{#1}
\renewcommand{\glossarysection}[2][]{}
\printglossary[style=index,nonumberlist]
\end{document}
Это производит:
Строка 2: Вторая строка.
Все строки:
1 Первая строка
2 Вторая строка
Расширение
Важно, чтобы значение индексации \the\value{#1}
было полностью развернуто перед сохранением, в противном случае оно будет продолжать меняться по мере изменения значения счетчика. Оба datatool
и glossaries
имеют возможность включать или выключать расширение при добавлении/определении новой записи.
В случае datatool
, расширение включается с помощью \dtlexpandnewvalue
. В случае glossaries
, расширение включается для определенного поля с помощью\glssetexpandfield{
метка поля}
.
Код, который вы хотите добавить (в последнем аргументе \addline
), может содержать хрупкие команды, в этом случае важно не расширять значение. С datatool
, расширение снова отключается с помощью \dtlnoexpandnewvalue
. С glossaries
, значение сохраняется в description
ключе, и расширение отключено по умолчанию для этого поля.
решение2
Следующее основано на предположении, что \printlistitem
происходитпосле \addlistitem
:
\documentclass{article}
\usepackage{xparse}
\newcounter{listitem}
\NewDocumentCommand{\addlistitem}{o m}{%
\IfValueTF{#1}
{\expandafter\def\csname #1-list\endcsname{#2}}
{\stepcounter{listitem}%
\begingroup\edef\x{\endgroup\noexpand\expandafter
\def\noexpand\csname \thelistitem-list\noexpand\endcsname}%
\x{#2}}%
}
\newcommand{\printlistitem}[1]{%
\ifcsname #1-list\endcsname
\csname #1-list\endcsname
\else
Item~#1 does not exist.
\fi
}
\begin{document}
\addlistitem{The first line}% 1
\addlistitem[B]{The second line}% C
\addlistitem{The third line}% 2
\printlistitem{2}
\printlistitem{1}
\printlistitem{3}
\printlistitem{B}
\end{document}
Можно добавить больше модификаций, включая проверку/обработку ошибок и модификацию для обработки обратных ссылок (с использованием -setup \label
) \ref
.
Вам может быть интересно добавить , \printallitems
чтобы перечислить все добавленные вами элементы в форме ToC. Следующий пример имитирует это, устанавливая каждый \addlistitem
как \section
. Вы можете вносить уточнения в представление по мере необходимости:
\documentclass{article}
\usepackage{xparse,tocloft}
\newcounter{listitem}
\NewDocumentCommand{\addlistitem}{o m}{%
\IfValueTF{#1}
{\expandafter\def\csname #1-list\endcsname{#2}%
\addcontentsline{los}{listitem}{\protect\numberline{#1} #2}}
{\stepcounter{listitem}%
\begingroup\edef\x{\endgroup\noexpand\expandafter
\def\noexpand\csname \thelistitem-list\noexpand\endcsname}%
\x{#2}%
\addcontentsline{los}{listitem}{\protect\numberline{\thelistitem} #2}}%
}
\newcommand{\printlistitem}[1]{%
\ifcsname #1-list\endcsname
\csname #1-list\endcsname
\else
Item~#1 does not exist.
\fi
}
\makeatletter
\let\l@listitem\l@section
\newcommand{\printallitems}{{%
\renewcommand{\cftsecfont}{\mdseries}% Add more ToC-related tuning here
\@starttoc{los}}}
\makeatother
\begin{document}
\printallitems
\bigskip
\addlistitem{The first line}% 1
\addlistitem[B]{The second line}% C
\addlistitem{The third line}% 2
\printlistitem{2}
\printlistitem{1}
\printlistitem{3}
\printlistitem{B}
\end{document}