当使用 pdftex 将多字节字符串作为 \def 传递时,如何获取字符串长度

当使用 pdftex 将多字节字符串作为 \def 传递时,如何获取字符串长度

我需要确定多字节字符串中打印字符的数量。此外,字符串通过 \def 传递给例程(最终通过 readarray 过程输入 csv 文件)。

我正在使用的代码多字节 StrLen?(汉字的 StrLen)因为包(xstring 和 stringstrings)不处理多字节字符。

我已经将示例简化为最少的代码

\documentclass[a4paper,11pt]{article}

% Attempt to get the length of a utf8 multi byte string
% Only works when supplied with the string directly
% does not work with \def strings
%
% https://tex.stackexchange.com/questions/419215/multibyte-strlen-strlen-for-chinese-characters
%
\def\zz#1{\edef\theresult{\zzz0#1\relax}}
\def\zzz#1#2{%
    \ifx\relax#2 \the\numexpr#1\relax
    \else
    \expandafter\zzz\expandafter{%
        \the\numexpr(#1+\ifnum\expandafter`\string#2<"80 1\else \ifnum\expandafter`\string#2>"BF 1 \else 0 \fi\fi
        \expandafter)\expandafter\relax\expandafter}%
\fi}%

\begin{document}
    \def\v1{abc}
    \v1 \zz{abc} \theresult\\   %this works
    \zz{\v1} \theresult\\       %this doesn't work Error: Missing = inserted for \ifnum.
\end{document}

答案1

#1您只需在定义上下文中展开一次即可\zz。这将允许第一个参数成为包含实际感兴趣的数据的宏。

\documentclass[a4paper,11pt]{article}

% Attempt to get the length of a utf8 multi byte string
% Only works when supplied with the string directly
% does not work with \def strings
%
% https://tex.stackexchange.com/questions/419215/multibyte-strlen-strlen-for-chinese-characters
%
\def\zz#1{\edef\theresult{\expandafter\zzz\expandafter0#1\relax}}
\def\zzz#1#2{%
    \ifx\relax#2 \the\numexpr#1\relax
    \else
    \expandafter\zzz\expandafter{%
        \the\numexpr(#1+\ifnum\expandafter`\string#2<"80 1\else \ifnum\expandafter`\string#2>"BF 1 \else 0 \fi\fi
        \expandafter)\expandafter\relax\expandafter}%
\fi}%

\begin{document}
    \def\v1{abc}
    \v1 \zz{abc} \theresult\\   %this works
    \zz{\v1} \theresult\\       %this doesn't work Error: Missing = inserted for \ifnum.
\end{document}

在此处输入图片描述

ps 在用户代码宏名中使用非字母符号通常不是一种好的做法,例如\v1。虽然变量可能被命名为\v1,但实际上它被命名为\v,需要强制参数1


补充

这是一种不同的方法,虽然不可扩展,但可以计算空格、组标记、未扩展的宏标记以及字符。它将扩展参数仅有的如果它是一个单独的标记,则涵盖了 OP 感兴趣的情况。

它计算组内的单个标记,而不是将组视为单个“标记”。

\documentclass[a4paper,11pt]{article}
\usepackage{tokcycle}
\newcounter{mycount}
\tokcycleenvironment\countenv
{\stepcounter{mycount}}
{\addtocounter{mycount}{2}\processtoks{##1}}
{\stepcounter{mycount}}
{\stepcounter{mycount}}
\newcommand\countem[1]{%
  \setcounter{mycount}{0}%
  \countenv#1\endcountenv
  \ifnum\themycount=1\relax
    \setcounter{mycount}{0}%
    \expandafter\countenv#1\endcountenv
  \fi
  \themycount
}

\begin{document}
\def\v{abc}

\countem{abc}

\countem{\v}

\countem{\v2345}

\countem{a b{c{\today}e}fg}
\end{document}

在此处输入图片描述

答案2

\zz以下是 David Carlisle 的宏的修改版本https://tex.stackexchange.com/a/419216/4427区分的参数是否\utfstrlen是单个宏。不要尝试在参数中挤入两个宏:要么是一个表示 UTF-8 字符串的单个宏,要么是一个显式字符列表。

\documentclass{article}
%\usepackage{xparse} % uncomment if using LaTeX release prior to 2020-10-01

\ExplSyntaxOn

\NewExpandableDocumentCommand{\utfstrlen}{m}
 {
  \egreg_utf_str_len:n { #1 }
 }

\cs_generate_variant:Nn \tl_to_str:n { e }

\cs_new:Nn \egreg_utf_str_len:n
 {
  \bool_lazy_and:nnTF { \tl_if_single_p:n { #1 } } { \token_if_cs_p:N #1 }
   {% #1 is a single control sequence
    \__egreg_utf_str_len:e { \tl_to_str:e { \exp_not:V #1 } }
   }
   {% #1 is a list of characters
    \__egreg_utf_str_len:e { \tl_to_str:n { #1 } }
   }
 }

\cs_new:Nn \__egreg_utf_str_len:n
 {
  \int_eval:n { \tl_map_function:nN { #1 } \__egreg_utf_char:n }
 }
\cs_generate_variant:Nn \__egreg_utf_str_len:n { e }

\cs_new:Nn \__egreg_utf_char:n
 {
  \int_compare:nTF { `#1 < "80 }
   { +1 } % ascii 7-bit
   { \int_compare:nT { `#1 > "BF } { +1 } } % prefix character
 }



\ExplSyntaxOff

\begin{document}

\utfstrlen{容容}

\utfstrlen{abc}

\utfstrlen{¢Àïα}

\def\test{容容abc¢Àïα}

\utfstrlen{\test}

\end{document}

在此处输入图片描述

就像 David 的回答一样,字符列表(可能是通过扩展一次宏获得的)一次解析一个字符;如果找到 7 位 ASCII 字符,则添加 1;如果找到代码高于十六进制 BF 的字符,则它是多字节字符的前缀,因此添加 1;否则忽略该字符。

该宏\utfstrlen是完全可扩展的。

相关内容