(一个实验)我想向您介绍我在日常工作中解决的可能的任务之一(要自动化):在itemize
和enumerate
环境中添加缺失的标点符号。
我润色数学论文,这需要大量的时间和脑力,因此任何可以自动化的东西都是有价值的,而且,它可以最大限度地减少一些额外的人为错误(打字错误、忽略关键点等)。
我尝试将尽可能多的事情自动化,但我还没有触及这个主题。我希望自动检查\item
s 的结尾,我们期望那里有一个标点符号。有一些例外(根本没有标点符号)或者可能有一个连词and
(可能是其他术语),但当尝试编程时,它并不像看起来那么无聊。在这个问题中,我介绍了问题和一个可能的起点。TeX 世界(Lua)之外的呈现代码片段未在生产中使用,因为我仍在思考这里最好的方法是什么。
itemize
第一个想法是删除和环境中的所有标点符号enumerate
,然后根据我们的规则添加标点符号,比如说:
\item
如果以大写字母或数学表达式开头,则添加句号。- 如果没有,请添加逗号或分号。
- 例外:在最后一个项目的末尾添加句号,
\item
因为它结束了项目列表。
它可能有效,而且编程起来也不难,但这不是一个好策略。我们会几乎不假思索地改变原始论文,考虑特殊情况并遵循环境之外的文本。
似乎更好的策略是不更改论文中的任何一个字母,而是在可能存在问题时通知用户(我们)。让我来演示几个(虚构的)示例(mal-itemize.tex
)。
% *latex mal-itemize.tex
% A testing file...
\documentclass[a4paper]{article}
\pagestyle{empty}
\parindent=0pt
% For testing purposes...
\usepackage{xcolor}
\def\itsOK{{\color{green}IT'S OK!}}
\def\notOK{{\color{red}NOT OK!}}
\begin{document}
Text before.
\begin{itemize}
\item First
item.
\item Second
item $a+b+c.$
\begin{itemize}
\item 2a % .
\item 2b, and % ;
\item 2c % ,
\end{itemize}
\item Third item
still third item\end{itemize}
text in the middle.
\begin{enumerate}
\item Fourth item;
\item Fifth item $\left(\frac{a}{b}\right .$
\item Sixth item $d+e . $
\item Seventh item,
\end{enumerate}
A common situation:
\begin{itemize}
\item 8th item
\item 9th item,
\item 10th item
\item 11th item,
\item 12th item.
\end{itemize}
Text after.
\end{document}
- 第一项和第二项看起来正确。句号直接结束项目(第一项)或从数学表达式中结束项目(第二项)。
- 2a 和 2c 可能是正确的,但 2b 可能不包含逗号,或者项 2a 和 2c 不正确(更有可能),我们应该添加逗号(2a)和句号(2c)。
- 如果第三项
text in the middle
是该项的一部分,则看起来是正确的。否则,应该有一个句号(如第一项和第二项中一样),并且text
应该更改为Text
。 - 只要我们不检查其他项,第四项末尾的标点符号看起来是正确的。第五项是一个棘手的部分,因为它包含
\right.
句号是 TeX 表达式的一部分。 - 第七项也比较棘手。我们更希望有一个句号(因为下面的文本以大写字母开头),或者
A common
应该改为a common
。 - 现在,让我演示一下我经常遇到的一种常见情况。我们只是缺少标点符号。在第 10 项中添加逗号很容易,因为我们可以存储前一项(第 9 项)中的标点符号。困难的部分是第 8 项中缺少标点符号,我们需要在文本中向前查找作者使用了哪种类型的标点符号(逗号、分号或句号、跳过
and
、、or
...)。
让我演示一下我在 Lua 中的第一次尝试。我不熟悉 LPeg,所以我将预选环境中的\begin
s 和\end
s 更改为单个字符。之后,我可以%b
在命令中使用(平衡)运算符string.gsub
。
接下来我把命令加倍,这样我们就可以找到直到下一个命令\item
的内容。这意味着还有一个尚未被该正则表达式吞掉的。我还在环境结束之前添加了一个,以便能够找到最后一个的内容。这是使用正则表达式的一个小技巧。接下来我要做的是删除 TeX 注释,以便测试项目内容。TeX 注释不会影响结果。\item
\item
\item
\item
\item
该代码片段的核心是,我通过删除临时字符串中的一部分来检查标点符号和and
连词(可能还有or
,neither
...)。如果有变化(它包含该部分),则该项目是正确的。我检查标点符号本身加上该字符串,然后检查常见数学表达式的结尾。
该代码片段告知终端中的进度(可能不正确的项目的内容),并且为了回答这个问题,它保存了 TeX 文件的修改版本(mal-output.tex
)。我附上了源代码和此测试文件的预览。我们运行(可以使用任何 LaTeX 引擎):
texlua mal-item.lua
lualatex mal-itemize.tex
lualatex mal-output.tex
我们在终端中得到了可能缺少标点符号的项目列表:
2a %.
2c %,
Third item
still third item
8th item
10th item
Fifth item $\left(\frac{a}{b}\right.$
我们可以看到,这个代码片段在常见情况下(第 8 和第 10 项)可以正常工作,但很难说 2a、2c 和第三项是否真的不正确。另一方面,第四、第六和第七项可能不正确,因为它们在环境中包含不同的标点符号。
不过,我想知道是否有比我的拙见更好的方法。
片段mal-item.lua
:
-- Lua snippet checks the end of the \item commands for missing punctuations.
file=io.open("mal-itemize.tex", "r")
content=file:read("*all")
file:close()
--print(content) -- print an original content
thecore="([%.,;])" -- punctuation in a group
beginchar="\002" -- starting character, temporary character, %b{}
endchar="\003" -- ending character, temporary character, %b{}
testcases={"%.", ",", ";", "and"} -- which characters and words I would like to have at the end of \item
-- The main loop for different environments...
environ={"itemize", "enumerate"}
for _,environment in pairs(environ) do
begintext="\\begin{"..environment.."}"
endtext="\\end{"..environment.."}"
-- shrink more letters to a single one, for purpose of string.gsub, %b operator
content=string.gsub(content, begintext, beginchar)
content=string.gsub(content, endtext, endchar)
content=string.gsub(content, "(%b"..beginchar..endchar..")",
function(malstring)
malstring=string.gsub(malstring, "%s+"..thecore, "%1") -- delete spaces before punctuation marks
malstring=string.gsub(malstring, thecore.." +", "%1 ") -- shrink more spaces after punctuation to one
malstring=string.gsub(malstring, "(\\item)(%A)", "%1%1%2") -- doubling \item
--malstring=string.gsub(malstring, "\\item\\item", "\\item", 1) -- no need to store the first occurence
malstring=string.gsub(malstring, endchar, "\\item"..endchar) -- but add closing \item
malstring=string.gsub(malstring, "\\item([^\\].-)\\item", function(s) -- find the content of a single \item
--print(s)
mals=s
mals=string.gsub(mals, "[^\\]%%.-\n", "") -- delete TeX comments for purpose of this testing
mals=string.gsub(mals, "\\right%.", "") -- delete all \right., we don't need them
mals=string.gsub(mals, "%s+", " ") -- delete almost all white spaces, we don't need them here
mals=string.gsub(mals, beginchar, "") -- delete end character of inner itemize/enumerate
mals=string.gsub(mals, "%s+$", "") -- delete extra spaces at the end of a string
maltest=mals
--print(maltest)
--print()
for _, test in pairs(testcases) do
maltest=string.gsub(maltest, test.."$", "") -- character as it is, there cannot be an extra space
maltest=string.gsub(maltest, test.."%s?%$$", "") -- character just before $
maltest=string.gsub(maltest, test.."%s?%$%$$", "") -- character just before $$
maltest=string.gsub(maltest, test.."%s?\\%]$", "") -- character just before \]
maltest=string.gsub(maltest, test.."%s?\\end{equation%*?}$", "") -- character just before \end{equation}
maltest=string.gsub(maltest, test.."%s?\\end{e?q?n?array%*?}$", "") -- character just before \end{eqnarray}
maltest=string.gsub(maltest, test.."%s?\\end{gather%*?}$", "") -- character just before \end{gather}
maltest=string.gsub(maltest, test.."%s?\\end{array%*?}$", "") -- character before \end{array}
end -- for, testcases
if mals~=maltest then OK="its" else print(s); OK="not" end
OK=" \\"..OK.."OK{}"
--print()
return "\\item"..s..OK.."\\item" -- return string untouched
end)
malstring=string.gsub(malstring, "\\item\\item", "\\item") -- return double \item to normal
malstring=string.gsub(malstring, "\\item"..endchar, endchar)
return malstring -- don't return anything
end)
-- return environments to normal, due to %b operator
content=string.gsub(content, beginchar, begintext)
content=string.gsub(content, endchar, endtext)
end -- for, environment
-- Minor modification to get file compilable... (inner itemize/enumerate).
-- for purpose of easy spotting (TeX.SX)
for _,environment in pairs(environ) do
content=string.gsub(content, "(%s+\\begin{"..environment.."}%s+)(\\[in][to][st]OK{})", "%2%1")
end -- for, the main loop, environment
-- Print the result of our efforts termin or output file...
--print(content)
file=io.open("mal-output.tex", "w")
file:write(content)
file:close()
文件mal-output.tex
:
% *latex mal-itemize.tex
% A testing file...
\documentclass[a4paper]{article}
\pagestyle{empty}
\parindent=0pt
% For testing purposes...
\usepackage{xcolor}
\def\itsOK{{\color{green}IT'S OK!}}
\def\notOK{{\color{red}NOT OK!}}
\begin{document}
Text before.
\begin{itemize}
\item First
item.
\itsOK{}\item Second
item $a+b+c.$\itsOK{}
\begin{itemize}
\item 2a %.
\notOK{}\item 2b, and %;
\itsOK{}\item 2c %,
\notOK{}\end{itemize}
\item Third item
still third item \notOK{}\end{itemize}
text in the middle.
\begin{enumerate}
\item Fourth item;
\itsOK{}\item Fifth item $\left(\frac{a}{b}\right.$
\notOK{}\item Sixth item $d+e. $
\itsOK{}\item Seventh item,
\itsOK{}\end{enumerate}
A common situation:
\begin{itemize}
\item 8th item
\notOK{}\item 9th item,
\itsOK{}\item 10th item
\notOK{}\item 11th item,
\itsOK{}\item 12th item.
\itsOK{}\end{itemize}
Text after.
\end{document}
答案1
以下是我对您所问问题的微不足道的尝试,以环境的形式Qitemize
。它有一些缺点,我在这里列举一下:
嵌套环境必须在其自己的组中设置(Ack!)
该项目在注释之前设置。因此,如果项目包含嵌套环境,则正确属于嵌套环境之前的标点符号的注释直到嵌套环境之后才会出现。
我变得太疲倦了,无法调查前导大写字母或数学项目的测试,而 OP 指出这可能会影响所需结尾标点符号的选择。
话虽如此,我还是会检查一些内容,并尝试将它们包含在 MWE 中。您可以在 中定义所需的倒数第二个项目结尾标点符号\itemender
(此处设置为;
)。
现在来看看 MWE。
\documentclass{article}
\usepackage{listofitems,environ,xcolor}
\def\itemender{;}
\NewEnviron{Qitemize}{%
\setsepchar{\item/.||,||;||!||?}%
\readlist\myarg{\BODY}%
\begin{itemize}
\foreachitem\z\in\myarg[]{%
\if\relax\z\relax\else
\item\z
\foreachitem\zz\in\myarg[\zcnt]{%
\ifnum\zzcnt=\listlen\myarg[\zcnt]\relax
\ifnum\zzcnt=1\relax
\textcolor{red}{No punctuation in this item}
\else
% end-of-line punctuation
\ifnum\zcnt=\listlen\myarg[]\relax
% end of last item
\if.\myargsep[\zcnt,\zzcnt-1]\else
\textcolor{red}{\ Should end with .}\fi
\else
% end of non-last items
\ifx\space\zz
% Truly end of the item, only a space follows last punctuation
\if\itemender\myargsep[\zcnt,\zzcnt-1]\else
\textcolor{red}{Should end with \itemender}\fi
\else
% Not really the item end...more than space follows final punctuation
% Test for nested Qitemize
\expandafter\testnest\zz\endtestnest
\fi
\fi
\fi
\else
% mid-line punctuation
\fi
}
\fi
}%
\end{itemize}
}
\def\Qitemizename{Qitemize}
\newcommand\testnest[1]{\testnestaux#1\relax\relax}
\def\testnestaux#1#2#3\endtestnest{%
\ifx\begin#1
\def\tmp{#2}%
\ifx\tmp\Qitemizename
% NESTED Qitemize. Test prior punctuation
\textcolor{red}{Qitemize environment follows last punctuation}
\if\itemender\myargsep[\zcnt,\zzcnt-1]\else
\textcolor{red}{\\Should end with \itemender{} prior to this environment}\fi
\else
\textcolor{red}{An environment follows last punctuation}
\if\itemender\myargsep[\zcnt,\zzcnt-1]\else
\textcolor{red}{\\Should end with \itemender{} prior to this environment}\fi
\fi%
\else
\textcolor{red}{No ending punctuation}
\fi
}
\begin{document}
\begin{Qitemize}
\item 8th item, ending not here; but here
\item 9th item;
\item 10th item
\item 11th item,
\item 12th item
{\begin{Qitemize}
\item a,
\item b
\item c.
\end{Qitemize}}
\item 13th item,
{\begin{Qitemize}
\item a,
\item b;
\end{Qitemize}}
\item 14th item;
{\begin{Qitemize}
\item a;
\item b.
\end{Qitemize}}
\item 15th item,
{\begin{enumerate}
\item a,
\item b.
\end{enumerate}}
\item 16th item!
\end{Qitemize}
\end{document}