由于需要处理大量的页面范围,我希望有一个强大而简单的命令可以正确地解析和打印它们。
最简单的情况是
264,15-26
意思是:
第 264 页,第 15 至 26 行
然后我们就有了类似
264,15-266,26
也就是说
第 264 页第 15 行至第 266 页第 26 行
更棘手的是,我们会得到类似
264,15-266,26+277,13-14+312,14-316,23
这意味着我们实际上这里有三个序列:
从第 264 页第 15 行到第 266 页第 26 行和第 277 页第 13-14 行以及从第 312 页第 14 行到第 316 页第 23 行
也就是说,无论提供多少“类型 1”或“类型 2”序列,命令都应该起作用。
我想将页面范围(类型 1 或 2 或 3)放在一个命令中,然后以可读和准确的方式打印其中的每一个......对于像我这样可怜的老人文学生来说,这相当棘手!
答案1
这是一个 LuaLaTeX 解决方案:
最小工作示例
\documentclass{article}
\directlua{dofile("parse.lua")}
\def\parserange#1{\directlua{tex.sprint(parsePageRanges("#1"))}}
\begin{document}
\begin{itemize}
\item \parserange{264,15-26}
\item \parserange{264,15-266,26}
\item \parserange{264,15-266,26+277,13-14+312,14-316,23}
\end{itemize}
\end{document}
结果
Lua 代码
保存在文件中parse.lua
function split(text, sep)
sep = sep or "\n"
local lines = {}
local pos = 1
while true do
local b,e = text:find(sep, pos)
if not b then table.insert(lines, text:sub(pos)) break end
table.insert(lines, text:sub(pos, b-1))
pos = e + 1
end
return lines
end
function parsePageRanges(s)
ranges=split(s, "+")
parsed_ranges = {}
for i,range in ipairs(ranges) do
ends = split(range, "-")
if #ends ~=2 then
return("Error in page range ``"..range.."''")
end
r={}
r[1] = split(ends[1], ",")
r[2] = split(ends[2], ",")
if #r[2]==1 then
p1=r[1][1]; l1=r[1][2]; l2=r[2][1]
table.insert(parsed_ranges, "page "..p1..", lines "..l1.." to "..l2)
else
p1=r[1][1]; l1=r[1][2]; p2=r[2][1]; l2=r[2][2]
table.insert(parsed_ranges, "from page "..p1.." line "..l1.." to page "..p2.." line "..l2)
end
end
return table.concat(parsed_ranges, " and ")
end
答案2
这是一个 LaTeX3 的实现。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\parsepages}{m}
{
\pierre_parse_pages:n { #1 }
}
\seq_new:N \l_pierre_blocks_seq
\seq_new:N \l_pierre_output_seq
\seq_new:N \l_pierre_temp_seq
\cs_new_protected:Npn \pierre_parse_pages:n #1
{
% decompose the argument into blocks delimited by +
\seq_set_split:Nnn \l_pierre_blocks_seq { + } { #1 }
\seq_clear:N \l_pierre_output_seq
% process each block
\seq_map_inline:Nn \l_pierre_blocks_seq
{
\pierre_parse_block:n { ##1 }
}
% print the result
\seq_use:Nnnn \l_pierre_output_seq {~and~} {~and~} {~and~}
}
\cs_new_protected:Npn \pierre_parse_block:n #1
{
\tl_if_in:nnTF { #1 } { - }
{% we have a "complex block"; either a,b-c,d or a,b-c
\pierre_parse_complex_block:n { #1 }
}
{% a "simple block" a,b
\pierre_parse_simple_block:w #1 \q_stop
}
}
\cs_new_protected:Npn \pierre_parse_simple_block:w #1 , #2 \q_stop
{
\seq_put_right:Nn \l_pierre_output_seq
{
page~#1~line~#2
}
}
\cs_new_protected:Npn \pierre_parse_complex_block:n #1
{
\seq_set_split:Nnn \l_pierre_temp_seq { , } { #1 }
\int_compare:nTF { \seq_count:N \l_pierre_temp_seq = 2 }
{% only one comma: a,b-c
\pierre_single_page:w #1 \q_stop
}
{% two commas: a,b-c,d
\pierre_multi_page:w #1 \q_stop
}
}
\cs_new_protected:Npn \pierre_single_page:w #1 , #2 - #3 \q_stop
{
\seq_put_right:Nn \l_pierre_output_seq
{
page~#1~lines~#2~to~#3
}
}
\cs_new_protected:Npn \pierre_multi_page:w #1 , #2 - #3 , #4 \q_stop
{
\seq_put_right:Nn \l_pierre_output_seq
{
from~page~#1~line~#2~to~page~#3~line~#4
}
}
\ExplSyntaxOff
\begin{document}
% just for formatting the example
\setlength{\parindent}{0pt}
\setlength{\parskip}{1ex}
\parsepages{264,15}
\parsepages{264,15-26}
\parsepages{264,15-266,26}
\parsepages{264,15-266,26+277,13-14+312,14-316,23}
\end{document}
答案3
pdflatex
带有软件包的解决方案listofitems
。
修改后的答案
我担心我原来的回答(如下)误解了何时应插入“from”的上下文。在此修订中,当序列的范围跨越页面边界时,将应用“from”。
\documentclass{article}
\usepackage{listofitems}
\newcommand\parsepages[1]{\bgroup%
\def\+{ and }%
\def\-{ to }%
\def\,{, line\checkplural{} }%
\setsepchar{+/-/,}%
\readlist*\mypages{#1}%
\foreachitem\i\in\mypages[]{%
\foreachitem\j\in\mypages[\icnt]{%
\foreachitem\k\in\mypages[\icnt,\jcnt]{%
\ifnum\kcnt<\listlen\mypages[\icnt,\jcnt]\relax\checkpages page \fi%
\k \ifnum\kcnt<\listlen\mypages[\icnt,\jcnt]\relax
\csname\mypagessep[\icnt,\jcnt,\kcnt]\endcsname \fi%
}\ifnum\jcnt<\listlen\mypages[\icnt]\relax%
\csname\mypagessep[\icnt,\jcnt]\endcsname\fi%
}\ifnum\icnt<\listlen\mypages[]\relax\csname\mypagessep[\icnt]\endcsname\fi%
}%
\egroup}
\newcommand\checkplural{\ifnum\numexpr\jcnt+1=\listlen\mypages[\icnt]\relax%
\ifnum\kcnt=\listlen\mypages[\icnt,\numexpr\jcnt+1]\relax s\fi\fi}
\newcommand\checkpages{\ifnum\jcnt=\listlen\mypages[\icnt]\relax\else%
\ifnum\listlen\mypages[\icnt,\the\numexpr\jcnt+1\relax]>1\relax from \fi\fi}
\begin{document}
\begin{enumerate}
\item \verb|\parsepages{264,15-26}|\par
\parsepages{264,15-26}
\item \verb|\parsepages{264,15-266,26}|\par
\parsepages{264,15-266,26}
\item\verb|\parsepages{264,15-266,26+277,13-14+312,14-316,23}|\par
\parsepages{264,15-266,26+277,13-14+312,14-316,23}
\end{enumerate}
\end{document}
原始答案
在此答案中,当答案涉及多个序列时,会插入“from”。然后,“from”应用于第一个和最后一个序列。
\documentclass{article}
\usepackage{listofitems}
\newcommand\parsepages[1]{%
\def\+{ and \checkfrom}%
\def\-{ to }%
\def\,{, line\checkess{} }
\setsepchar{+/-/,}
\readlist*\mypages{#1}%
\foreachitem\i\in\mypages[]{%
\ifnum\listlen\mypages[]>1\relax%
\ifnum\icnt=1\relax from \fi\fi%
\foreachitem\j\in\mypages[\icnt]{%
\foreachitem\k\in\mypages[\icnt,\jcnt]{%
\ifnum\kcnt<\listlen\mypages[\icnt,\jcnt]\relax%
page %
\fi%
\k \ifnum\kcnt<\listlen\mypages[\icnt,\jcnt]\relax
\csname\mypagessep[\icnt,\jcnt,\kcnt]\endcsname \fi%
}\ifnum\jcnt<\listlen\mypages[\icnt]\relax%
\csname\mypagessep[\icnt,\jcnt]\endcsname\fi%
}\ifnum\icnt<\listlen\mypages[]\relax%
\csname\mypagessep[\icnt]\endcsname\fi%
}%
}
\newcommand\checkfrom{\ifnum\numexpr\icnt+1=\listlen\mypages[]\relax from \fi}
\newcommand\checkess{\ifnum\numexpr\jcnt+1=\listlen\mypages[\icnt]\relax%
\ifnum\kcnt=\listlen\mypages[\icnt,\numexpr\jcnt+1]\relax s\fi\fi}
\begin{document}
\begin{enumerate}
\item \verb|\parsepages{264,15-26}|\par
\parsepages{264,15-26}
\item \verb|\parsepages{264,15-266,26}|\par
\parsepages{264,15-266,26}
\item\verb|\parsepages{264,15-266,26+277,13-14+312,14-316,23}|\par
\parsepages{264,15-266,26+277,13-14+312,14-316,23}
\end{enumerate}
\end{document}