语境

语境

语境

假设我有一个大型文档,它被分成许多子文件。此外,假设我有独特的章节或部分名称(例如,当该名称出现在文本的任何地方时,它都明确地指代章节或部分等)。

笔记

  1. 虽然有查找和替换功能,但我的编辑器没有全局查找和替换功能。此外,如上所述,我正在使用许多 subfiles

  2. 虽然终端始终是一个选项,也是我熟悉的一个选项,但这个问题是关于LaTeX 原生进行改变的方式。

问题

如何编写宏乳胶(包括在之前\end{document} 或者在序言中),这将超引用任何与给定链接匹配的逐字字符串,例如

\hyperrefALL[ch:my-unique-chapter-name]{My Unique Chapter Name}.

然后任何的实例My Unique Chapter Name都会被超引用ch:my-unique-chapter-name

答案1

您可以使用 LuaLaTeX 通过其数据处理回调来拦截输入的处理process_input_buffer。此回调允许您在 LuaTeX 实际开始查看行输入缓冲区的内容之前更改行输入缓冲区的内容。

我对 Lua 的使用比不存在高出一个像素,因此这里可能还有改进的空间:

在此处输入图片描述

\documentclass{report}

\usepackage{luacode,hyperref}
\usepackage{lipsum}

\begin{luacode}
  function inserthref ( s )
    if string.find ( s , '\\chapter' ) == nil then
      s = string.gsub ( s , 'My unique chapter name', 
        '\\hyperref[ch:my-unique-chapter-name]{My unique chapter name}' )
      s = string.gsub ( s , 'Introduction',
        '\\hyperref[ch:introduction]{Introduction}' )
    end
    return ( s )
  end
\end{luacode}
\AtBeginDocument{\directlua{luatexbase.add_to_callback (
   "process_input_buffer", inserthref, "inserthref" ) }}

\begin{document}

\chapter{Introduction}
\label{ch:introduction}

\chapter{My unique chapter name}
\label{ch:my-unique-chapter-name}
\lipsum[1-10]

See My unique chapter name as well as introduction, Introduction.

\end{document}

回调代码本质上会检查您是否调用了\chapter。如果没有,它会根据您的唯一章节名称执行字符串替换。找到的每个唯一章节名称都会被其超链接对应项替换。请注意,字符串替换区分大小写。

您可以向此回调替换列表中添加更多章节,每个章节或部分一个。


对于“真实”用例,您可以考虑使用 Lua 中的查找表。一种尽量减少重复的方法是

\begin{luacode}
  local mylabels =  {
      ["My unique chapter name"] = "my-unique-chapter-name" ,
      ["Introduction"]           = "introduction" 
  }

  function inserthref (s)
    for k,v in pairs(mylabels) do
      if string.match(s, "\\chapter{" .. k .. "}") then
        return string.gsub(s, k, "\\hyperref[ch:" .. v .. "]{" .. s .. "}")
      end
    end
    return s
  end
\end{luacode}

或者,您可能希望指定“完整”搜索字符串,以便可以使用一个函数进行不同类型的替换。例如

\begin{luacode}
  local mylabels =  {
      ["\\chapter{My unique chapter name}"] = 
        "\\chapter{\\hyperref[ch:my-unique-chapter-name]{My unique chapter name}}" ,
      ["\\chapter{Introduction}"]           =
        "\\chapter{\\hyperref[ch:introduction]{Introduction}}" ,
  }

  function inserthref (s)
    for k,v in pairs(mylabels) do
      if string.match(s, k) then
        return string.gsub(s, k, v)
      end
    end
    return s
  end
\end{luacode}

如果我们假设字符串只是一整行,那么这种方法可以变得更简单:

\begin{luacode}
  local mylabels =  {
      ["\\chapter{My unique chapter name}"] = 
        "\\chapter{\\hyperref[ch:my-unique-chapter-name]{My unique chapter name}}" ,
      ["\\chapter{Introduction}"]           =
        "\\chapter{\\hyperref[ch:introduction]{Introduction}}" ,
  }

  function inserthref (s)
    if mylabels[s] then
        return mylabels[s]
    end
    return s
  end
