在 TeX 中规范化文件路径字符串

在 TeX 中规范化文件路径字符串

我有一个包,它可以从相对文件路径递归加载许多小模块(本质上是 tex 文件)。由于模块可能会加载其他模块,我必须小心不要两次加载模块,因此我会记录已加载的文件路径。但是,为了使其工作,我必须规范化文件名以避免重复加载。所以我真正想要的是一个宏\mod@simpl,它通过消除任何出现的 来规范化相应的字符串foo/..。我已经能够对一个级别做到这一点(代码太丑了,我不想在这里包括它;一定有更好的方法),但不能对两个级别做到这一点。我也查看了包xstring,但这也无济于事。下表是我的测试表。

\begin{tabular}{|l|l|l|}\hline
  source       & result                          & should be \\\hline\hline
  aaa            & \mod@simpl{aaa}            &  aaa\\\hline
  ../../aaa      & \mod@simpl{../../aaa}      &  ../../aaa\\\hline
  aaa/bbb        & \mod@simpl{aaa/bbb}        & aaa/bbb\\\hline
  aaa/..         & \mod@simpl{aaa/..}         & \\\hline
  ../../aaa/bbb  & \mod@simpl{../../aaa/bbb}  & ../../aaa/bbb\\\hline
  ../aaa/../bbb  & \mod@simpl{../aaa/../bbb}  &  ../bbb\\\hline
  ../aaa/bbb     & \mod@simpl{../aaa/bbb}     &  ../aaa/bbb\\\hline
  aaa/bbb/../ddd & \mod@simpl{aaa/bbb/../ddd} & aaa/ddd\\\hline
  aaa/bbb/../..  & \mod@simpl{aaa/bbb/../..}  & \\\hline
\end{tabular}

请帮忙。

迈克尔

答案1

因为提到并使用了 LaTeX 包,所以我假设该包是 LaTeX 包。如果要在符合 TDS 的树中安装包,模块的文件名必须是唯一的,请参阅TDS 规范,“2.4 重复文件名”

texmf/texTEX 输入文件的名称在和的每个一级子目录中必须是唯一的texmf/tex/generic,[...]

此外,现代 TeX 发行版多年来一直支持在子目录中搜索。无需指定查找模块的目录。

然后,使用唯一的文件名,您可以使用 LaTeX 进行模块管理。LaTeX 中的模块是包。LaTeX 不会两次加载包文件。在第二次加载请求时,它只会检查选项。它会在以下情况下触发错误/警告:

  • 请求的文件名(\RequirePackage\usepackage)与提供的文件名(\ProvidesPackage文件内)不同。
  • 后续请求附带第一次包加载时不存在的选项。
  • 版本管理:包中提供的日期( )比或\ProvidesPackage的最后一个可选参数中的日期旧。\RequirePackage\usepackage

moda模块和包的示例pkg。模块的文件名将是 pkg-moda.sty。它包含以下行,例如:

\ProvidesPackage{pkg-moda}[2012/10/06 v2.4 Module moda for package pkg]

然后通过以下方式将该模块加载到其他模块或包中:

\RequirePackage{pkg-moda}

然后 LaTeX 将仅在第一次加载该模块。

如果模块需要某个版本,可以添加日期:

\RequirePackage{pkg-moda}[2012/10/06]

或者

\RequirePackage{pkg-moda}[2010/06/01]

如果自 2010/06/01 以来的版本pkg-moda实现了所需的功能。

纯 TeX

LaTeX 的包管理不是​​由纯 TeX 提供的。每个模块都可以定义一个在模块加载开始时检查的标记:

% File: pkg-moda.sty
\expandafter\ifx\csname [email protected]\endcsname\relax
\else
  \expandafter\endinput
