news 2026/9/19 18:23:47

Pandoc 缩写识别与 `--abbreviations` 选项:从 `test/command/256.md` 看非断行空格机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Pandoc 缩写识别与 `--abbreviations` 选项:从 `test/command/256.md` 看非断行空格机制
  • 文档
  • 开发工具
  • CLI

【免费下载链接】pandoc

Universal markup converter

项目地址:https://gitcode.com/gh_mirrors/pa/pandoc
点击查看免费下载

导读

本文以 pandoc 仓库中的命令行测试用例 test/command/256.md 为核心线索,深入讲解 pandoc 的缩写(abbreviation)识别机制:读者将掌握--abbreviations选项的完整用法、内置缩写表与自定义缩写文件的区别、缩写如何影响 Markdown 阅读器在句间空格上的处理(非断行空格\160),以及如何在 LaTeX/PDF 输出中避免因句号被误判为句尾而产生的多余间距问题。文中所有结论均可通过仓库源码与测试用例逐一验证。

一、测试用例全景:一个段落,两种输出

test/command/256.md用同一个输入句子,对比了启用自定义缩写文件使用默认内置缩写表两种场景下 Markdown 阅读器的原生 AST 输出:

输入文本:

Foo. bar baz h.k. and e.g. and Mr. Brown.

场景一:显式指定自定义缩写文件

% pandoc --abbreviations=command/abbrevs -t native

其输出为:

[ Para [ Str "Foo.\160bar" , Space , Str "baz" , Space , Str "h.k.\160and" , Space , Str "e.g." , Space , Str "and" , Space , Str "Mr." , Space , Str "Brown." ] ]

场景二:不指定--abbreviations,使用默认行为

% pandoc -t native

其输出为:

[ Para [ Str "Foo." , Space , Str "bar" , Space , Str "baz" , Space , Str "h.k." , Space , Str "and" , Space , Str "e.g.\160and" , Space , Str "Mr.\160Brown." ] ]

对比两段 AST 可以得出一个清晰的结论:

  • 在场景一中,Foo.h.k.被识别为缩写,其后紧跟的空格被替换为非断行空格(AST 中表现为Str "Foo.\160bar"这类字符串内含\160字符);而e.g.Mr.未被识别,保留普通Space
  • 在场景二中正好相反:e.g.Mr.被识别为缩写,Foo.h.k.则被当作普通单词加句号处理。

也就是说,测试文件模拟的自定义command/abbrevs文件包含Foo.h.k.而不包含e.g.Mr.,而 pandoc 内置的默认缩写表中恰好包含e.g.Mr.却不包含Foo.h.k.。两次测试从正反两个方向验证了缩写识别完全由缩写表驱动。

二、缩写识别的实现原理:Markdown 阅读器中的源码依据

2.1 缩写表从哪里来:readAbbreviations

--abbreviations选项的作用链在源码中清晰可见。命令行的--abbreviations FILE选项在 src/Text/Pandoc/App/CommandLineOptions.hs#L719-L725 中解析:

, option "" ["abbreviations"] (ReqArg (\arg opt -> return opt { optAbbreviations = Just $ normalizePath arg }) "FILE") Files (T.pack "File with abbreviations")

而缩写表的实际加载由 src/Text/Pandoc/App.hs#L376-L384 中的readAbbreviations完成:

-- | Retrieves the set of abbreviations to be used by pandoc. These currently -- only affect the Markdown reader. readAbbreviations :: PandocMonad m => Maybe FilePath -> m (Set.Set Text) readAbbreviations mbfilepath = (case mbfilepath of Nothing -> readDataFile "abbreviations" Just f -> readFileStrict f) >>= fmap (Set.fromList . filter (not . T.null) . T.lines) . toTextM (fromMaybe mempty mbfilepath)

这段实现揭示了两个关键事实:

  1. 未指定--abbreviations,pandoc 会读取随程序分发的数据文件data/abbreviations(仓库中的 data/abbreviations);
  2. 指定时,则严格读取用户提供的文件,两者互斥——这也是测试 256.md 中两次输出行为迥异的根本原因。

