我有一份如下的文件:
\documentclass{report}
\begin{document}
\section{A}
\subsection{a}
\element\element
\subsection{b}
\element\element\element
\subsection{c}
\element\element\element\element
\section{B}
\subsection{a}
\element\element
\subsection{b}
\element
\end{document}
其中我重新定义了section
和subsection
宏,并定义了一个\element
宏。它们都采用几个参数来格式化输出,但我认为这与解决此处的问题无关,所以我只留下了第一个参数,即节/子节的名称。
我想汇总\element
各节和子节中宏的使用情况,并将这些统计信息记录到日志文件中。因此,对于给定的实例,输出应该是:
9
2
3
4
3
2
1
使用该totcount
包,我首先尝试创建两个变量\sectionname
和\subsectionname
,然后\newtotcounter{c\sectionname\subsectionname}
在评估\section
和时根据这两个变量的连接创建一个计数器\subsection
。但是,只有最后一节和小节被正确计数,-1
其余部分则打印出来,表明计数器根本没有增加。第一次编译期间生成的文件仅列出最后两个计数器的事实也表明了这一点.aux
。查看包的代码时,我注意到使用了宏,然后假设只保留了最后一节和小节,因为每次调用和宏\AtEndDocument
时,这个宏都不会看到更新的名称。因此,我转而为每个节和小节创建一个不同的计数器:\section
\subsection
\usepackage{totcount}
\newwrite\statsfile
\immediate\openout\statsfile=stats.txt
\newcounter{sectionid}
\newcounter{subsectionid}
\newcommand{\createsectioncounter}{
\stepcounter{sectionid}
\newtotcounter{sec\roman{sectionid}}
}
\newcommand{\createsubsectioncounter}{
\stepcounter{subsectionid}
\newtotcounter{subsec\roman{subsectionid}}
}
\renewcommand{\section}[1]{
\createsectioncounter
\immediate\write\statsfile{\number\totvalue{sec\roman{sectionid}}}
}
\renewcommand{\subsection}[1]{
\createsubsectioncounter
\immediate\write\statsfile{\number\totvalue{subsec\roman{subsectionid}}}
}
\newcommand{\element}{
\stepcounter{sec\roman{sectionid}}
\makeatletter
\ifcsname c@subsec\roman{subsectionid}\endcsname
\stepcounter{subsec\roman{subsectionid}}
\fi
\makeatother
}
输出:
-1
-1
-1
-1
3
-1
1
显然存在同样的问题。.aux
文件内容如下:
\relax
\expandafter\ifx\csname c@secii@totc\endcsname\relax\newcounter{secii@totc}\fi\setcounter{secii@totc}{3}
\expandafter\ifx\csname c@subsecv@totc\endcsname\relax\newcounter{subsecv@totc}\fi\setcounter{subsecv@totc}{1}
\expandafter\ifx\csname c@subsecv@totc\endcsname\relax\newcounter{subsecv@totc}\fi\setcounter{subsecv@totc}{1}
\expandafter\ifx\csname c@subsecv@totc\endcsname\relax\newcounter{subsecv@totc}\fi\setcounter{subsecv@totc}{1}
\expandafter\ifx\csname c@secii@totc\endcsname\relax\newcounter{secii@totc}\fi\setcounter{secii@totc}{3}
\expandafter\ifx\csname c@subsecv@totc\endcsname\relax\newcounter{subsecv@totc}\fi\setcounter{subsecv@totc}{1}
\expandafter\ifx\csname c@subsecv@totc\endcsname\relax\newcounter{subsecv@totc}\fi\setcounter{subsecv@totc}{1}
我怎样才能实现这种行为,而不让 totcount 只考虑最后一个计数器?
注意:我对将统计数据输出为 PDF 不感兴趣。
注意:我知道这个totalcount
软件包,但不知道它能有什么用处。但如果它能解决问题,我很乐意使用它。
答案1
另一个expl3
答案采用增量方法,其中元素计数存储在属性列表(即键值数组)中,并以(子)部分编号作为键。功能与其他答案大致相同(一个小小的区别是,没有元素的子部分计为 0),但实现可能更接近原始 MWE。
有四个整数变量,两个用于节和子节编号,两个用于计算当前节和子节的元素数量。计数存储在形式为的数组中section_stats_prop[section_number] = number_of_elements
。内部\element
值会增加。如果在同一节或子节中添加元素,则数组中的值将被覆盖。
最后,这些值被写入文本文件。可以使用 打印键值数组\prop_map_inline
,但是值的顺序无法保证。因此,将存储一个单独的列表,其中仅按顺序排列(子)部分编号。此列表用于提供打印键值数组的键。
代码:
\documentclass{report}
\usepackage{xparse}
\newwrite\statsfile
\immediate\openout\statsfile=stats.txt
\ExplSyntaxOn
\prop_new:N \g_section_stats_prop % array with counts
\seq_new:N \g_section_numbers_seq % array with (sub)section numbers
\int_new:N \g_sec_count % section counter
\int_new:N \g_subsec_count % subsection counter
\int_new:N \g_current_sec % elements in current section
\int_new:N \g_current_subsec % elements in current subsection
\RenewDocumentCommand\section{m}{
\int_incr:N \g_sec_count % increase section counter
\int_zero:N \g_subsec_count % set subsection counter to 0
\int_zero:N \g_current_sec % set section element counter to 0
\int_zero:N \g_current_subsec % set subsection element counter to 0
% store section number in array of (sub)section numbers
\seq_put_right:NV \g_section_numbers_seq {\g_sec_count}
% initialize counts array for this section to 0
\prop_gput:NVn \g_section_stats_prop {\g_sec_count}{0}
}
\RenewDocumentCommand\subsection{m}{
\int_incr:N \g_subsec_count % increase subsection counter
\int_zero:N \g_current_subsec % set subsection element counter to 0
% compose subsection number as section-subsection
\str_set:Nx \g_seccn_str {\int_use:N \g_sec_count - \int_use:N \g_subsec_count}
% store subsection number in array of (sub)section numbers
\seq_put_right:NV \g_section_numbers_seq {\g_seccn_str}
% initialize counts array for this subsection to 0
\prop_gput:NVn \g_section_stats_prop {\g_seccn_str}{0}
}
\NewDocumentCommand\element{}{
\int_incr:N \g_current_sec % increase element counter for section
\int_incr:N \g_current_subsec % increase element counter for subsection
% overwrite counts array for this section with new value
\prop_gput:NVV \g_section_stats_prop {\g_sec_count}{\g_current_sec}
% compose subsection number
\str_set:Nx \g_seccn_str {\int_use:N \g_sec_count - \int_use:N \g_subsec_count}
% overwrite counts array for this subsection with new value
\prop_gput:NVV \g_section_stats_prop {\g_seccn_str}{\g_current_subsec}
}
% print counts array to file using order of (sub)section number array
\NewDocumentCommand{\printseq}{}{
\seq_map_inline:Nn \g_section_numbers_seq {
\immediate\write\statsfile{##1:\space\prop_item:Nn \g_section_stats_prop {##1}}
}
}
\ExplSyntaxOff
\begin{document}
\section{A}
\element
\subsection{a}
\element\element
\subsection{b}
\element\element\element
\subsection{c}
\element\element\element\element
\section{B}
\subsection{a}
\element\element
\subsection{b}
\element
\subsection{c}
\printseq
\end{document}
stats.txt
:
1: 10
1-1: 2
1-2: 3
1-3: 4
2: 3
2-1: 2
2-2: 1
2-3: 0
答案2
假设仅包括章节和小节,我的想法是定义\element
它以便在按顺序调用时存储章节/小节的状态。
在文档结束时,对序列进行处理以获得总数。
每个项目都具有以下形式{<section>}{<subsection>}
,我为每个部分映射一次序列;对于与当前部分索引匹配的项目,我增加计数器并为相应的子部分设置一个 intarray 项目。
作业结束时,终端上会为\element
出现的每个部分和子部分写一行,并显示条目数。.aux
文件中会写出相同的信息,可轻松从中检索。
\documentclass{article}
\usepackage{xparse}
\newcommand{\realelement}{I'm an element}
\ExplSyntaxOn
\NewDocumentCommand{\element}{}
{
\seq_gput_right:Nx \g_element_section_seq { {\arabic{section}}{\arabic{subsection}} }
\realelement
}
\AtEndDocument{\__element_process:}
\seq_new:N \g_element_section_seq
\int_new:N \l__element_section_int
\int_new:N \l__element_subsection_int
\intarray_new:Nn \g__element_subsection_intarray { 1000 }
\cs_new_protected:Nn \__element_process:
{
\int_step_inline:nn { \value{section} }
{
\__element_process_section:n { ##1 }
}
}
\cs_new_protected:Nn \__element_process_section:n
{
\int_zero:N \l__element_section_int
\int_zero:N \l__element_subsection_int
\intarray_gzero:N \g__element_subsection_intarray
\seq_map_inline:Nn \g_element_section_seq
{
\__element_process_item:nnn { #1 } ##1
}
\__element_write_section:n { #1 }
}
\cs_new_protected:Nn \__element_process_item:nnn
{
\str_if_eq:nnT { #1 } { #2 }
{
\int_incr:N \l__element_section_int
\int_compare:nT { #3 > \l__element_subsection_int }
{
\int_set:Nn \l__element_subsection_int { #3 }
}
\intarray_gset:Nnn \g__element_subsection_intarray { #3 }
{ \intarray_item:Nn \g__element_subsection_intarray { #3 } + 1 }
}
}
\cs_new_protected:Nn \__element_write_section:n
{
\typeout{Section~#1:~\int_to_arabic:n { \l__element_section_int } }
\iow_now:cx { @auxout }
{
\token_to_str:N \elementsection {#1} { \int_to_arabic:n { \l__element_section_int } }
}
\int_step_inline:nn { \l__element_subsection_int }
{
\typeout{Subsection~#1.##1:~\intarray_item:Nn \g__element_subsection_intarray { ##1 }}
\iow_now:cx { @auxout }
{
\token_to_str:N \elementsubsection {#1} {##1} { \intarray_item:Nn \g__element_subsection_intarray { ##1 } }
}
}
}
\NewDocumentCommand{\elementsection}{mm}{}
\NewDocumentCommand{\elementsubsection}{mmm}{}
\ExplSyntaxOff
\begin{document}
\section{A}
\subsection{a}
\element\element
\subsection{b}
\element\element\element
\subsection{c}
\element\element\element\element
\section{B}
\subsection{a}
\element\element
\subsection{b}
\element
\end{document}
终端上的输出将是
Section 1: 9
Subsection 1.1: 2
Subsection 1.2: 3
Subsection 1.3: 4
Section 2: 3
Subsection 2.1: 2
Subsection 2.2: 1
在.aux
文件中我们将有
\elementsection{1}{9}
\elementsubsection{1}{1}{2}
\elementsubsection{1}{2}{3}
\elementsubsection{1}{3}{4}
\elementsection{2}{3}
\elementsubsection{2}{1}{2}
\elementsubsection{2}{2}{1}