稍微改变一下描述的宏会出现问题可扩展宏,无需额外软件包即可提取 UTF-8/西里尔字符串的第一个字符对于可能包含两个或更多单词的参数。例如,对于参数 Vladimir Fedorovich,它将返回 VF,对于 Владимир Фёдорович - В.Ф。以下是备受尊敬的大卫·卡莱尔对于单词参数(当然它可以包含多个单词,但只会提取第一个单词的第一个字母):
\documentclass{article}
\usepackage[T2A]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[russian]{babel}
\makeatletter
\newcommand{\firstof}[1]{\expandafter\checkfirst#1\@nil}
\def\checkfirst#1{%
\ifx\UTFviii@two@octets#1%
\expandafter\gettwooctets
\else
\expandafter\@car\expandafter#1%
\fi
}
\def\gettwooctets#1#2#3\@nil{\UTFviii@two@octets#1#2}
\makeatother
\begin{document}
\firstof{Vladimir}
\firstof{Владимир}
\end{document}
我需要\firstof{Vladimir Fedorovich}
宏调用返回V.F.
并调用\firstof{Владимир Фёдорович}
- В.Ф.
。但理论上,参数中可以有超过 2 个单词。
我将非常感激您的帮助。
答案1
下面的操作就是你想要的。
我使用 L3 而不是 David 使用的低级来编码。基本思路仍然相同,如果它以扩展为的内容开头,\UTFviii@two@octets
我们就收集这两个八位字节,否则我们只使用第一个字符。但是,这也用于\text_purify:n
输入,以确保循环的输入仅为文本。
然后我们简单地在每个空格处进行分割(我们必须检查是否完成\quark_if_recursion_tail_stop:n
)并获取第一个字符(或组成 UTF8 八位字节对的两个字符)。
该宏\firstofwords
将其第一个参数作为每个单词首字母后应放置的内容,第二个参数是用空格分隔的单词列表。
\documentclass{article}
\usepackage[T2A]{fontenc}
\usepackage[russian]{babel}
\makeatletter
\newcommand{\firstof}[1]{\expandafter\checkfirst#1\@nil}
\def\checkfirst#1{%
\ifx\UTFviii@two@octets#1%
\expandafter\gettwooctets
\else
\expandafter\@car\expandafter#1%
\fi
}
\def\gettwooctets#1#2#3\@nil{\UTFviii@two@octets#1#2}
\ExplSyntaxOn
\cs_new:Npn \crosfield_first_of_words:nn #1#2
{ \exp_not:e { \__crosfield_first_of_words:e { \text_purify:n {#2} } {#1} } }
\group_begin:
\cs_set:Npn \__crosfield_tmp:n #1
{
\cs_new:Npn \__crosfield_first_of_words:n ##1 ##2
{
\__crosfield_first_of_words_spaces:nw {##2} ##1 #1 % #1 is a space
\q_recursion_tail #1 % #1 is a space
\q_recursion_stop
}
}
\__crosfield_tmp:n { ~ }
\group_end:
\cs_generate_variant:Nn \__crosfield_first_of_words:n { e }
\makeatletter
\cs_new:Npn \__crosfield_first_of_words_spaces:nw #1 #2 ~
{
\quark_if_recursion_tail_stop:n {#2}
\tl_if_head_eq_meaning:oNTF {#2} \UTFviii@two@octets
{ \__crosfield_first_of_words_aux:nnw #2 \q_stop }
{ \tl_head:n {#2} }
\exp_not:n {#1}
\__crosfield_first_of_words_spaces:nw {#1}
}
\makeatother
\cs_generate_variant:Nn \tl_if_head_eq_meaning:nNTF { o }
% there can't be a \q_stop in the argument as that would've caused an infinite
% loop in \text_purify:n, so this is fine and faster than allowing arbitrary
% contents.
\cs_new:Npn \__crosfield_first_of_words_aux:nnw #1#2#3 \q_stop
{ \exp_not:n { #1#2 } }
% define a LaTeX2e name for the above function
\cs_new_eq:NN \firstofwords \crosfield_first_of_words:nn
\ExplSyntaxOff
\begin{document}
\firstof{Vladimir}
\firstof{Владимир}
\firstofwords{.\ }{Vladimir Владимир} example
\firstofwords{.}{foo bar baz}
\end{document}
编辑:如果您只需要第二个参数并且希望.
每次都在第一个字母之间放置一个,则可以使用以下内容代替\cs_new_eq:NN \firstofwords \crosfield_first_of_words:nn
:
\newcommand* \firstofwords { \crosfield_first_of_words:nn {.} }
编辑2:
此变体仅将第一个参数放置在\firstofwords
元素之间,但不放置在最后一个参数之后。
\documentclass[varwidth,border=3.14]{standalone}
\usepackage[T2A]{fontenc}
\usepackage[russian]{babel}
\makeatletter
\newcommand{\firstof}[1]{\expandafter\checkfirst#1\@nil}
\def\checkfirst#1{%
\ifx\UTFviii@two@octets#1%
\expandafter\gettwooctets
\else
\expandafter\@car\expandafter#1%
\fi
}
\def\gettwooctets#1#2#3\@nil{\UTFviii@two@octets#1#2}
\ExplSyntaxOn
\cs_new:Npn \crosfield_first_of_words:nn #1#2
{ \exp_not:e { \__crosfield_first_of_words:e { \text_purify:n {#2} } {#1} } }
\group_begin:
\cs_set:Npn \__crosfield_tmp:n #1
{
\cs_new:Npn \__crosfield_first_of_words:n ##1 ##2
{
\__crosfield_first_of_words_spaces:Nnw \use_none:n {##2} ##1 #1 % #1 is a space
\q_recursion_tail #1 % #1 is a space
\q_recursion_stop
}
}
\__crosfield_tmp:n { ~ }
\group_end:
\cs_generate_variant:Nn \__crosfield_first_of_words:n { e }
\makeatletter
\cs_new:Npn \__crosfield_first_of_words_spaces:Nnw #1 #2 #3 ~
{
\quark_if_recursion_tail_stop:n {#3}
#1 {#2}
\tl_if_head_eq_meaning:oNTF {#3} \UTFviii@two@octets
{ \__crosfield_first_of_words_aux:nnw #3 \q_stop }
{ \tl_head:n {#3} }
\__crosfield_first_of_words_spaces:Nnw \exp_not:n {#2}
}
\makeatother
\cs_generate_variant:Nn \tl_if_head_eq_meaning:nNTF { o }
% there can't be a \q_stop in the argument as that would've caused an infinite
% loop in \text_purify:n, so this is fine and faster than allowing arbitrary
% contents.
\cs_new:Npn \__crosfield_first_of_words_aux:nnw #1#2#3 \q_stop
{ \exp_not:n { #1#2 } }
% define a LaTeX2e name for the above function
\cs_new_eq:NN \firstofwords \crosfield_first_of_words:nn
\ExplSyntaxOff
\begin{document}
\firstof{Vladimir}
\firstof{Владимир}
:\firstofwords{.\,}{Vladimir Владимир}.:
\firstofwords{.}{foo bar baz}
\end{document}
如果你希望始终.\,
在元素之间使用,并且.
在最后一个元素之后使用,则可以使用
\newcommand\firstofwords[1]{ \crosfield_first_of_words:nn {.\,} {#1} . }
而不是\cs_new_eq:NN
-line。
答案2
有了expl3
它\text_map_inline:nn
,您就可以做您想做的事情。这个想法就是在第一个项目之后打破映射,添加所需的句点。
\documentclass{article}
\usepackage[T2A]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[russian]{babel}
\ExplSyntaxOn
\NewDocumentCommand{\firstof}{m}
{
\crosfield_firstof:n { #1 }
}
\seq_new:N \l__crosfield_firstof_items_seq
\cs_new_protected:Nn \crosfield_firstof:n
{
% split the input at spaces
\seq_set_split:Nnn \l__crosfield_firstof_items_seq { ~ } { #1 }
% extract the first item and add a period
\seq_map_function:NN \l__crosfield_firstof_items_seq \__crosfield_firstof:n
}
\cs_new_protected:Nn \__crosfield_firstof:n
{% map the elements, breaking after the first
\text_map_inline:nn { #1 } { ##1 \text_map_break:n { . } }
}
\ExplSyntaxOff
\begin{document}
\firstof{Vladimir}
\firstof{Владимир}
\firstof{Vladimir Fedorovich}
\firstof{Владимир Фёдорович}
\end{document}
借用 Skillmon 的答案中的一些代码,我们可以获得可扩展性。
\documentclass{article}
\usepackage[T2A]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[russian]{babel}
\ExplSyntaxOn
\NewExpandableDocumentCommand{\firstof}{m}
{
\crosfield_firstof:n { #1 }
}
\cs_new:Npn \crosfield_firstof:n #1
{
\exp_not:e { \__crosfield_firstof:n { #1 } }
}
\group_begin:
\cs_set:Npn \__crosfield_tmp:n #1
{
\cs_new:Nn \__crosfield_firstof:n
{
\__crosfield_firstof_item:w ##1 #1 % #1 is a space
\q_recursion_tail #1 % #1 is a space
\q_recursion_stop
}
}
\__crosfield_tmp:n { ~ }
\group_end:
\cs_generate_variant:Nn \__crosfield_firstof:n { e }
\cs_new:Npn \__crosfield_firstof_item:w #1 ~
{
\quark_if_recursion_tail_stop:n { #1 }
\text_map_function:nN { #1 } \__crosfield_firstof_do:n
\__crosfield_firstof_item:w
}
\cs_new:Nn \__crosfield_firstof_do:n { #1 \text_map_break:n { . } }
\ExplSyntaxOff
\begin{document}
\firstof{Vladimir}
\firstof{Владимир}
\firstof{Vladimir Fedorovich}
\firstof{Владимир Фёдорович}
\edef\test{\firstof{Владимир Фёдорович}}
\show\test
\end{document}
该\show
命令将打印
> \test=macro:
->В.Ф..
在控制台上,见证了全面的可扩展性。