如何在辅助文件中检索 zref 标签的顺序

如何在辅助文件中检索 zref 标签的顺序

我正在玩一个非常有趣的游戏zref,但由于我的扩展能力不足,我一度陷入困境,也许有人会发现这个问题更容易解决。

zref\protected@write使用(不是)将标签写入辅助文件,\immediate以便标签按照这些标签引用的对象被发送出去的顺序写入那里(据我所知,如果这个假设是错误的,请告诉我,并忽略以下内容)。而且我想我可以使用标签的“发送顺序”来推断(至少是近似的)页面上不同标签的相对位置(哪个在前,哪个在后)。所以我试图按照它们在辅助文件中出现的顺序检索标签序列。

zref辅助文件中存储的信息位于名为的宏下\zref@newlabel,其定义如下:

\def\ZREF@RefPrefix{Z@R}
\ZREF@Robust\edef\zref@newlabel{%
  \noexpand\@newl@bel{\ZREF@RefPrefix}%
}

(据我所知,对于当前的 TeXLive,也就是我这里所读的,\ZREF@Robust将是\protected)。

我的想法是,我可以使用(修补/挂钩/重新定义)这个宏来完成它要做的事情,同时将clist我想要的信息提供给(L3)。

但是,是文档\zref@newlabel中提到的那些“向前看的命令”之一。事实上,接收 3 个参数,并且只传递第一个参数,后面两个参数将被向前扫描。据我所知,这意味着使用对于这种情况是不可行的。(编辑:正如我后来发现的,不可行的另一个原因是,当读取 .aux 文件时,钩子实际上不可用,它们仅在之后出现)。ltcmdhooks\@newl@bel\zref@newlabelltcmdhooksltcmdhooks\begin{document}

我试图重新定义它,但不幸的是,我无法适当地控制扩展,所以这里的问题是我如何正确地进行这样的重新定义。

这是一个说明情况的 MWE,其中包括我的失败尝试:

\documentclass{book}

\usepackage[user]{zref}
\usepackage{hyperref}% just for \phantomsection

\usepackage{econlipsum}

\ExplSyntaxOn
\makeatletter

\clist_new:N \g__auxfile_labelseq_clist