2.2 缩写如何影响空格:str解析器

缩写识别的核心逻辑位于 Markdown 阅读器的str解析器,src/Text/Pandoc/Readers/Markdown.hs#L1799-L1821:

str :: PandocMonad m => MarkdownParser m (F Inlines) str = do !result <- mconcat <$> many1 ( takeWhile1P isAlphaNum <|> "." <$ try (char '.' <* notFollowedBy (char '.')) ) updateLastStrPos (do guardEnabled Ext_smart abbrevs <- getOption readerAbbreviations if result `Set.member` abbrevs then try (do ils <- whitespace notFollowedBy (() <$ cite <|> () <$ note) -- ?? lookAhead alphaNum -- replace space after with nonbreaking space -- if softbreak, move before abbrev if possible (#4635) return $ do ils' <- ils case B.toList ils' of [Space] -> return $! (B.str result <> B.str "\160") _ -> return $! (B.str result <> ils')) <|> return (return $! B.str result) else return (return $! B.str result)) <|> return (return $! B.str result)

从中可以提炼出完整的判定流程:

  1. 解析器先累积一个由字母数字与单个句点组成的“词”(注意notFollowedBy (char '.')保证...省略号不会被拆散);
  2. 仅在启用smart扩展(Ext_smart)时才会进入缩写判定分支;
  3. 将该词与缩写表readerAbbreviations(类型为Set.Set Text,见 src/Text/Pandoc/Options.hs#L72)做成员匹配;
  4. 命中缩写后,再查看其后的空白:若后面恰好是一个普通空格(Space,则把该空格替换为\160(非断行空格,NBSP),从而得到Str "Foo.\160bar"这样的输出;若后面是换行等其他内容,则保持原样;
  5. 同时使用notFollowedBy避免在引用(cite)或脚注(note)紧随其后的场景下误替换。

这正是测试 256.md 中 AST 字符串里出现\160的来源——\160即 Unicode 字符 U+00A0(NO-BREAK SPACE)在 Haskell 转义中的写法。

三、默认缩写表:data/abbreviations的内容与用途

当用户不提供--abbreviations时,pandoc 从数据目录读取 data/abbreviations。该文件每行一个缩写,共 80 条左右,覆盖了英文写作中常见的缩写形式,例如:

aet. aetat. al. Apr. Aug. bk. Bros. c. Capt. cf. ch. chap. chs. Co. col. Corp. cp. d. Dec. Dr. e.g. ed. eds. esp. f. fasc. Feb. ff. fig. fl. fol. fols. Fr. Gen. Gov. Hon. i.e. ill. Inc. incl. Jan. Jr. Jul. Jun. Ltd. M.A. M.D. Mar. Mr. Mrs. Ms. n. n.b. nn. No. Nov. Oct. p. Ph.D. pp. Pres. Prof. pt. q.v. Rep. Rev. s.v. s.vv. saec. sec. Sen. Sep. Sept. Sgt. Sr. St. univ. viz. vol. vs.

这也解释了测试 256.md 场景二的行为:e.g.Mr.都位于上表之中,因此被识别;而Foo.(大写开头且是普通专名)与h.k.不在表中,因此保持为“词 + 句号”的普通结构。

四、为什么要在意非断行空格

缩写识别并不仅仅是一个 AST 层面的细节,它对最终排版输出有实际影响。相关测试 test/command/md-abbrevs.md 明确说明了动机:

Pandoc recognizes an abbreviation and inserts a nonbreaking space (among other things, this prevents a sentence-ending space from being inserted in LaTeX output).

也就是说,当把Mr. Brown这样的文本转换为 LaTeX/PDF 时,如果不做处理,Mr.末尾的句号容易被 LaTeX 误判为句子结束符,从而在Mr.Brown之间插入过宽的句间间距。pandoc 将句号后的空格替换为非断行空格后,这一误判即可避免,同时也能防止缩写在行尾被换行拆散。

同类的非断行空格处理在其他阅读器中也有对应实现,例如 Muse 阅读器中的nbsp(src/Text/Pandoc/Readers/Muse.hs#L923-L924)将~~解析为\160,可以对照理解 pandoc 对空白语义的统一处理方式。

五、实操指南:自定义缩写文件

基于测试 256.md 的模式,自定义缩写文件的用法可以概括如下:

  1. 准备文本文件:每行一个缩写,含末尾句点,例如:
e.g. i.e. Mr. Mrs. Dr. h.k.
  1. 传入选项
pandoc --abbreviations=my-abbrevs.txt input.md -o output.pdf
  1. 验证效果(使用与测试相同的 native 输出):
pandoc --abbreviations=my-abbrevs.txt -t native

readAbbreviations的实现细节还提示了几个注意事项:

  • 空行会被过滤filter (not . T.null)),因此文件中出现空行不会报错;
  • 文件内容会整体替换内置默认表,而不是合并——如果业务文档中大量使用默认表之外的缩写,需要把默认 data/abbreviations 中的条目一并抄入自定义文件;
  • 该表“currently only affect the Markdown reader”(源码注释原文),对 HTML、LaTeX 等其他格式的阅读器没有影响;
  • 缩写识别依赖smart扩展(Ext_smart),若通过-f markdown-smart关闭 smart 扩展,则缩写判定分支不会进入,空格不会被替换。

六、相关配置与测试验证路径

除了--abbreviations,pandoc 还提供语义相近但用途不同的--citation-abbreviations选项(解析见 src/Text/Pandoc/App/CommandLineOptions.hs#L1100-L1108),它服务于citeproc文献处理流程(见 src/Text/Pandoc/Citeproc.hs#L125-L159),用于文献目录中期刊名的缩写,与本文讨论的 Markdown 阅读器空格机制相互独立,使用时注意不要混淆。

如需验证本文描述的行为,仓库提供了多条可复现的测试路径:

  • test/command/256.md:--abbreviations自定义文件 vs 默认表的对照测试(本文主线);
  • test/command/md-abbrevs.md:缩写识别的正向用例,以及用Mr\.转义句点以关闭缩写的反例;
  • data/abbreviations:随程序分发的默认缩写表;
  • src/Text/Pandoc/App.hs#L376-L384:缩写表加载实现;
  • src/Text/Pandoc/Readers/Markdown.hs#L1799-L1821:空格替换核心逻辑。

七、小结

通过test/command/256.md这一组对照测试,可以完整理解 pandoc 缩写识别机制的三个层面:命令行选项--abbreviations决定缩写表来源)、数据文件(data/abbreviations 提供默认集合)、解析器行为(Markdown.hs 的str在 smart 扩展开启时把缩写后的空格替换为非断行空格\160)。这一机制虽然只影响 Markdown 阅读器,却直接关系到 LaTeX/PDF 等输出格式的排版质量,是处理英文文本时值得了解和掌握的一个细节。

  • 文档
  • 开发工具
  • CLI

【免费下载链接】pandoc

Universal markup converter

项目地址:https://gitcode.com/gh_mirrors/pa/pandoc
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 18:18:50

TipTap编辑器扩展实战:数学公式与流程图开发

1. 项目概述作为一名长期奋战在前端开发一线的工程师&#xff0c;我最近在项目中深度使用了TipTap编辑器&#xff0c;并对其进行了功能扩展。TipTap作为基于ProseMirror构建的现代化富文本编辑器框架&#xff0c;以其模块化设计和出色的扩展性在前端开发圈内广受好评。不同于传…

作者头像 李华
网站建设 2026/9/19 18:17:30

APQP全套表单拆解:从可行性评估到开发计划的数字化管理

简介&#xff1a;面向汽车行业质量管理场景&#xff0c;这套APQP全套表单文档适用于产品开发工程师、质量策划人员及项目管理者&#xff0c;用于系统完成新产品制造可行性评估与产品成本核算。文档清晰覆盖顾客概况、质量技术要求、竞争分析、定点认可程序、市场预测、风险分析…

作者头像 李华