我需要确定多字节字符串中打印字符的数量。此外,字符串通过 \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
是完全可扩展的。