可以在参数内部使用/不会改变 catcode 的标准逐字环境的替代方案是什么?

可以在参数内部使用/不会改变 catcode 的标准逐字环境的替代方案是什么?

我怎样才能让输出排版与逐字环境的输出相同,例如此代码

\documentclass[12pt]{article}
\begin{document}

before
\begin{verbatim}
123456_789$^%
\end{verbatim}
after

\end{document}

但不需要改变 catcode(例如在命令参数/定义中使用它)?

(例子:因为\verb\texttt

澄清:我理解,如果不更改 catcode,则需要转义某些字符(\textbackslash\_如何在 LaTeX 中插入反斜杠或波浪符号 (~)?)。我对此没意见(这\texttt也是有用的)。

答案1

您可以重新实现 verbatim 环境,而无需更改 catcodes 的命令。如果这样做,您需要确保非可打印字符的正确排版和空格的非换行行为,并防止多个空格合并为一个空格,并可能手动防止扩展/执行活动字符等:

\documentclass[12pt]{article}

\makeatletter
\newenvironment{LookLikeVerbatimWithoutCatcodeChanges}{%
  \trivlist
  \item\relax
  \if@minipage\else\vskip\parskip\fi 
  \leftskip \@totalleftmargin
  \rightskip\z@skip
  \parindent\z@
  \parfillskip\@flushglue
  \parskip\z@skip
  \@@par
  \language\l@nohyphenation
  \@tempswafalse
  \def\par{%
    \if@tempswa
      \leavevmode
      \null
      \@@par
      \penalty\interlinepenalty
    \else
      \@tempswatrue
      \ifhmode\@@par\penalty\interlinepenalty\fi
    \fi
  }%
  %%%\let\do\@makeother\dospecials\obeylines 
  \verbatim@font
  \@noligs
  \everypar\expandafter{\the\everypar\unpenalty}%
  \frenchspacing
  %%%\@vobeyspaces\@xverbatim
}{%
  \if@newlist \leavevmode \fi \endtrivlist 
}%
\DeclareRobustCommand\MYspace{\leavevmode\nobreak\ }%
\DeclareRobustCommand\MYlinebreak{\par}%
\makeatother

\begin{document}

\medskip\hrule\medskip

before
\begin{verbatim}
123 456_789$^%
\end{verbatim}
after

\medskip\hrule\medskip

before\begin{LookLikeVerbatimWithoutCatcodeChanges}\MYlinebreak123\MYspace 456\string_789\string$\string^\%\MYlinebreak\end{LookLikeVerbatimWithoutCatcodeChanges}after

\medskip\hrule\medskip

\end{document}

在此处输入图片描述

缺点/陷阱:

请注意,类似这样的操作\fbox{before\begin{LookLikeVerbatimWithoutCatcodeChanges}...\end{LookLikeVerbatimWithoutCatcodeChanges}after}会产生有关缺少物品的错误消息。

这与类别代码无关。原因是环境verbatimLookLikeVerbatimWithoutCatcodeChanges内部基于“想要”垂直/水平模式,而不“喜欢”诸如受限水平模式之类的东西\trivlist\trivlist

在移动参数(例如,分段命令的参数)中,它们最终会出现在目录、书签中以及\nameref您可能需要的引用它们的任何位置\texorpdfstring等。的行为\nameref可能与预期不符。

答案2

环境的问题verbatim是:

环境verbatim期望其“主体”和短语\end{verbatim}在某些非标准 catcode 制度下进行标记化,从而产生一组与在标准 catcode 制度下进行标记化所获得的标记集不同的标记。

如果来自在标准 catcode 制度下标记的宏的参数,则该短语\end{verbatim}将不会被识别为环境的结束verbatim

作为一种解决方法,我可以提供例程

\DefineVerbatimToScantokens{⟨control-word-token⟩}{⟨xparse-argument-specifiers⟩}{%
  ⟨verbatim-material to be passed to \scantokens⟩
}%

⟨逐字逐句地传递给 \scantokens⟩在 verbatim-catcode-régime 下读取并标记。

然后在⟨逐字逐句地传递给 \scantokens⟩每一个都#被 catcode 6(参数)的字符标记替换。

然后⟨控制字标记⟩定义为根据以下方式处理参数⟨xparse-参数说明符⟩并通过⟨逐字逐句地传递给 \scantokens⟩\scantokens

注意事项:

  • 该例程不会接管您的任务,即确保在可以正确处理/排版的情况下引用/调用事物。

    例如,如果你定义一个⟨控制字标记⟩仅表示需要数学模式的标记,那么在执行时需要确保数学模式⟨控制字标记⟩

    例如,如果你有一个⟨控制字标记⟩扩展为逐字-环境,它基于\trivlist,那么你需要确保⟨控制字标记⟩仅在可以排版的情况下进行\trivlist,但实际情况并非如此,例如,使用受限的水平模式。

  • ⟨控制字标记⟩可以在宏参数中使用,但需要注意在移动参数中的使用:⟨控制字标记⟩在使用移动参数时必须已经定义。这可能是一个问题。这就是为什么我添加了另一个答案是定义宏\verblabel/\verbref

\documentclass{article}

%=== Code of \DefineVerbatimToScantokens ========================
% With older LaTeX-releases uncomment the following line:
%\usepackage{xparse}   
\NewDocumentCommand\DefineVerbatimToScantokens{mm}{%
  \begingroup
  \catcode`\^^I=12\relax
  \InnerDefineVerbatimToScantokens{#1}{#2}%
}%
\begingroup
\makeatletter
\def\InnerDefineVerbatimToScantokens#1#2{%
  \endgroup
  \NewDocumentCommand\InnerDefineVerbatimToScantokens{mm+v}{%
    \endgroup\ReplaceHashloop{##3}{##1}{##2}%
  }%
  \newcommand\ReplaceHashloop[3]{%
    \ifcat$\detokenize\expandafter{\Hashcheck##1#1}$%
    \expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
    {%
      \NewDocumentCommand{##2}{##3}{%
         \begingroup\newlinechar=\endlinechar
         \scantokens{\endgroup##1#2}%
      }%
    }{%
      \expandafter\ReplaceHashloop\expandafter{\Hashreplace##1}{##2}{##3}%
    }%
  }%
  \@ifdefinable\Hashcheck{\long\def\Hashcheck##1#1{}}%
  \@ifdefinable\Hashreplace{\long\def\Hashreplace##1#1{##1####}}%
}%
\catcode`\%=12\relax
\catcode`\#=12\relax
\InnerDefineVerbatimToScantokens{#}{%}%
%=== End of code of \DefineVerbatimToScantokens =================


% Be aware that indenting does matter within \DefineVerbatimToScantokens's
% <verbatim-material to be passed to \scantokens>-argument:

% We don't want arguments, thus the <xparse-argument-specifiers>-argument
% is left empty. As \DefineVerbatimToScantokens is a command for defining
% macros, the rules for hash-doubling apply:

\DefineVerbatimToScantokens\mymacro{}{%
before
\begin{verbatim}
12##345  6_789$^%

12##345  6_789$^%
\end{verbatim}
after%
}%



\begin{document}

\medskip\hrule\medskip

before
\begin{verbatim}
12#345  6_789$^%

12#345  6_789$^%
\end{verbatim}
after%

\medskip\hrule\medskip

\mymacro

\medskip\hrule\medskip

\end{document}

在此处输入图片描述

答案3

如果你有兴趣我可以提供一个界面

\verblabel{⟨referencing-label⟩}⟨verbatim-argument⟩

\verbref{⟨referencing-label⟩}

其中\verblabel⟨逐字论证⟩在 verbatim-catcode-régime 下,并将其复制到 .aux 文件,以便可以在文档的任何地方引用它\verbref。在引用时\verbref传递⟨逐字论证⟩\scantokens

该机制基于 kernel-macro \@newl@bel,但带有一些用于在 verbatim-catcode-régime 下复制/读取参数的包装器。这样,您会收到有关多次定义标签和标签被更改的警告。(\@setref无法使用,但需要以类似方式实现,因为 hyperref 已重新定义。)

(如果 xparser 提供了在读取 av/+v 参数时获取逐字分隔符的方法,则可以通过 xparser 轻松实现这一点。)

注意事项:

  • 该机制不会取代您的任务,即确保在可以正确处理/排版的情况下引用/调用事物。

    例如,如果您有一个动词标签仅表示需要数学模式的标记,那么您在引用它们时需要确保数学模式。

    例如,如果你有一个动词标签表示逐字-环境,它基于\trivlist,那么您需要确保它仅在\trivlist可以排版的情况下被引用,但事实并非如此,例如,在受限水平模式下。

  • 在第一次 LaTeX 运行中,虽然不存在 .aux 文件,但交叉引用标签/动词标签未定义,因此您得到相反。这意味着与需要匹配对应项的东西一起使用可能会至少在第一次运行 LaTeX 时导致问题。例如,不要做类似的事情:

    \[\left(x^2+y^2\verbref{foobar}%
    \verblabel{foobar}{=z^2\right)\]}%
    

    在第一次运行 LaTeX 时,这将产生一堆错误,因为动词标签foobar尚未定义。

  • 接口使用 .aux 文件。因此,如果 TeX 发行版中的某些字符以 ^^ 符号写入 .aux 文件(例如,由于字符翻译/tcx 文件),并且这些字符出现在逐字环境中/如果在引用时这些字符在没有类别代码 7(上标)的\scantokens情况下被处理,则可能会导致问题。^

\makeatletter
%%======================Code for \UDcollectverbarg=============================
%% \UDcollectverbarg{<mandatory 1>}{<mandatory 2>}|<verbatim arg>|
%% 
%% reads <verbatim arg> under verbatim-catcode-regime and delivers:
%%
%%    <mandatory 1>{<mandatory 2>{<verbatim arg>}{|<verbatim arg>|}}
%%
%% Instead of verbatim-delimiter | the <verbatim arg> can be nested in braces.
%% You cannot use percent or spaces or horizontal tab as verbatim-delimiter.
%%
%% You can use <mandatory 1> for nesting calls to \UDcollectverbarg.
%% <mandatory 2> gets the <verbatim arg> twice: Once without verbatim-delimiters/braces,
%% once surrounded by verbatim-delimiters/braces.
%% Reason: When you feed it to \scantokens you don't need the verbatim-delimiters.
%%         When you use it for writing to temporary files and reading back,
%%         you need them.
%%=============================================================================
%% Check whether argument is empty:
%%=============================================================================
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\@secondoftwo\string{\expandafter
  \@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \@secondoftwo\string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@secondoftwo\string}\expandafter\UD@stopromannumeral\@secondoftwo}{%
  \expandafter\UD@stopromannumeral\@firstoftwo}%
}%
%%=============================================================================
\newcommand\UDcollectverbarg[2]{%
  \begingroup
  \let\do\@makeother % <- this and the next line switch to
  \dospecials        %    verbatim-category-code-régime.
  \catcode`\{=1      % <- give opening curly brace the usual catcode so a 
                     %    curly-brace-balanced argument can be gathered in
                     %    case of the first thing of the verbatimized-argument 
                     %    being a curly opening brace.
  \catcode`\ =10     % <- give space and horizontal tab the usual catcode so \UD@collectverbarg
  \catcode`\^^I=10   %    cannot catch a space or a horizontal tab as its 4th undelimited argument.
                     %    (Its 4th undelimited argument denotes the verbatim-
                     %     syntax-delimiter in case of not gathering a
                     %     curly-brace-nested argument.)
  \catcode`\%=14     % <- make percent comment.
  \kernel@ifnextchar\bgroup
  {% seems a curly-brace-nested argument is to be caught:
    \catcode`\}=2    % <- give closing curly brace the usual catcode also.
    \UD@collectverbarg{#1}{#2}{}%
  }{% seems an argument with verbatim-syntax-delimiter is to be caught:
    \do\{% <- give opening curly brace the verbatim-catcode again.
    \UD@collectverbarg{#1}{#2}%
  }%
}%
\newcommand\UD@collectverbarg[3]{%
  \do\ %   <- Now that \UD@collectverbarg has the delimiter or
  \do\^^I%    emptiness in its 4th arg, give space and horizontal tab
         %    the verbatim-catcode again.
  \do\^^M% <- Give the carriage-return-character the verbatim-catcode.
  \do\%%   <- Give the percent-character the verbatim-catcode.
  \long\def\@tempb##1#3{%
    \def\@tempb{##1}%
    \UD@CheckWhetherNull{#3}{%
      \def\@tempc{{##1}}%
    }{%
      \def\@tempc{#3##1#3}%
    }%
    \@onelevel@sanitize\@tempb % <- Turn characters into their "12/other"-pendants.
                               %    This may be important with things like the 
                               %    inputenc-package which may make characters 
                               %    active/which give them catcode 13(active).
    \@onelevel@sanitize\@tempc
    \expandafter\expandafter\expandafter\UD@@collectverbarg% <- this "spits out the result.
    \expandafter\expandafter\expandafter{%
    \expandafter\@tempb\expandafter}%
    \expandafter{\@tempc}{#1}{#2}%
  }%
  \@tempb
}%
\newcommand\UD@@collectverbarg[4]{%
  \endgroup
  #3{#4{#1}{#2}}%
}%
%%================= End of code for \UDcollectverbarg =========================
%%
\DeclareRobustCommand\verblabel[1]{%
   \@bsphack\UDcollectverbarg{\@firstofone}{\UD@writeverblabel{#1}}%
}%
\newcommand\UD@writeverblabel[3]{%
  \begingroup
  \newlinechar=\endlinechar
  \immediate\write\@auxout{\string\newverblabel{#1}#3\@percentchar}%
  \endgroup
  \@esphack
}%
\newcommand\newverblabel[1]{%
  \UDcollectverbarg{\@firstofone}{\UD@defineverblabel{#1}}%
}%
\begingroup
\catcode`\Z=14
\catcode`\%=12
\@firstofone{Z
  \endgroup
  \newcommand\UD@defineverblabel[3]{Z
    \@newl@bel{vrblbl}{#1}{Z
      \begingroup\newlinechar=\endlinechar\scantokens{\endgroup#2%}Z
    }Z
  }Z
}%
\DeclareRobustCommand\verbref[1]{%
  % \expandafter\@setref\csname vrblbl@#1\endcsname\@empty{#1}%
  \expandafter\ifx\csname vrblbl@#1\endcsname\relax 
    \protect\G@refundefinedtrue
    \nfss@text{\reset@font\bfseries??}%
    \@latex@warning {Reference `#1' on page \thepage\space undefined}%
  \else 
    \csname vrblbl@#1\expandafter\endcsname
  \fi
}%
\makeatother

\documentclass{article}

%\usepackage{hyperref}

\begin{document}

% Referencing:
% ============

\verbref{verblabel 2}

\medskip\hrule\medskip

\verbref{verblabel 1}

% Defining:
% =========

\verblabel{verblabel 1}|something with

linebreaks|

%   Indenting does matter with verbatim-arguments:

\verblabel{verblabel 2}{%
before
\begin{verbatim*}
12#345  6_789$^%

12#345  6_789$^%
\end{verbatim*}
after
}%<--- end of \verblabel's argument

% This will throw a bunch of errors in the first LaTeX-run/while the
% verb-label "foobar" is undefined:
%\[\left(x^2+y^2\verbref{foobar}%
%\verblabel{foobar}{=z^2\right)\]}%

\end{document}

在此处输入图片描述

相关内容