尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

latexdiff实战:LaTeX论文修改稿差异标记生成与避坑指南

latexdiff实战:LaTeX论文修改稿差异标记生成与避坑指南 前两周我把一篇改了三个月的论文终稿发给合作者ta收到后第一句话不是“结果不错”而是“你这次具体改了哪些地方”。那一刻我特别理解ta因为整篇重写过的章节混在一堆微调里光靠肉眼根本看不出来。后来我固定用TeX Live自带的latexdiff把所有前后版本diff一遍生成一份红蓝修订版PDF再发出去合作者扫一眼就能知道“我做了哪些修改”。这篇东西就把我折腾latexdiff的经验完整写出来从最基础的命令到多文件项目、中文环境、公式表格那些坑希望能让你少走点弯路。1. 为什么需要latexdiff改稿子最痛的那一刻1.1 手动标红标蓝真的会瞎我自己写论文的习惯是先写一版然后一版一版迭代。改到第三轮的时候说实话我自己都记不清上一版里哪句话是新增的、哪段是挪过位置的、哪个结论是从附录提到正文里的。最笨但也最常见的做法是手动把旧版复制一份然后在正文里用红色标新增、用删除线标删除。这活儿看着简单实际上极其费眼睛尤其当正文里混着大量LaTeX命令、公式、引用标签时你很容易漏掉一两处。更别提交给导师之后导师自己又改了一版你还得把导师改过的地方再手动整理一遍——我在这个环节崩溃过不止一次。后来我意识到这个场景本质上就是“文本差异比较”。既然代码里有git diffLaTeX文档为什么不能有对应的工具latexdiff就是干这个的。它能比较两个LaTeX源文件生成一个新.tex文件这个新文件里用不同的颜色和字体把新增、删除、替换的内容标记出来再由你正常编译成PDF。你不需要一个字节一个字节去对着看也不用担心遗漏。1.2 latexdiff到底是怎么干活的我第一次用这个工具前以为它会直接输出一个带标记的PDF事实证明我想简单了。latexdiff本质上是一个Perl脚本它拿到old.tex和new.tex之后会先解析两者的LaTeX结构再把文本切分成可比较的词元逐段做差异比对最后输出一个mixed.tex文件也就是diff.tex。这个diff.tex并没有被编译它只是把LaTeX的变更用命令包了起来比如新增内容套在\DIFadd{}里删除内容套在\DIFdel{}里。你拿到这个文件之后还要再用LaTeX编译器正常编译一次才能得到带标记的PDF。这一步很多人第一次跑会懵明明是latexdiff为什么终端里只刷刷刷打了几行字就结束了PDF呢原因就在这——它只是“生成差异源文件”的助手真正的编译还得交给xelatex或pdflatex。理解了这一点后面很多看起来“失败了”的状况就都能解释通了。提示latexdiff不会帮你编译PDF它只会生成diff.tex。编译diff.tex是你要自己干的活。2. 环境准备与第一次跑通2.1 确认你的环境里有没有latexdifflatexdiff是TeX Live的一部分如果你装的是完整版TeX Live正常情况下直接用就行。我当时的系统是macOS装的是MacTeX打开终端敲下面这行一看工具就在那latexdiff --version输出里能看到版本号就说明能用。Windows用户如果装的是完整版TeX Live在命令行里同样能调用MiKTeX用户一般也能用只是在首次调用时可能会弹窗询问是否安装相应组件选安装就行。但是有一种情况要注意如果你当初安装TeX Live时是精简安装或者是用某个发行版里自己裁剪过的套装latexdiff可能不在。我遇到过一位同事机器里能编LaTeX但一敲latexdiff就提示command not found后来他用下面的命令把包补上就行了tlmgr install latexdiff装完之后再跑latexdiff --version确认。补装时如果网络慢也可以直接用各高校搭建的开源软件镜像站下载完整版TeX Live镜像站一般会把整个TeX Live的安装包同步得很完整装完也就一劳永逸了。2.2 单文件对比最基础的用法假设你有一个论文的主文件叫main.tex投稿之前你另存了一份main_old.tex作为旧版然后在main.tex上继续改。这时候要生成修改标记最简洁的写法是latexdiff main_old.tex main.tex diff.tex这里有个顺序问题我一开始就踩过坑第一个参数是旧文件第二个参数是新文件。如果你把顺序写反了生成的diff.tex会把原来的删除内容当成新增内容标出来整个PDF看起来就像你在把论文改回旧版本一样不但返工还会让合作者看得一头雾水。生成出diff.tex之后再按你平时的编译方式编译。如果你平时用的是pdflatexpdflatex diff.tex如果文档里涉及中文或者你平时用的是XeLaTeX就改成xelatex diff.tex第一次打开编译好的PDF时你会看到默认样式删除的内容被标成红色带删除线新增的内容被标成蓝色公式里的细微变化也都能被摘出来。这种直观程度比你拿着两个PDF并排对比高到不知道哪里去了。2.3 编译diff.tex之前先看几行我后来养成了一个习惯编译diff.tex之前一定会先打开这个文件扫一眼导言区。因为latexdiff生成的diff.tex基本沿用旧文件的导言区再根据两边文件的差异做一些修补但这个修补并不总是完美。比如旧文件导言区里没有加载xcolor宏包而diff.tex里用了大量\color命令来控制文字颜色直接编译就会报\color undefined。又比如默认样式里用到了\sout命令删除线这需要ulem宏包支持如果源文件没加载过diff.tex编译时同样会报错。解决办法很简单在运行latexdiff之前先确认两个源文件的导言区里都加上了这几个宏包\usepackage{xcolor} \usepackage[normalem]{ulem}如果你不想动源文件也可以在生成diff.tex之后手动编辑它的导言区补上这两行。我的习惯是前者因为源文件的导言区本来就该规范加上这两个包对正文没有任何副作用。3. 从单文件到真实项目参数与多文件3.1 标记样式怎么选latexdiff默认的标记方式是CFONT也就是彩色文字加删除线。如果你对标记效果有要求可以用--type参数切换成别的样式可选的样式大概有这几种样式标记效果适合场景CFONT删除内容红色删除线新增内容蓝色默认最通用FONTSTRIKE删除内容加删除线新增内容仅换字体打印黑白版也看得清UNDERLINE新增内容下划线删除内容仍删除线期刊要求明显下划线时PDFCOLOR用背景色块区分增删公式多、需要区块感时WHOLE整行/整段标色不细分每句话大段重写时更干净我在投稿给不同期刊时经常切换样式。有的期刊编辑要求修改稿必须用下划线标出新内容有的要求删除内容不能显示太多怕占版面。这时候就可以用--typeUNDERLINE或--typeFONTSTRIKE来适应。不过要提醒一句样式切换后编译产物对宏包的依赖略有不同。比如UNDERLINE样式更依赖ulemPDFCOLOR样式会用到颜色的背景宏包。如果编译报错优先去diff.tex导言区补对应的宏包。3.2 自定义宏和命令安全列表写LaTeX论文的人基本都会自定义一些命令比如把期刊名缩写成一个\journalname或者把某个经常出现的专有名词封成\method{}。latexdiff对于LaTeX内置命令的处理规则比较成熟但对你自己定义的宏就没那么聪明了。举个例子我在一篇论文里定义了\newcommand{\algo}[1]{\textsc{#1}}正文里写\algo{FastFilter}。如果新版里改成了\algo{FastFilterV2}latexdiff可能不知道怎么处理\algo这种未知命令最坏的情况下会把整个命令标成删除又新增甚至影响编译。这时候要用--append-textcmd参数告诉latexdiff哪些命令应当被当作文本命令来比较latexdiff --append-textcmd\algo main_old.tex main.tex diff.tex如果命令不止一个用逗号分隔。反过来如果你希望某些命令的变化不被标记比如期刊要求某些内容不能出现在修订标记里可以用--exclude-textcmdlatexdiff --exclude-textcmd\algo,\url main_old.tex main.tex diff.tex这个“命令安全列表”的思路实际应用时很灵活。我投稿前一般会先在旧版和新版里搜一下自定义命令的数量然后把它们统一加到--append-textcmd后面这样diff出来的结果才不容易出现奇怪的整段标记。3.3 多文件工程flatten与latexdiff-vc大部分论文不会把所有内容塞在一个.tex文件里通常是main.tex加一堆\input{chapter1}、\include{chapter2}。直接用单文件命令对比主文件diff出来的内容只会看到\input这一行有没有变化根本看不到子文件里改了啥。这时候要用--flatten参数。它的作用是先把所有\input、\include的子文件展开合并成一个虚拟的完整文件再去做差异对比latexdiff --flatten main_old.tex main.tex diff.tex生成diff.tex时你可能会发现文件特别长因为它已经把各个章节都平铺进去了。后续编译依旧正常但diff.tex会比较大编译速度会慢一些。如果你的项目用git管理还有另一个更省心的路径latexdiff-vc。比如latexdiff-vc --git -r HEAD~1 main.tex这条命令会自动比较当前工作区与上一次提交之间的差异并生成带修订标记的PDF。latexdiff-vc还支持svn、hg等版本控制工具核心参数都差不多。但如果你当前工作区有未提交的修改某些版本控制场景下它可能不会按你预想的方式运行所以我个人还是更习惯一个通用方案先用git导出旧版再跑latexdiff --flattengit show HEAD~1:main.tex main_old.tex latexdiff --flatten main_old.tex main.tex diff.tex这样不依赖latexdiff-vc的具体行为只要git能用就行。4. 公式、表格、图片最容易翻车的三个地方4.1 公式的颗粒度问题我最初以为latexdiff处理公式是直接把整个公式标记成新增或删除实际用下来才发现它会深入到公式内部。比如\alpha改成\beta默认情况下diff会把\alpha标成删除、把\beta标成新增整页公式看起来密密麻麻全是红蓝小碎片。这种标记对审稿人来说反而不友好他们只想知道这个公式是不是大改并不想看清楚每一个符号的变化。所以我通常在涉及大量公式的文档里加一个参数latexdiff --math-markup0 main_old.tex main.tex diff.tex--math-markup后面跟的数值是标记颗粒度。数值越大标记越精细设为0时是把整个数学环境当作一个整体来处理要么整段标新增要么整段标删除视觉上干净很多。如果公式只是小改你可能还是希望看到符号级差异那可以把值调成1或2。我个人的建议是初稿阶段用默认值看细节定稿前生成给合作者看的那种版本时用0否则红蓝噪声太大反而掩盖了真正的结构变化。4.2 表格改多了会崩表格是我踩坑最多的地方。latexdiff在文本段落里表现很好但一进到tabular环境就容易出问题。特别是当你改了列数、增删了符号、或者调整了列对齐方式时diff.tex里可能出现对齐符数量不一致的情况编译直接报Extra alignment tab has been changed to \cr。后来我的处理策略分几种情况。如果只是改单元格里的某个数值latexdiff一般能正常标记很实用如果整张表格从3列改成4列那我不再强求latexdiff去做单元格级标记因为结构变化本身就说明表格做了大改评审人一眼就能看明白。遇到需要手动处理的情况我会先跑一遍latexdiff然后打开diff.tex定位到表格区域把列定义部分统一成新版的再把那些已经错乱的符号修好。虽然麻烦但比完全手动标整个表格快得多。4.3 图片路径变更的干扰图片这块最常见的情况是旧版用的是\includegraphics{fig_v1.pdf}新版换成了\includegraphics{fig_v2.pdf}。latexdiff会把路径字符串也当作文本差异标记出来于是一整行红蓝标记就出来了。问题在于如果图片只是生成参数微调后的重绘你和合作者其实更关心图片本身内容而不是路径名。这种标记有时候还会干扰编译因为diff.tex里保留的路径可能指向不存在的文件。我现在遇到图片变更基本是两种做法要么在运行latexdiff前把新文件拷贝成旧文件名让路径不发生变化要么生成diff.tex后手动把图片相关的那几行红蓝标记清理干净。如果你有一堆图片路径都变了还可以统一在diff.tex里搜索includegraphics一键替换成新版路径。5. 中文文档与编码XeLaTeX下的避坑记录5.1 UTF-8和Perl中文用户最关心的肯定是编码问题。我现在用的TeX Live版本对UTF-8支持已经很好了直接对包含中文的.tex文件跑latexdiff基本不会出现乱码。但如果你用的版本比较旧或者是在Windows上手动改过文件编码就可能遇到Perl脚本读取文件时把UTF-8中文解析成乱码的情况。这时候可以强制指定Perl的IO层为UTF-8命令是这样的perl -CSD $(which latexdiff) main_old.tex main.tex diff.tex-CSD参数的意思是让Perl把标准输入输出和文件句柄都按UTF-8处理实测下来对老版本环境有很大改善。平时在新版本上直接调latexdiff就行不需要套这层perl。另外要强调一个基础但很重要的习惯所有源文件统一存成UTF-8编码。有些编辑器默认会存成GBK或GB2312在Linux和macOS上编译时容易出问题latexdiff处理时也会跟着遭殃。把所有文件统一成UTF-8能省掉后面一大堆莫名其妙的乱码问题。5.2 导言区宏包缺失的连锁反应中文文档通常会加载ctex宏包或使用ctexart文档类。latexdiff本身不会破坏这些宏包但它生成的diff.tex会用到一些标记命令这时候如果源文件的导言区没有兼容好很容易出现连锁报错。比如中文模板里一般会自动加载xcolor但有些精简模板没有。我遇到过一次比较典型的报错diff.tex编译到一半提示\color undefined当时我第一反应是宏包冲突排查了半天才发现就是旧版导言区缺了xcolor而新版导言区加了xcolorlatexdiff又把这个差异忽略了导致diff.tex继承了旧版没有xcolor的导言区。解决方式前面提过在两个源文件的导言区都加上xcolor和ulem。中文文档的话推荐用[normalem]选项加载ulem因为它默认会重新定义\emph的样式不关掉这个行为可能会改变你原本的强调效果。\usepackage[dvipsnames]{xcolor} \usepackage[normalem]{ulem}补上之后再用XeLaTeX编译diff.tex基本就顺畅了。6. 投稿前我常用的完整流程与问题速查6.1 一套稳的workflow现在我每次准备给合作者发修改稿流程基本固定下来了分享给你参考第一步先确认源文件都是UTF-8编码导言区都有xcolor和ulem。这一步检查完后面能少碰很多编译报错。第二步用git或其他方式保留旧版文件。没有git的话就直接把当前要改的文件复制一份出来命名为xxx_old.tex。第三步在旧版基础上改新版。这里注意新版文件里如果用了原来没有的自定义命令记得把自定义命令的\newcommand也留在导言区否则diff.tex会因为找不到命令定义而编译失败。第四步运行latexdiff --flatten main_old.tex main.tex diff.tex如果正文里公式多我会再加--math-markup0。如果有自定义命令就追加--append-textcmd...。第五步先不急着编译打开diff.tex看一下导言区和开头几段确认没有重复宏包、没有缺包、没有明显错乱的表格。然后再用XeLaTeX或pdflatex编译。第六步生成PDF后从头到尾翻一遍重点看那些标记比较密集的地方确认标记逻辑符合直觉。如果发现某些改动没被标记出来或者标记了不该标记的内容回到diff.tex里手动微调或者重新调整参数。我一般最后会把diff.pdf和new.pdf一起发给合作者diff.pdf负责回答“你改了什么”new.pdf负责给一个干净的最终版对方看起来会非常省心。6.2 高频问题速查表下面这张表是我自己遇到过的典型问题按照“问题-原因-处理”的方式整理出来希望能对你有帮助问题常见原因处理方式latexdiff: command not found精简安装或未装latexdiff包用tlmgr install latexdiff补装或换完整版镜像重新安装生成的diff.tex编译报\DIFadd undefined导言区缺少标记相关宏包在源文件导言区加xcolor和ulem出现乱码中文变成一堆符号文件编码不是UTF-8统一保存为UTF-8再运行必要时用perl -CSD调用表格编译报Extra alignment tabtabular列结构被diff错乱手动修复diff.tex中表格部分列定义统一用新版公式处红蓝标记过多过碎默认math-markup颗粒度过细用--math-markup0整段标记生成的PDF里图片路径不存在图片路径变化且指向旧文件手动把diff.tex中的图片路径替换为新版路径diff.tex里重复加载了某个宏包--flatten展开时导言区合并异常手动删除重复的\usepackage行表格里的问题基本覆盖了90%的日常使用场景。如果你遇到的是其他报错建议先打开diff.tex看看出错位置的上下文很多时候手动改一下比纠结参数更高效。6.3 一点更进阶的玩法最后分享一个我后来才学会的技巧latexdiff生成的标记命令本身是可以用宏重新定义的。也就是说你完全可以自定义一套属于自己期刊模板的标记样式。比如默认的\DIFadd会把内容变成蓝色有的期刊要求新增内容用蓝色加粗旧内容用灰色删除线。你可以在diff.tex的导言区重新定义\renewcommand{\DIFadd}[1]{{\color{blue}\bfseries #1}} \renewcommand{\DIFdel}[1]{{\color{gray}\sout{#1}}}修改之后再编译整个PDF的标记风格就按你的要求来了。这个办法非常适合应对不同期刊的投稿格式要求比反复调--type参数更精细。我在给一个要求“新增内容必须加粗标蓝”的期刊改稿时就是靠这个搞定的。
返回列表