使用 \message 防止哈希值重复

使用 \message 防止哈希值重复

假设一个标记序列:

标记 1:类别代码 6 的显式哈希字符。
标记 2:类别代码 12 的显式 1。
标记 3:类别代码 6 的显式哈希字符。
标记 4:控制字标记\relax
标记 5:类别代码 6 的显式哈希字符。
令牌 6:类别代码 12 的显式 2。

就像是:,,,,,
#6112#6\relaxcontrol word token#6212

基于此标记序列,您如何才能\message在屏幕上获得哈希值未加倍的内容,其在屏幕上显示如下内容:
#1#\relax #2,它在屏幕上看起来像这样: ?

传统 TeX 中是否存在一种方法来转换 catcode 6 的哈希值,且不会使其加倍?

答案1

您可以搜索#并将其替换为 catcode 12 井号,然后显示它。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\hmessage}{m}
 {
  \tl_set:Nn \l__jewdokija_hmessage_tl { #1 }
  \regex_replace_all:nnN { \cP\# } { \cO\# } \l__jewdokija_hmessage_tl
  \iow_term:V \l__jewdokija_hmessage_tl
 }
\tl_new:N \l__jewdokija_hmessage_tl
\cs_generate_variant:Nn \iow_term:n { V }

\ExplSyntaxOff

\hmessage{#1#\relax#2}

终端将显示

> pdflatex jewdokija.tex
This is pdfTeX, Version 3.14159265-2.6-1.40.20 (TeX Live 2019) (preloaded format=pdflatex)
 restricted \write18 enabled.
entering extended mode
(./jewdokija.tex
LaTeX2e <2019-10-01> patch level 3
(/usr/local/texlive/2019/texmf-dist/tex/latex/base/article.cls
Document Class: article 2019/10/25 v1.4k Standard LaTeX document class
(/usr/local/texlive/2019/texmf-dist/tex/latex/base/size10.clo))
(/usr/local/texlive/2019/texmf-dist/tex/latex/l3packages/xparse/xparse.sty
(/usr/local/texlive/2019/texmf-dist/tex/latex/l3kernel/expl3.sty
(/usr/local/texlive/2019/texmf-dist/tex/latex/l3kernel/expl3-code.tex
(/usr/local/texlive/2019/texmf-dist/tex/latex/l3kernel/l3deprecation.def))
(/usr/local/texlive/2019/texmf-dist/tex/latex/l3backend/l3backend-pdfmode.def))
)
#1#\relax #2

答案2

您可以通过 Lua 往返来对字符串进行去标记化,而无需哈希加倍。如果您的字符串完全可扩展,那么这也可以完全扩展。

\documentclass{article}

\begin{document}

\message{\directlua{tex.sprint(-2, "\luaescapestring{#1#\relax#2}")}}

\end{document}
$ lualatex test.tex
This is LuaTeX, Version 1.11.2 (TeX Live 2020/dev) 
 restricted system commands enabled.
(./test.tex
LaTeX2e <2019-10-01> patch level 3

luaotfload | main : initialization completed in 0.140 seconds
(/opt/texlive/2019/texmf-dist/tex/latex/base/article.cls
Document Class: article 2019/10/25 v1.4k Standard LaTeX document class
(/opt/texlive/2019/texmf-dist/tex/latex/base/size10.clo)) (./test.aux)
#1#\relax #2 (./test.aux))
 384 words of node memory still in use:
   2 hlist, 1 vlist, 1 rule, 2 glue, 3 kern, 1 glyph, 5 attribute, 44 glue_spec
, 5 attribute_list, 2 write nodes
   avail lists: 2:7,3:1,4:1,5:4,7:1,9:1

warning  (pdf backend): no pages of output.
Transcript written on test.log.

答案3

tokcycle包根据用户定义的指令逐个处理其输入的 token。它具有环境形式和宏形式。无论哪种形式,#token 都被捕获为 cat-6,即\catSIXtrue设置一个标志,但它们会暂时转换为 cat-12 以供 Character 指令处理。

在这里,我将只告诉 character 指令输出它#获得的(现在是 cat-12 )标记,而不管\catSIXtrue(这实际上是默认值)。

我介绍了三种使用这种技术的方法。第一种方法是,只需将命令\message作为参数应用于\tokcyclexpress宏。然后,重新整理标记列表\the\cytoks

在第二种环境方法中,将\message{...}命令包装在\tokencyclexpress...\endtokencyclexpress

最后,在第三种方法中,我定义了一个新tokcycle环境\msg...\endmsg,您只需在其中插入原本\message参数。

因为#现在令牌循环中处理的令牌是 cat-12,所以它们不会加倍。

\documentclass{article}
\usepackage{tokcycle}[2021/03/10]
\begin{document}
Direct Macro Method:
\tokcyclexpress{\message{#1#\relax#2}}
\the\cytoks

Direct Environment Method:
\tokencyclexpress\message{#1#\relax#2}\endtokencyclexpress

Special environment method:
\xtokcycleenvironment\msg
{\addcytoks{##1}}
{\processtoks{##1}}
{\addcytoks{##1}}
{\addcytoks{##1}}
{}
{\cytoks\expandafter{\expandafter\message\expandafter{\the\cytoks}}}

\msg #1#\relax#2\endmsg 
Now go check the log file.
\end{document}

日志文件的屏幕截图:

在此处输入图片描述

答案4

我可以提供一个\MakeUnexpandedMessageWithSingleHashes带有底层循环的例程\StringifyBracesAndHashesLoop,该例程处理一个参数并将类别代码 1(开始组)或 2(结束组)或 6(参数)的每个显式字符标记字符串化,然后将 eTeX 应用于\detokenize其结果。

%% Copyright (C) 2019 by Ulrich Diez ([email protected])
%%
%% This work may be distributed and/or modified under the
%% conditions of the LaTeX Project Public Licence (LPPL), either
%% version 1.3 of this license or (at your option) any later
%% version. (The latest version of this license is in:
%% http://www.latex-project.org/lppl.txt
%% and version 1.3 or later is part of all distributions of LaTeX
%% version 1999/12/01 or later.)
%% The author of this work is Ulrich Diez.
%% This work has the LPPL maintenance status 'not maintained'.
%% Usage of any/every component of this work is at your own risk.
%% There is no warranty - neither for probably included
%% documentation nor for any other part/component of this work.
%% If something breaks, you usually may keep the pieces.
%%
%% For this example to compile properly, LaTeX 2e with eTeX-extensions
%% is required: \UD@CheckWhetherHash and \StringifyBracesAndHashes
%%              require \detokenize.

%%\errorcontextlines=10000

\documentclass{article}

\makeatletter
%%=============================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo,
%%    \UD@PassFirstToSecond, \UD@Exchange, \UD@removespace
%%    \UD@CheckWhetherNull, \UD@CheckWhetherBrace,
%%    \UD@CheckWhetherLeadingSpace, \UD@CheckWhetherHash (requires eTeX's
%%    \detokenize), \UD@ExtractFirstArg
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@removespace{}\UD@firstoftwo{\def\UD@removespace}{} {}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \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]{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
  {\UD@firstoftwo\expandafter{} \UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has leading
%%                        catcode-1-token>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has no leading
%%                        catcode-1-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\UD@firstoftwo\expandafter{} \UD@firstoftwo}%
  {\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is a
%%                               space-token>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is not
%%                               a space-token>}%
\newcommand\UD@CheckWhetherLeadingSpace[1]{%
  \romannumeral0\UD@CheckWhetherNull{#1}%
  {\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
  {\expandafter\UD@secondoftwo\string{\UD@@CheckWhetherLeadingSpace.#1 }{}}%
}%
\@ifdefinable\UD@@CheckWhetherLeadingSpace{%
  \long\def\UD@@CheckWhetherLeadingSpace#1 {%
    \expandafter\UD@CheckWhetherNull\expandafter{\UD@secondoftwo#1{}}%
    {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
    {\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
     \expandafter\expandafter\expandafter}\expandafter\expandafter
     \expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
  }%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument (which mut be a single token!!!!) is an
%% explicit character token of category code 6(parameter)
%%.............................................................................
%% \UD@CheckWhetherHash{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case <argument which is to
%%                       be checked> is an explicit character token of category
%%                       code 6>}%
%%                     {<Tokens to be delivered in case <argument which is to
%%                       be checked> is not an explicit character token of
%%                       category code 6>}%
\newcommand\UD@CheckWhetherHash[1]{%
  \romannumeral0%
  \expandafter\UD@CheckWhetherLeadingSpace\expandafter{\string#1}{%
    \UD@@CheckWhetherHash{\UD@removespace}{#1}%
  }{%
    \UD@@CheckWhetherHash{\UD@firstoftwo\expandafter{\expandafter}}{#1}%
  }%
  {%
    \UD@firstoftwo\expandafter{} \UD@secondoftwo
  }%
}%
\newcommand\UD@@CheckWhetherHash[2]{%
  \expandafter\expandafter\expandafter\UD@CheckWhetherNull
  \expandafter\expandafter\expandafter{%
  \expandafter#1\string#2}{%
    \expandafter\expandafter\expandafter\UD@CheckWhetherNull
    \expandafter\expandafter\expandafter{%
    \expandafter#1\detokenize{#2}}{%
      % Single token
      \UD@firstoftwo\expandafter{} \UD@secondoftwo
    }{% Explicit catcode-6-character-token
      \UD@firstoftwo\expandafter{} \UD@firstoftwo
    }%
  }%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%%   \UD@ExtractFirstArg{ABCDE} yields  {A}
%%
%%   \UD@ExtractFirstArg{{AB}CDE} yields  {AB}
%%.............................................................................
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArg[1]{%
  \romannumeral0%
  \UD@ExtractFirstArgLoop{#1\UD@SelDOm}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  { #1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%-----------------------------------------------------------------------------    
%% In case an argument's first token is an opening brace, stringify that and
%% add another opening brace before that and remove everything behind the 
%% matching closing brace:
%% \UD@StringifyOpeningBrace{{Foo}bar} yields {{Foo}  whereby the second
%% opening brace is stringified:
%%.............................................................................
\newcommand\UD@StringifyOpeningBrace[1]{%
  \romannumeral0%
  \expandafter\UD@ExtractFirstArgLoop\expandafter{%
    \romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
    \expandafter\expandafter
    \expandafter            {%
    \expandafter\UD@firstoftwo
    \expandafter{%
    \expandafter}%
    \romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
    \expandafter\string
    \expandafter}%
    \string#1%
  \UD@SelDOm}%
}%
%%-----------------------------------------------------------------------------    
%% In case an argument's first token is an opening brace, remove everything till 
%% finding the corresponding closing brace. Then stringify that closing brace:
%% \UD@StringifyClosingBrace{{Foo}bar} yields: {}bar} whereby the first closing
%% brace is stringified:
%%.............................................................................
\newcommand\UD@StringifyClosingBrace[1]{%
   \romannumeral0\expandafter\expandafter\expandafter
                 \UD@StringifyClosingBraceloop
                 \UD@ExtractFirstArg{#1}{#1}%
}%
\newcommand\UD@CheckWhetherStringifiedOpenBraceIsSpace[1]{%
%% This can happen when character 32 (space) has catcode 1...
  \expandafter\UD@CheckWhetherLeadingSpace\expandafter{%
    \romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
    \expandafter\UD@secondoftwo
    \expandafter{%
    \expandafter}%
    \expandafter{%
    \romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
    \expandafter\UD@firstoftwo
    \expandafter{%
    \expandafter}%
    \romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
    \expandafter\string
    \expandafter}%
    \string#1%
  }%
}%
\newcommand\UD@TerminateStringifyClosingBraceloop[2]{%
  \UD@Exchange{ }{\expandafter\expandafter\expandafter}%
  \expandafter\expandafter
  \expandafter{%
  \expandafter\string      
  \romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
  \expandafter#1%
  \string#2%
  }%
}%
\newcommand\UD@StringifyClosingBraceloopRemoveElement[4]{%
  \expandafter\UD@PassFirstToSecond\expandafter{\expandafter
  {\romannumeral0\expandafter\UD@secondoftwo\string}{}%
    \UD@CheckWhetherStringifiedOpenBraceIsSpace{#4}{%
      \UD@Exchange{\UD@removespace}%
    }{%
      \UD@Exchange{\UD@firstoftwo\expandafter{\expandafter}}%
    }{%
      \UD@Exchange{ }{\expandafter\expandafter\expandafter}%
      \expandafter#1%
      \romannumeral0\UD@Exchange{ }{\expandafter\expandafter\expandafter}%
      \expandafter
    }%
    \string#4%
  }{\expandafter\UD@StringifyClosingBraceloop\expandafter{#2#3}}%
}%
\newcommand\UD@StringifyClosingBraceloop[2]{%
  \UD@CheckWhetherNull{#1}{%
    \UD@CheckWhetherStringifiedOpenBraceIsSpace{#2}{%
      \UD@TerminateStringifyClosingBraceloop{\UD@removespace}%
    }{%
      \UD@TerminateStringifyClosingBraceloop{\UD@firstoftwo\expandafter{\expandafter}}%
    }%
    {#2}%
  }{%
    \UD@CheckWhetherLeadingSpace{#1}{%
      \UD@StringifyClosingBraceloopRemoveElement
      {\UD@removespace}{\UD@removespace}%
    }{%
      \UD@StringifyClosingBraceloopRemoveElement
      {\UD@firstoftwo\expandafter{\expandafter}}{\UD@firstoftwo{}}%
    }%
    {#1}{#2}%
  }%
}%
%%-----------------------------------------------------------------------------
%% Create message-text from unexpanded brace balanced  set of tokens where
%% hashes are _not_ doubled:
%%
%% \MakeUnexpandedMessageWithSingleHashes{<token 1><token 2>...<token n>}
%%
%% yields:  <if token 1 is explicit character token of catcode 6, then 
%%           stringification+detokenization of token 1, else detokenization 
%%           of token 1>%
%%          <if token 2 is explicit character token of catcode 6, then 
%%           stringification+detokenization of token 2, else detokenization 
%%           of token 2>%
%%          ...
%%          <if token n is explicit character token of catcode 6, then 
%%           stringification+detokenization of token n, else detokenization 
%%           of token n>%
%%
%% whereby "stringification of token" means the result of applying \string
%% to the token in question.
%% Due to \detokenize/\romannumeral-expansion the result is delivered after two
%% \expandafter-chains.
%%.............................................................................
\newcommand\MakeUnexpandedMessageWithSingleHashes[1]{%
  \detokenize\expandafter{\romannumeral0\StringifyBracesAndHashesLoop{}{#1}}%
}%
%%-----------------------------------------------------------------------------
%% Stringify explicit character tokens of catcode 1 or 2 or 6:
%%
%% \romannumeral0\StringifyBracesAndHashesLoop{}{<token 1><token 2>...<token n>}
%%
%% yields:  <if token is explicit character token of catcode 1/2/6, then 
%%           stringification of token 1, else token 1>%
%%          <if token is explicit character token of catcode 1/2/6, then 
%%           stringification of token 2, else token 2>%
%%          ...
%%          <if token is explicit character token of catcode 1/2/6, then 
%%           stringification of token n, else token n>%
%%
%% whereby "stringification of token" means the result of applying \string
%% to the token in question.
%% Due to \romannumeral-expansion the result is delivered after two
%% \expandafter-chains.
%%
%% Syntax:  
%%
%% \StringifyBracesAndHashesLoop{<token 1 or stringification of token 1>%
%%                               ...%
%%                               <token k-1 or stringification of token k-1>}%
%%                              {<token k>...<token n>}
%%.............................................................................
\newcommand\StringifyBracesAndHashesLoop[2]{%
  \UD@CheckWhetherNull{#2}{ #1}{%
    \UD@CheckWhetherBrace{#2}{%
      \expandafter\expandafter\expandafter\UD@Exchange
      \expandafter\expandafter\expandafter{%
        \UD@StringifyClosingBrace{#2}%
      }{%
        \expandafter\StringifyBracesAndHashesLoop\expandafter{%
          \romannumeral0%
          \expandafter\expandafter\expandafter\UD@Exchange
          \expandafter\expandafter\expandafter{\UD@StringifyOpeningBrace{#2}}{\StringifyBracesAndHashesLoop{#1}}%
        }%
      }%
    }{%
      \UD@CheckWhetherLeadingSpace{#2}{%
        \expandafter\UD@PassFirstToSecond\expandafter{\UD@removespace#2}{%
          \StringifyBracesAndHashesLoop{#1 }%
        }%
      }{%
        \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#2}{%
          \expandafter\StringifyBracesAndHashesLoop\expandafter{%
            \romannumeral0%
            \expandafter\UD@CheckWhetherHash\romannumeral0\UD@ExtractFirstArgLoop{#2\UD@SelDOm}{%
              \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\UD@Exchange
              \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter{%
                \expandafter\expandafter\expandafter\string
                \expandafter\UD@Exchange
                \romannumeral0\UD@ExtractFirstArgLoop{#2\UD@SelDOm}{}%
              }%
            }{%
              \expandafter\expandafter\expandafter\UD@Exchange
              \expandafter\expandafter\expandafter{%
                \expandafter\UD@Exchange
                \romannumeral0\UD@ExtractFirstArgLoop{#2\UD@SelDOm}{}%
              }%
            }{ #1}%
          }%
        }%
      }%
    }%
  }%
}%
\makeatother

\begin{document}

\message{^^JHere come the messages^^J======================^^J^^J}%

\message{\MakeUnexpandedMessageWithSingleHashes{#1#\relax#2}}%
\message{^^J^^J}

{%
\catcode`\R=6 %
\message{\MakeUnexpandedMessageWithSingleHashes{R1#\relaxR2 s p a c e s {br{a{c}}es} }}%
\message{^^J^^J}
}%

\end{document}

在此处输入图片描述

相关内容