使用原语从字符串中提取位置 x 处的字符

使用原语从字符串中提取位置 x 处的字符

假设我有一个宏

\def\x{This is a string 2015/12.}

我想要一个宏\extract{string}{number},它仅使用原始宏返回字符串中位置编号处的字符。我查看了该xstring包,但对纯原始解决方案感兴趣。这应该很简单,但我很难弄清楚。我认为它应该与 expand 一起使用。

答案1

假设您的字符串仅包含可打印的 ASCII 字符,这是一个相当笨拙的实现。最后一个例子表明,括号组被视为单个项目。

\catcode`@=11

\def\extract{\futurelet\next\extract@save}
\def\extract@save{%
  \ifx\next[%
    \expandafter\extract@save@opt
  \else
    \let\extract@return\@firstofone
    \expandafter\extract@
  \fi
}
\def\extract@save@opt[#1]{%
  \def\extract@return##1{\def#1{##1}}%
  \extract@
}
\def\extract@#1#2{%
  \edef\extract@string{#1}%
  \extract@loop=\z@
  \extract@max=#2\relax
  \expandafter\extract@i\extract@string\extract
}
\def\extract@i{%
  \advance\extract@loop\@ne
  \futurelet\next\extract@ii
}
\def\extract@ii{%
  \ifx\next\extract
    \expandafter\@gobble
  \else
    \expandafter\extract@iii
  \fi}
\def\extract@iii{%
  \ifx\next\space@token
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {\extract@check@space}%
  {\extract@check}%
}
\def\extract@check@space{%
  \ifnum\extract@loop=\extract@max
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {\extract@return{ }\extract@finish}%
  {\expandafter\extract@i\@firstofone}%
}
\def\extract@check#1{%
  \ifnum\extract@loop=\extract@max
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  {\extract@return{#1}\extract@finish}%
  {\extract@i}%
}
\def\extract@finish#1\extract{}
\newcount\extract@loop
\newcount\extract@max
\long\def\@firstoftwo#1#2{#1}
\long\def\@secondoftwo#1#2{#2}
\long\def\@firstofone#1{#1}
\long\def\@gobble#1{}
\begingroup\def\\ \\{\endgroup\let\space@token= }\\ \\
\catcode`@=12

X\extract{abc def}{3}X (should be c)

X\extract{abc def}{4}X (should be space)

X\extract{abc def}{5}X (should be d)

X\extract{abc def}{8}X (should be empty)

X\extract{abc }{4}X (should be space)

\def\mystring{abc def}

X\extract\mystring{7}X (should be f)

\extract[\foo]\mystring{6}

{\tt\meaning\foo}

\extract[\foo]{A{BC}D}{2}

{\tt\meaning\foo}

\bye

如果没有可选参数,则该项目仅在输入流中返回;如果有可选参数,则它将保存在括号内给出的控制序列中。

在此处输入图片描述

当然,我会用完全不同的方法。使用此代码,\extract可以完全扩展,如最后的示例所示\edef。请注意,您也可以向后计数。该代码需要 e-TeX 引擎,因此不需要 Knuth TeX。

\input expl3-generic

\ExplSyntaxOn
\cs_new:Npn \extract #1 #2
 {
  \str_item:fn { #1 } { #2 }
 }
\cs_generate_variant:Nn \str_item:nn {f}
\ExplSyntaxOff

X\extract{abc def}{3}X (should be c)

X\extract{abc def}{4}X (should be space)

X\extract{abc def}{5}X (should be d)

X\extract{abc def}{8}X (should be empty)

X\extract{abc }{4}X (should be space)

\def\mystring{abc def}

X\extract\mystring{7}X (should be f)

X\extract\mystring{-2}X (should be e)

\edef\foo{\extract\mystring{6}}

{\tt\meaning\foo}

\bye

在此处输入图片描述

答案2

类似这样的?显示忽略和计数空格。为 pdftex 设置,但带有 pdflatex 的注释行。

%\documentclass{article}
\def\extract{\catcode`\ =\active\extractx}
\def\extractx#1#2{\def\extractcount{#2}\expandafter\extracthelp#1\relax}
\def\extracthelp#1#2\relax{%
  \edef\extractcount{\the\numexpr\extractcount-1\relax}%
  \ifnum\extractcount=0\relax``#1''\else%
    \ifx\relax#2\relax[EOF]\else\extracthelp#2\relax\fi\fi%
  }
%\begin{document}
Spaces ignored: \def\x{This is a string 2015/12.}

\extract{\x}{1}

\extract{\x}{5}

\extract{\x}{6}

\extract{\x}{22}

Spaces counted: {\catcode`\ =\active \gdef\x{This is a string 2015/12.}}

\extract{\x}{1}

\extract{\x}{5}

\extract{\x}{6}

\extract{\x}{22}

%\end{document}
\bye

在此处输入图片描述

相关内容