% That's more or less what I'd like to do, but... I can't make it work.
% \cs_gset_protected:Npn \zref@newlabel #1#2 {
%   \clist_gput_right:Nn \g__zrefcheck_auxfile_labelseq_clist {#1}
%   \exp_not:N \@newl@bel {\ZREF@RefPrefix} {#1} {#2}
% }

\makeatother
\ExplSyntaxOff

\begin{document}

\chapter{Chapter 1}
\zlabel{cha:chapter-1}

\econ[1]

\begin{figure}[b]
  \centering
  \caption{Figure 1}
  \zlabel{fig:figure-1}
\end{figure}

A reference to the figure ``above''~\zref{fig:figure-1}%
\phantomsection\zlabel{fig:figure-1:reference}.

\end{document}

答案1

Ulrike 在评论中的建议(如何在辅助文件中检索 zref 标签的顺序) 很好,确实有效,但它涉及\ZREF@RefPrefix重新定义的硬编码。虽然在这种情况下这应该是无害的,但我还是对此事进行了进一步的思考,最终得出以下结论:

\seq_new:N \g__zrefcheck_auxfile_labelseq_seq

\ZREF@Robust\edef\zref@newlabel#1{
  \noexpand\seq_gput_right:Nn \noexpand\g__zrefcheck_auxfile_labelseq_seq {#1}
  \noexpand\@newl@bel{\ZREF@RefPrefix}{#1}
}

据我所知,它的行为方式与原始版本(当然,加上添加的代码)完全相同。(看来我在这个过程中学到了一些东西 ;-)

然而,与此同时,我发现我肯定不是唯一一个对现有标签列表感兴趣的人。CTAN 上至少有两个包也检索此信息:crossreftoolslistlbls。第一个为此目的构建了一个特殊的 toc(带有相应的文件),而后者直接读取 .aux 文件。

listlbls是基于 David Carlisle 的回答(所有带超链接的标签列表) 在 TeX.SX 上。我认为这是一个很好的方法,因为它不需要重新定义任何内部或额外的基础设施。可惜的是,这个包是为处理常规标签而设计的,而不是 的标签zref,而且并没有真正以可检索的方式公开信息(据我所知)。所以我做了一个改编版本(一个穷人版):

\seq_new:N \g__zrefcheck_auxfile_labelseq_seq

\tl_set:Nn \g_tmpa_tl { \c_sys_jobname_str .aux }
\file_if_exist:nT { \g_tmpa_tl }
  {
    \ior_open:Nn \g_tmpa_ior { \g_tmpa_tl }
    \group_begin:
      \ior_map_inline:Nn \g_tmpa_ior
        {
          \tl_set:Nn \l_tmpa_tl { \zref@newlabel }
          \tl_set:Nx \l_tmpb_tl { \tl_item:nn {#1} { 1 } }
          \tl_if_eq:NNT \l_tmpa_tl \l_tmpb_tl
            {
              \tl_set:Nx \l_tmpa_tl { \tl_item:nn {#1} { 2 } }
              \seq_gput_right:NV \g__zrefcheck_auxfile_labelseq_seq \l_tmpa_tl
            }
        }
    \group_end:
    \ior_close:N \g_tmpa_ior
  }

答案2

您可以修补\zref@newlabel以计数标签,将标签的编号和名称添加到 expl3 序列,将标签的编号作为属性的值添加labelnumber到标签本身,以及使用\@newl@bel不同的前缀将 zref 标签的编号映射到其名称:

\errorcontextlines=10000

\documentclass{article}
\usepackage[user]{zref}
\usepackage{atveryend}

\makeatletter
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
\zref@newprop{labelnumber}{0}%
\newcommand*\labelcount{0}%
\ZREF@Robust\edef\zref@newlabel{%
  \xdef\noexpand\labelcount{\noexpand\number\numexpr\noexpand\labelcount+1\relax}%
  \noexpand\expandafter\noexpand\AddElementAndCallNewlabel\noexpand\expandafter{\noexpand\labelcount}{\ZREF@RefPrefix}%
}
\makeatother
\ExplSyntaxOn
\makeatletter
\NewDocumentCommand\AddElementAndCallNewlabel {mmmm} {
   % #1 = number of label
   % #2 \@newl@bel-prefix
   % #3 name of label
   % #4 property-list of label
   \__MyStuff_AddElement:nn{#1}{#3}
   \@newl@bel{MYmapping}{#1}{#3}
   \@newl@bel{#2}{#3}{#4\labelnumber{#1}}
}
\newcommand\GetLabelNameFromLabelNumber[2]{
   % #1 = number of label
   % #2 = tokens in case no label name is mapped to number of label
   \romannumeral
   \cs_if_exist:cTF{MYmapping@#1}
                   { \exp_args:Nc \use_ii_i:nn 
                                  {MYmapping@#1}
                                  {\exp_args:No \use_ii_i:nn}{\UD@stopromannumeral} }
                   {\UD@stopromannumeral#2}
}
% Error-message in case no label name is mapped to number of label;
% like zref@refused, but for prefix MYmapping instead of \ZREF@RefPrefix=Z@R:
\NewDocumentCommand\LabelNumberNotMapped{m}{
   % #1 = term denoting number of label
  \protect\G@refundefinedtrue\@latex@warning {No~zref-label~is~mapped~to~number~`#1'~on~page~\thepage}
}
\makeatother
\seq_new:N \g__zrefcheck_auxfile_labelseq_seq
\cs_new:Nn \__MyStuff_AddElement:nn {
  \seq_gput_right:Nn \g__zrefcheck_auxfile_labelseq_seq {{#1}{#2}}
}
\NewDocumentCommand \ListOfZrefLabels {} {%
  \seq_map_function:NN \g__zrefcheck_auxfile_labelseq_seq \__MyStuff_PrintElement:n
}
\cs_new:Nn \__MyStuff_PrintElement:n {
  \__MyStuff_PrintElement:nn #1
}
\cs_new:Nn \__MyStuff_PrintElement:nn{
  #1.\nobreakspace#2,~
}
\AfterLastShipout{\xdef\labelcount{0}\cs_set:Nn \__MyStuff_AddElement:nn {}}%
\ExplSyntaxOff

\makeatletter

% For the sake of having fun define a loop which prints the names of all labels defined
% before and after the label specified as argument:

\@ifdefinable\gobbletorelax{\long\def\gobbletorelax#1\relax{}}%
\@ifdefinable\delivertorelax{\long\def\delivertorelax#1\relax{#1}}%
\newcommand\UD@CheckWhetherRelax[1]{%
  \ifcat$\detokenize\expandafter{\gobbletorelax#1\relax}$%
  \expandafter\@secondoftwo\else\expandafter\@firstofone\fi
  {%
     \ifcat$\detokenize\expandafter{\romannumeral\delivertorelax\UD@stopromannumeral#1}$%
     \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
     {\@firstoftwo}%
  }%
  {\@secondoftwo}%
}%
\newcommand\AllPreceedingAndFollowingLabels[2]{%
   \romannumeral
   \zref@ifrefundefined{#1}{\UD@stopromannumeral#2}{%
     \AllSubSequentLabelsLoop{#1}{%
        \AllSubSequentLabelsLoop{#1}%
                                {\expandafter\UD@stopromannumeral\@firstofone}%
                                {+}%
     }{-}{\textbf{#1}}%
   }%
}%
\newcommand\jointhem[4]{\AllSubSequentLabelsLoop{#1}{#2}{#3}{#4, #1}}%
\newcommand\jointhemexchanged[4]{\AllSubSequentLabelsLoop{#1}{#2}{#3}{#1, #4}}%
\newcommand\AllSubSequentLabelsLoop[4]{%
  % #1 = name of label.
  % #2 = tokens to aplly to the list of label-names gathered so far when the loop is done.
  % #3 = +/- ; marker for obtaining name of following/previous label.
  % #4 = list of label-names gathered so far.
  \expandafter\expandafter\expandafter\UD@CheckWhetherRelax
  \expandafter\expandafter\expandafter{%
    \GetLabelNameFromLabelNumber{%
      \number\numexpr\zref@extractdefault{#1}{labelnumber}{-2}#31\relax
    }{\relax}%
  }%
  {#2{#4}}%
  {%
    \ifx#3-%
    \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
    {\expandafter\expandafter\expandafter\jointhemexchanged}%
    {\expandafter\expandafter\expandafter\jointhem}%
    \expandafter\expandafter\expandafter{%
      \GetLabelNameFromLabelNumber{%
        \number\numexpr\zref@extractdefault{#1}{labelnumber}{-2}#31\relax
      }{\relax}%
    }{#2}{#3}{#4}%
  }%
}%

\makeatother

\begin{document}

\hrulefill

Some text.

\hrulefill

\ListOfZrefLabels

\hrulefill

\makeatletter

LabelUndef has number \zref@extractdefault{LabelUndef}{labelnumber}{\zref@refused{LabelUndef}\textsf{-not available-}}%

LabelA has number \zref@extractdefault{LabelA}{labelnumber}{\zref@refused{LabelA}\textsf{-not available-}}%

LabelB has number \zref@extractdefault{LabelB}{labelnumber}{\zref@refused{LabelB}\textsf{-not available-}}%

LabelC has number \zref@extractdefault{LabelC}{labelnumber}{\zref@refused{LabelC}\textsf{-not available-}}%

LabelD has number \zref@extractdefault{LabelD}{labelnumber}{\zref@refused{LabelD}\textsf{-not available-}}%

LabelE has number \zref@extractdefault{LabelE}{labelnumber}{\zref@refused{LabelE}\textsf{-not available-}}%

\hrulefill

The name of label number 0 is 
\GetLabelNameFromLabelNumber{0}{\LabelNumberNotMapped{0}\textsf{-not available-}}

The name of label number 1 is 
\GetLabelNameFromLabelNumber{1}{\LabelNumberNotMapped{1}\textsf{-not available-}}

The name of label number 2 is 
\GetLabelNameFromLabelNumber{2}{\LabelNumberNotMapped{2}\textsf{-not available-}}

The name of label number 3 is 
\GetLabelNameFromLabelNumber{3}{\LabelNumberNotMapped{3}\textsf{-not available-}}

The name of label number 4 is 
\GetLabelNameFromLabelNumber{4}{\LabelNumberNotMapped{4}\textsf{-not available-}}

The name of label number 5 is 
\GetLabelNameFromLabelNumber{5}{\LabelNumberNotMapped{5}\textsf{-not available-}}

The name of label number 6 is 
\GetLabelNameFromLabelNumber{6}{\LabelNumberNotMapped{6}\textsf{-not available-}}

The name of label number W is 
\GetLabelNameFromLabelNumber{W}{\LabelNumberNotMapped{W}\textsf{-not available-}}

The name of the label which was placed after LabelC is
\GetLabelNameFromLabelNumber{%
  \number\numexpr\zref@extractdefault{LabelC}{labelnumber}{-2}+1\relax
}{\LabelNumberNotMapped{%
    <Number of LabelC>+1
  }%
  \textsf{-not available-}%
}

The name of the label which was placed after LabelE is
\GetLabelNameFromLabelNumber{%
  \number\numexpr\zref@extractdefault{LabelE}{labelnumber}{-2}+1\relax
}{\LabelNumberNotMapped{%
    <Number of LabelE>+1
  }%
  \textsf{-not available-}%
}


\hrulefill

\AllPreceedingAndFollowingLabels{LabelUndef}{\zref@refused{LabelUndef}\textsf{-not available-}}%

\AllPreceedingAndFollowingLabels{LabelA}{\zref@refused{LabelA}\textsf{-not available-}}%

\AllPreceedingAndFollowingLabels{LabelB}{\zref@refused{LabelB}\textsf{-not available-}}%

\AllPreceedingAndFollowingLabels{LabelC}{\zref@refused{LabelC}\textsf{-not available-}}%

\AllPreceedingAndFollowingLabels{LabelD}{\zref@refused{LabelD}\textsf{-not available-}}%

\AllPreceedingAndFollowingLabels{LabelE}{\zref@refused{LabelE}\textsf{-not available-}}%

\hrulefill

\zref@labelbylist{LabelA}{main}%

\zref@labelbylist{LabelB}{main}%

\zref@labelbylist{LabelC}{main}%

\zref@labelbylist{LabelD}{main}%

\zref@labelbylist{LabelE}{main}%

\end{document}

在此处输入图片描述

相关内容