\fi
\expandafter\def\csname [email protected]\endcsname{2012/10/06}
  • 文件名的扩展名无关紧要。我之所以使用,是.sty因为我的许多 LaTeX 包也可以通过纯 TeX(甚至是 iniTeX)加载,扩展名来自包的 LaTeX 要求。
  • 标记可以是任何东西,一个由模块专门定义的命令,一个由模块专门分配的寄存器,...

    在上面的例子中,我使用了 LaTeX 约定: \ver@<filename>是空宏:

    \expandafter\def\csname ver@<filename>\endcsname{}
    

    或者它包含 形式的日期YYYY/MM/DD,后面可选择跟一个空格、一个版本号和一个说明。

如果标记已定义,则文件开头的第一次检查将停止模块的进一步加载。

还可以在加载模块之前添加检查,例如:

\expandafter\ifx\csname [email protected]\endcsname\relax
  \input pkg-moda.sty\relax
\fi

那么模块文件甚至不会被打开,即使它已经被加载。

答案2

Heiko 已经回答了如何处理您描述的情况。事实上,让每个小文件定义一个宏来指示它已被加载是最好的解决方案。我将回答如何从aaa/..字符串中删除以使其规范化。

编辑:egreg 提到我的解决方案是错误的:我使用的正则表达式没有按照我声称的那样工作。现在应该修复这个问题了。另外,我曾半心半意地尝试支持 Windows,现在我决定把这个留给读者作为练习。

(解释如下。)

\documentclass{article}
\usepackage{expl3,l3regex}
\ExplSyntaxOn
\str_new:N \l__kohlhase_str
\cs_new_protected:Npn \mod@simpl #1
  {
    \str_set:Nn \l__kohlhase_str {/#1/}
    \regex_replace_all:nnN { /\./ } { / } \l__kohlhase_str
    \kohlhase_aux:N \l__kohlhase_str
    \str_substr:Nnn \l__kohlhase_str { 2 } { -2 }
  }
\cs_new_protected:Npn \kohlhase_aux:N #1
  {
    \regex_replace_all:nnNT
      {
        /
          ( [^/.]
          | [^/.][^/]
          | [^/][^/.]
          | [^/]{3,}
          )
        / \.\. /
      }
      { / }
      #1
      { \kohlhase_aux:N #1 }
  }
\ExplSyntaxOff
\begin{document}
\begin{tabular}{|l|l|l|}\hline
  source       & result                          & should be \\\hline\hline
  aaa            & \mod@simpl{aaa}            &  aaa\\\hline
  ../../aaa      & \mod@simpl{../../aaa}      &  ../../aaa\\\hline
  aaa/bbb        & \mod@simpl{aaa/bbb}        & aaa/bbb\\\hline
  aaa/..         & \mod@simpl{aaa/..}         & \\\hline
  ../../aaa/bbb  & \mod@simpl{../../aaa/bbb}  & ../../aaa/bbb\\\hline
  ../aaa/../bbb  & \mod@simpl{../aaa/../bbb}  &  ../bbb\\\hline
  ../aaa/bbb     & \mod@simpl{../aaa/bbb}     &  ../aaa/bbb\\\hline
  aaa/bbb/../ddd & \mod@simpl{aaa/bbb/../ddd} & aaa/ddd\\\hline
  aaa/bbb/../..  & \mod@simpl{aaa/bbb/../..}  & \\\hline
\end{tabular}
\end{document}

主要内容是使用 LaTeX3 的正则表达式模块递归地转向/aaa/../(如果模式匹配,/请参见对的调用:的 rue 参数)。的正确模式是什么?该模式几乎可以满足要求,但我们不想将其替换为,因此我们需要付出更多努力。这样做,我们很快意识到会遗漏的前导或尾随模式,因此我们首先在进行替换之前添加和添加。作为额外的奖励,我将更改为,因为在Unix 中代表当前目录。最后,删除前导和尾随(该步骤之前的字符串可能包含单个字符),仅保留一个子字符串。\kohlhase_aux:NT\regex_replace_all:nnNTaaa[^/]+/../..//aaa/..//.//.//

相关内容