\end{luacode}

答案2

此答案并未提供导致此问题的问题的解决方案。
请将此答案视为无意义的事物/旨在展示问题复杂性的“学术”事物。

您的请求似乎包括正确检测 (La)TeX 输入文件中文本短语的出现的任务。

建议将此“检测任务”的处理留给用于在创建时创建(La)TeX 输入文件的编辑程序,而不是将此检测任务的处理留给在编译时处理(La)TeX 输入文件以生成 .pdf 输出文件的(La)TeX 编译器。

我将集中讨论这一点。


示例 1:

您可以通过将该短语与读取时被 (La)TeX 忽略的字符交错,或将该短语与引入注释或属于注释的字符交错,使当今的编辑程序难以在 (La)TeX 输入文件中找到相关短语。请注意,在 (La)TeX 中,任何字符都可以变成读取时被忽略的字符。请注意,在 (La)TeX 中,任何字符都可以变成引入注释的字符。(此类字符仍然可以是控制符号的名称。)

\documentclass{article}
\begin{document}
\catcode`\A=14
\catcode`\B=14
\catcode`\D=14
\catcode`\X=9
\catcode`\Y=9
\catcode`\Z=9

My Unique Chapter Name =
My UA 129(dec) is not a prime number yet.
nXi%
que CD Father Christmas has Santa Claus' reindeer under 
haYpteB the vegan sledgehammer. Isn't this nice?!
r NaZme
\end{document}

示例 1 提供:

在此处输入图片描述


示例 2:

您可以应用 catcode 技巧,使 (La)TeX 输入文件中出现的相关短语在 .pdf 输出文件中呈现完全不同的内容。如果您这样做,您可能不希望它被替换。

\documentclass{article}

