我正在处理其他人的 LaTeX 文件,有时它们看起来很乱。我正在寻找一种不更改代码即可删除所有注释的方法。问题是我想保留所有空格并排除百分号\%
。我自己做这件事感觉有点不安全,而且我是 Emacs 新手。也许已经写好了一些东西?但是如果没有,也许您可以通过指出我应该包含在 elisp 代码中的危险情况来帮助我。
PS 另外,如果能提供任何关于如何让其他人的代码更易读的建议,我将非常感激:)。
答案1
如果你不需要担心逐字或动词的使用,那么
(query-replace-regexp "\\(^\\| *[^\\\\]\\)%.*" "" nil nil)
可能是安全的(并且它确实进行查询替换,因此无论如何您都可以说“是”或“否”)。
请注意,如果注释位于行首,则此操作将删除整行(因为留下一个空白行会形成一个段落)。但是,如果注释不是整行,则不会删除行尾,这意味着它可能会引入空格,因此它在文档上可能是安全的,但在宏代码中则不安全。
那是
blah blah
%not this line
blah blah
变成
blah blah
blah blah
但
abc% a comment here
xyz
变成
abc
xyz
更正确的翻译(相对容易做到)应该是
abcxyz
但这会使整个文件变成一行长行,所以您必须注意在命令名称后留出空格。
LaTeX 不是常规语言,因此如果使用正则表达式解析它,那么它会弄乱一些构造。这就是为什么它们被称为正则表达式。另一种方法是做一个完整的 latex 解析器,但这很难。考虑一下xii.tex
这个网站上有一些这样的例子。试图在其中找到注释会很棘手(没有,但 emacs 不知道)。
如果您愿意,在删除百分比时删除前面的换行符会相对容易。对于动词和逐字,我可能会先将 % 更改为 [[[PERCENTWASHERE]],然后在删除 % 后再将其改回
也许与此不同(定义一个交互式命令,M-x xxx
在定义被评估后您可以运行它)它会处理5
并且verbatim
如果\verb| ... % |
您使用其他字符作为\verb
分隔符,则需要进行一些修改。
(defun xxx ()
(interactive)
(goto-char (point-min))
(while (re-search-forward "\\\\begin{verbatim}" nil 1)
(progn
(replace-regexp "%" "@@@@@PERCENT@@@@@" nil (point)
(save-excursion
(progn (re-search-forward "\\\\end{verbatim}" nil 1) (point))))))
(goto-char (point-min))
(while (re-search-forward "\\\\verb|" nil 1)
(progn
(replace-regexp "%" "@@@@@PERCENT@@@@@" nil (point)
(save-excursion
(progn (re-search-forward "|" nil 1) (point))))))
(goto-char (point-min))
(query-replace-regexp "%.*\\(\n\\|$\\)" "" nil nil)
(goto-char (point-min))
(replace-regexp "@@@@@PERCENT@@@@@" "%" nil nil)
)
答案2
有一些用例需要使用正则表达式查询
(query-replace-regexp "\\(^\\| *[^\\\\]\\)%.*" "" nil nil)
提议者大卫·卡莱尔对我不起作用。
注意:下面我写的“斜杠”是指“反斜杠”。
首要问题
正则表达式匹配非斜杠后跟百分号,因此:
123%foo
456
变成:
12
456
第二期
引入%
评论可能会被忽略,因此它不是评论,但转义也可能被转义,因此这是注释。简而言之,我们需要确保注释前面有零个或偶数个斜杠。这似乎很复杂,但请考虑一下:
\\% <- Don't forget the newline
正则表达式跳过了此注释,因为百分比被视为转义,但实际上并非如此。在 LaTeX 中,长斜杠序列并不罕见,因此我们需要管理奇偶校验。
第三期
注释会吃掉尾随换行符。此功能通常是故意使用的,例如在宏中,您需要换行符以提高可读性,但您不希望在替换宏时使用它。因此,对于全行注释,最好删除带有尾随(或前导)换行符的注释,即
123
%foo
456
应该
123
456
对于内联注释,最好移动注释文本,同时保留新行,即
123%foo
应该
123%
为了克服这些问题,我建议使用以下宏。
(defun no-coms ()
(interactive)
(while (search-forward-regexp "\\(\n?\\)\\(.*?\\)\\(\\\\*\\)\\(%.*\\)" nil t)
(when (cl-evenp (length (match-string 3))) ; bslahes should be even
;; Are we at bol?
(if (and (string-empty-p (match-string 2)) (string-empty-p (match-string 3)))
(replace-match "" nil nil nil 0) ; if so remove whole match
(replace-match "%" nil nil nil 4))))) ; else just remove comment text
评估宏,可能将其添加到您的初始化文件中,然后将光标放在您希望宏开始工作的位置后,键入
ALT+ Xno-coms
注意,我还没有彻底测试过这个宏。
以下是一些解释,供好奇的人参考。
删除 ELisp 转义后,正则表达式简化为:
(\n?)(.*?)(\\*)(%.*)
因此它匹配为子组:可选的换行符、非贪婪的任意字符、零个或多个斜杠、百分号后跟任意字符。
(while (search-forward-regexp ...
继续向前搜索,直到正则表达式找到匹配项。
(when (cl-evenp (length ...
提取第三个子组匹配(斜线)并且仅当返回的匹配数为偶数时才继续。
(if (string-empty-p (match-string 2))...
检查可选换行符和百分比之间是否有字符,即第二和第三个子组是否都为空,在这种情况下百分比位于行首,并且我们有整行注释。
(replace-match "" nil nil nil 0)
是if
肯定的情况。我们有一个整行注释,并且我们替换整个匹配(子组零),其中包括初始换行符。
(replace-match "%" nil nil nil 4)
是另一种情况。此处第四个匹配项(%
后面是注释文本)仅被替换为百分号。