\begin{document}
\ttfamily\selectfont
{%
  \newcommand\activedef[2]{\begingroup\lccode`\~=`#1\relax
  \lowercase{\endgroup\def~}{#2}\catcode`#1=13\relax}%
  \csname @firstofone\endcsname{\activedef{e}{\activedef{e}{%
  \activedef{e}{\activedef{e}{\activedef{e}{\activedef{e}{\string
  s}\string n}\string h}\string s}\string n}\string h}\activedef
  {M}{\string S}\activedef{y}{\string p}\activedef{ }{\activedef
  { }{\activedef{ }{\activedef{ }{\activedef{ }{\activedef{ }{ }%
  \string a}\string i} }\string a}\string i}\activedef{U}{\string
  d}\activedef{n}{\string e}\activedef{i}{\string r}\activedef{q}%
  {\string s}\activedef{u}{ }\activedef{t}{\string a}\activedef
  {p}{\string m}\activedef{r}{\string y}\activedef{N}{\string e}%
  \activedef{m}{\string e}\activedef{C}{\string v}\activedef{h}{%
  \string e}\activedef{a}{\activedef{a}{\activedef{a}{\activedef
  {a}{\string y} }\string y} }}%
%
1.My Unique Chapter Name%

2.My Unique Chapter Name%
}%

3.My Unique Chapter Name%
\end{document}

示例 2 提供:

在此处输入图片描述

LaTeX 输入文件中没有短语“蜘蛛有很多眼睛”。但是 .pdf 输出文件中有两个短语。
虽然 LaTeX 输入文件中有三个短语“我独特的章节名称”,但是 .pdf 输出文件中只有一个短语“我独特的章节名称”。
这怎么可能呢?LaTeX 输入文件中的哪一个短语应该被文本文件编辑程序的替换例程替换?LaTeX 输入文件中的哪一个短语应该保留以保留有关蜘蛛的信息?


示例 3:

您可以让相关短语出现在 .pdf 输出文件中,而无需让它出现在 (La)TeX 输入文件中。如果短语未出现在 (La)TeX 输入文件中,则无法通过编辑文本文件的程序在 (La)TeX 输入文件中替换它。

%%   The following piece of code requires LaTeX with eTeX-support.    %%

\documentclass{article}

\begin{document}

\noindent My homework is that I must write ten times a phrase
about a unique chapter name.

\bigskip

\noindent Here we go:

\bigskip

\begingroup\endlinechar-1\let\+\let\+\a\def\a\c{\j\o}\+\m\relax\+\g\else
\a\b{\c7 \f\o>93 \f\o<101\c1 \h\h\f\o>125\c-93 \h}\+\j\advance\+\f\ifnum
\a\d{\catcode\o12 \uccode\o\p}\+\n\endgroup\+\l\noexpand\+\o\fam\+\p\mag
\a\q#1{\f`#1=13\k~\h\f`#1=32\k!\h\f`#1=36\k##\h\f`#1=94\k"\h}\+\k\string
\+\e\expandafter\+\h\fi\a\i{\j\p1 }\a~#1{\f\p<126\d\b\i\f\p=94\i\h\g\f\p
=126\b\b\b\b\c127 \g\d\c7 \f\o>255\c-129 \h\h\i\h\f\p>255\edef~{\a\l~\l~
####1 ####2\k E\k T}~{\+~\q\uppercase{\edef~{##1}}\e\n\e\begingroup\e
\newlinechar\e1\e3\e\relax\e\scantokens\e{\e\endgroup~}}\h#1#1}\o"41
\begingroup\a~{\n\p13 ~\m\p32 ~\m\p94 ~\m\p36 ~~}~
]b{H`4&Jm4IuHgPHW`.WHEH&_PWHmIgWJHWJ-HWg&JPHW`JH;`I.PJH~Ob{H=-gB_JHx`.;W
JIHi.&J~O@A]A]>JIJHmJHX48A]ANU8Hb{H=-gB_JHx`.;WJIHi.&JAAN\8Hb{H=-gB_JHx`
.;WJIHi.&JAANd8Hb{H=-gB_JHx`.;WJIHi.&JAANk8Hb{H=-gB_JHx`.;WJIHi.&JAANr8H
b{H=-gB_JHx`.;WJIHi.&JAANy8Hb{H=-gB_JHx`.;WJIHi.&JAAN#8Hb{H=-gB_JHx`.;WJ
IHi.&JAAN*8Hb{H=-gB_JHx`.;WJIHi.&JAAN18Hb{H=-gB_JHx`.;WJIHi.&JAAUN8Hb{H=
-gB_JHx`.;WJIHi.&J %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% END OF SNIPPET

\end{document}

示例 3 提供:

在此处输入图片描述

虽然在 LaTeX 输入文件中找不到该短语的任何实例,但在 .pdf 输出文件中可以找到十个短语“我的独特章节名称”的实例。


(顺便说一句:我不知道“章节名称”或“节名称”是不是好的术语。一方面,章节也是文档的一部分。另一方面,节通常有由节号和/或节标题组成的标题,有时节标题也是该节的唯一标识符。)

答案3

TeX 在处理文本时,只是收集材料进行排版:它没有“检查”文本含义的意思。命令要么启动\,要么使用“活动”字符(下面有更多介绍):在这两种情况下,我们都在谈论宏。因此,如果我们有一块纯文本,除非采取某些措施来启用它,否则不会进行“进一步处理”。特别是,我们无法从 TeX 访问“为当前段落收集的文本”(我们可以安排提前查看即将推出令牌)。

处理材料的明显例子是命令的参数:

\foo{text}

例如,\foo可以检查是否text与几种特殊情况之一匹配。但是,在问题中我们讨论的是“一般”文本,因此这并不适用。

可以抓取环境的内容以进行类似的工作,因此可以想象抓取整个document正文并进行搜索和替换。但是,一次抓取的文本可能很多,而且对于任何“隐藏”的内容都不起作用

\newcommand\awkward{My special text}
Here I talk about `\awkward' stuff.

\input我想这在现实生活中是一个更大的风险。)非常搜索过程非常痛苦:本质上,我们必须一次检查一个字符,然后针对每个可能的匹配项进行分支!任何改变输入解释(类别代码)的事情也存在问题:例如,\verb会中断。因此,这实际上不是一种通用(或推荐!)方法。

扩展这个想法,值得注意的是,它确实listings通过逐字抓取内容、写入文件并以受控方式读回,在其环境中对文本进行搜索和替换。然而,这通常是针对短片段而不是整个文档。listings包的另一个优势是它不需要扩展它抓取的“其余”文本:一切都是逐字的除了针对特定搜索字符串。因此,从“受控环境”到“整个文档”并非不可能,但极具挑战性,当然与编辑源相比。

另一种方法是使所有字符都“活动”,这样每个字母都是命令而不是“文本”。大多数情况下,这些只会输出字母本身,但可以先附加一个前瞻,这样每个字母都会查看下一个标记以查看它是否与“特殊”文本匹配。然而,这又(极其)繁琐且容易出错,并且可能会破坏几乎所有其他功能(因为大多数代码都希望字母是文本!)。

详情沃纳的回答,LuaTeX 提供了一种解决这个问题的方法,因为在“Lua 端”有查看文本“关于”什么的概念(在输入缓冲区中)并在那里进行搜索和替换。

答案4

在某种程度上,您可以用 (La)TeX 轻松地将源文本文件复制到目标文本文件,其中 (La)TeX 在 verbatim-category-code-régime 下逐行读取源文件并逐行写入目标文件。这样,您就可以让 (La)TeX 在将每行写入目标文件之前对其进行一些替换。

根据我所概述的方法,替换仅在行内进行,而不是跨换行符进行。

根据我所概述的方法,替换复制不会在多个\input文件之间进行。

但您可能可以用它在 LaTeX 中编写一个小脚本来处理/替换复制所有输入文件。

我决定通过“破解”来实现这一点逐字抄录软件包(版本 2008/11/17 v0.06)由 Lars Madsen 和我编写。

这次“黑客”又制造了另一个小包裹,名为逐字替换副本.sty

% verbatimreplacementcopy.sty (C) 2016 by Ulrich Diez.
% Licence: LPPL.
\ProvidesPackage{verbatimreplacementcopy}[2016/12/08 v0.02 beta by Ulrich Diez]
\RequirePackage{verbatimcopy}[2008/11/17]
\begingroup
\newcommand*\VerbatimCopyB[2]{% {from file}{to file}
  \@bsphack
  \expandafter\def\expandafter\VC@target\expandafter{\VC@outputdir#2}%
  \IfFileExists{\VC@@quote#1\VC@@quote}%
  {%
    \bgroup
    \def\@verbatim{%
      \obeylines
      \let\do\@makeother
      \dospecials
    }%
    \let\endtrivlist\relax
    \def\verbatim@processline{%
      \begingroup
      \edef\verbatim@line{\the\verbatim@line}%
      \@onelevel@sanitize{\verbatim@line}%
      \verbatim@replacementhook
      \expandafter\endgroup
      \expandafter\verbatim@line\expandafter{\verbatim@line}%
      \immediate\write\verbatim@out{\the\verbatim@line}%
    }%
    \immediate\openout\verbatim@out\VC@@quote\VC@target\VC@@quote\relax
    \verbatiminput{\VC@@quote#1\VC@@quote}%
    \immediate\closeout\verbatim@out%
    \egroup%
  }%
  {%
    \PackageError{verbatimcopy}%
    {Source-file cannot be found}%
    {%
      For copying source-file to target-file it would be nice to
      have the source-file available.%
    }%
  }%
  \@esphack
}%
\ifx\VerbatimCopy\OldVerbatimCopy
  \expandafter\global\expandafter\let\csname VerbatimCopy\endcsname\VerbatimCopyB
\fi
\global\let\OldVerbatimCopy\VerbatimCopyB
\endgroup
\newcommand\verbatim@replacementhook{}%
\newcounter{verbatim@replacements}%
\newcommand\Replace{}%
\outer\def\Replace{%
  \stepcounter{verbatim@replacements}%
  \VCverbaction{\VCverbaction{\expandafter\VC@repldef\@firstofone}}{}%
}%
\newcommand\Noreplacements{}%
\outer\def\Noreplacements{\def\verbatim@replacementhook{}}%
\newcommand*\VC@repldef[2]{%
  \expandafter\VC@@repldef
  \csname VC@@repl\number\value{verbatim@replacements}\expandafter\endcsname
  \csname VC@repl\number\value{verbatim@replacements}\endcsname{#1}{#2}%
}%
\newcommand*\VC@@repldef[4]{%
  \@ifdefinable#1{%
    \def#1##1#3##2\@nil{%
      \ifx\@nil##2\@nil
        \expandafter\@firstoftwo
      \else
        \expandafter\@secondoftwo
      \fi
      {##1}{##1#4#1##2\@nil}%
    }%
  }%
  \newcommand*#2[1]{#1##1#3\@nil}%
  \expandafter\def\expandafter\verbatim@replacementhook\expandafter{%
    \verbatim@replacementhook
    \edef\verbatim@line{\expandafter#2\expandafter{\verbatim@line}}%
  }%
}%
\endinput

作为逐字替换副本.sty是基于逐字抄录包,我强烈建议阅读该包的手册。手册解释了如何更改目标目录等。它还指出\VerbatimCopy-macro 将会覆盖并因此破坏目标文件而没有任何警告!

您可以致电逐字替换副本.sty在用于复制替换文本文件的脚本中。我们称这样的脚本为替换器.tex

\documentclass{minimal}
\usepackage{verbatimreplacementcopy}

\Replace{My Unique Chapter Name}{ch:my-unique-chapter-name}
\Replace{My other Unique Chapter Name}{ch:my-other-unique-chapter-name}
\VerbatimCopy{Original.tex}{CopyA.tex}%
\Noreplacements
\Replace{My Unique Chapter Name}{ch:my/unique/chapter/name}
\Replace{My other Unique Chapter Name}{ch:my/other/unique/chapter/name}
\VerbatimCopy{Original.tex}{CopyB.tex}%

\stop

\Replace将为 添加另一个替换指令\VerbatimCopy

\VerbatimCopy每当\VerbatimCopy复制文本文件时,都会依次应用替换指令。

\Noreplacements将清除所有替换指令,以便在复制文本文件 \VerbatimCopy时不会应用任何替换指令。\VerbatimCopy

因此该脚本现在将复制文件原始.tex到文件复制A.tex
因此,Original.tex 的每一行都将执行以下操作:

  • 每个短语“我的独特章节名称”将被替换为“ch:my-unique-chapter-name”。
  • 替换后每个短语“我的其他唯一章节名称”将被替换为“ch:my-other-unique-chapter-name”。

然后\Noreplacements-directive 会清除所有替换指令。

然后这个脚本将复制文件原始.tex到文件复制B.tex
因此,Original.tex 的每一行都将执行以下操作:

  • 每个短语“我的独特章节名称”将被替换为“ch:my/unique/chapter/name”。
  • 替换后每个短语“我的其他唯一章节名称”将被替换为“ch:my/other/unique/chapter/name”。

如果内容原始.tex曾是

My Unique Chapter Name. Some text. My other Unique Chapter Name Some
text text. My other Unique Chapter Name. Phrases. Text.
Chapter Name. My Unique Chapter Name text text phrase 

,内容复制A.tex将:

ch:my-unique-chapter-name. Some text. ch:my-other-unique-chapter-name Some
text text. ch:my-other-unique-chapter-name. Phrases. Text.
Chapter Name. ch:my-unique-chapter-name text text phrase

以及内容复制B.tex将:

ch:my/unique/chapter/name. Some text. ch:my/other/unique/chapter/name Some
text text. ch:my/other/unique/chapter/name. Phrases. Text.
Chapter Name. ch:my/unique/chapter/name text text phrase

使用风险由您自行承担!
我不提供任何保证。
如果出现问题,我对零件不感兴趣。
(我感兴趣的是错误报告。)

相关内容