我有一个字符串,想要将其解析为数字和非数字。
对于我的目的来说:
A数字能任何一个是任意连续的数字串或者以 . 开头的连续数字字符串,后跟另一个连续字符串。
A非数字是任何不是数字。
例如
ljksadflh23898129hfafh0324.22234
应该解析成类似
ljksadflh, 23898129, hfafh, 0324.22234
或者
ljksadflh/23898129/hfafh/0324.22234
或者任何您喜欢的东西,只要列表保持相同的顺序。
答案1
使用实验性的(但几乎可以发布)软件包l3regex
(在l3experimental
CTAN 上的捆绑包中找到),这项任务很容易完成。
\documentclass{article}
\usepackage{l3regex,xparse}
\ExplSyntaxOn
\seq_new:N \l_uiy_result_seq
\NewDocumentCommand { \UiySplit } { m }
{
\regex_extract_all:nnN { \D+ | \d+(?:\.\d*)? } {#1} \l_uiy_result_seq
\seq_map_inline:Nn \l_uiy_result_seq { item:~##1\par }
}
\ExplSyntaxOff
\begin{document}
\UiySplit{ljksadflh23898129hfafh0324.22234}
\end{document}
该\regex
行将用户输入拆分#1
为由一个或多个(+
)非数字(\D
)或(|
)一个或多个数字( )组成的部分\d
,后面可能跟着(?
作用于组(...)
,我们希望它是“非捕获的”,使用 完成(?:...)
)一个点(\.
转义点,因为点具有特殊含义)和零个或多个数字(\d*
)。下面的行映射了我们找到的所有匹配项,其中##1
是单个匹配项。当然,您可以对序列的项目执行任何您想做的事情\l_uiy_result_seq
。
编辑:该模块还提供正则表达式替换。如果我没记错语法,下面的代码应该可以工作。
\ExplSyntaxOn
\seq_new:N \l_uiy_result_seq
\NewDocumentCommand { \UiySplit } { m }
{
\tl_set:Nn \l_uiy_result_tl {#1}
\regex_replace_all:nnN
{ (\D+) (\d+(\.\d*)) }
{ \c{uiy_do:nn} \cB{\1\cE} \cB{\2\cE} }
\l_uiy_result_tl
\tl_use:N \l_uiy_result_tl
}
\cs_new_protected:Npn \uiy_do:nn #1#2 { \use:c {#1} {#2} }
\ExplSyntaxOff
这次,我捕获了非数字序列和数字,作为捕获组\1
和\2
。每个这样的出现都由宏替换\uiy_do:nn
(\c
在这种情况下,转义表示“构建命令”),然后是开始组(\cB
)字符{
(这次\c
表示类别代码),然后是非数字(\1
),然后是结束组(\cE
)字符}
,然后是另一个\cB{
,数字和结束\cE}
。
之后,标记列表看起来像\uiy_do:nn {ljksadflh} {1}
。然后我们只需将其内容与 一起使用即可\tl_use:N
。最后一步是实际定义\uiy_do:nn
。在这里,我将其定义为简单地从 构建一个命令#1
,并为其提供参数#2
。这个非常简单的操作可以在替换步骤中使用\c{\1}
“从 group 的内容构建一个命令\1
”来完成,从技术上讲,如果未定义相关命令,它会稍微好一些,产生“未定义的控制序列”错误。进行该错误检测的另一种选择是用 替换\use:c {#1} {#2}
,\cs_if_exist_use:cF {#1} { \msg_error:nnx { uiy } { undefined-command } } {#2}
并带有适当定义的错误消息。
答案2
这个纯粹是通过扩展来实现的,所以写入等都是安全的:
$ tex split
This is TeX, Version 3.1415926 (Web2C 2010)
(./split.tex
ljksadflh, 23898129, hfafh, 0324.22234
6, ljksadflh, 23898129, hfafh, 0324.22234
)
No pages of output.
Transcript written on split.log.
\def\xytest#1{?%
\ifnum`#1<46?\else
\ifnum`#1>57?\else
\ifnum`#1=47?\else!\fi\fi\fi}
\def\x#1{%
\ifx\relax#1%
\else
\if\xytest#1%
#1\expandafter\expandafter\expandafter\xx
\else
#1\expandafter\expandafter\expandafter\y
\fi
\fi}
\def\xx#1{%
\ifx\relax#1%
\else
\if\xytest#1%
#1\expandafter\expandafter\expandafter\xx
\else
, #1\expandafter\expandafter\expandafter\y
\fi
\fi}
\def\y#1{%
\ifx\relax#1%
\else
\if\xytest#1%
, #1\expandafter\expandafter\expandafter\xx
\else
#1\expandafter\expandafter\expandafter\y
\fi
\fi}
\immediate\write20{\x ljksadflh23898129hfafh0324.22234\relax}
\immediate\write20{\x 6ljksadflh23898129hfafh0324.22234\relax}
\bye
答案3
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
% Document commands
% \NewDocumentCommand\parsestring { m } { \uiy_parse_string:n { #1 } }
\NewDocumentCommand\MyMacro { s O{0} m }
{
\IfBooleanTF { #1 }
{
\uiy_parse_string:n { #3 }
\uiy_my_macro_parsed:n { #2 }
}
{
\clist_set:Nn \l_uiy_parsed_string_clist { #3 }
\uiy_my_macro_parsed:n { #2 }
}
}
% Inner commands
\tl_const:Nn \c_uiy_numbers_tl {0123456789.}
\tl_new:N \l_uiy_parsed_string_tl
\clist_new:N \l_uiy_parsed_string_clist
\seq_new:N \l_uiy_main_seq
\bool_new:N \l_uiy_previous_is_number_bool
\cs_generate_variant:Nn \tl_if_in:NnTF {NV}
\cs_new:Npn \uiy_parse_string:n #1
{
\tl_clear:N \l_uiy_parsed_string_tl
\seq_set_split:Nnn \l_uiy_main_seq {} { #1 }
\seq_pop_left:NN \l_uiy_main_seq \l_uiy_parsed_string_tl
\tl_if_in:NVTF \c_uiy_numbers_tl \l_uiy_parsed_string_tl
{ \bool_set_true:N \l_uiy_previous_is_number_bool }
{ \bool_set_false:N \l_uiy_previous_is_number_bool }
\seq_map_inline:Nn \l_uiy_main_seq
{
\tl_if_in:NnTF \c_uiy_numbers_tl { ##1 }
{
\bool_if:NF \l_uiy_previous_is_number_bool
{ \tl_put_right:Nn \l_uiy_parsed_string_tl { , } }
\tl_put_right:Nn \l_uiy_parsed_string_tl { ##1 }
\bool_set_true:N \l_uiy_previous_is_number_bool
}
{
\bool_if:NT \l_uiy_previous_is_number_bool
{ \tl_put_right:Nn \l_uiy_parsed_string_tl { , } }
\tl_put_right:Nn \l_uiy_parsed_string_tl { ##1 }
\bool_set_false:N \l_uiy_previous_is_number_bool
}
}
\clist_set:NV \l_uiy_parsed_string_clist \l_uiy_parsed_string_tl
}
\cs_new:Npn \uiy_my_macro_parsed:n #1
{
\int_compare:nTF { #1 = 0 }
{
\clist_map_inline:Nn \l_uiy_parsed_string_clist
{
% here you do something with the successive items
item: ~ ##1 \par
}
}
{
% here you do something with the #1-th item
\clist_item:Nn \l_uiy_parsed_string_clist { #1 - 1 } \par
}
}
\ExplSyntaxOff
\begin{document}
1. \MyMacro*{ljksadflh23898129hfafh0324.22234}
\bigskip
2. \MyMacro{ljksadflh,23898129,hfafh,0324.22234}
\bigskip
\bigskip
3. \MyMacro*[2]{ljksadflh23898129hfafh0324.22234}
\bigskip
4. \MyMacro[2]{ljksadflh,23898129,hfafh,0324.22234}
\end{document}
通过这个,你可以看到一个未解析的字符串被 *-variant 调用;可选参数指示只访问列表中的一个项目。当然,如果不知道数据的预期用途,就不可能说得更多。
答案4
这是我自己的 latex3 版本,与 egreg 的类似。这是一个概述版本,但可以修改。在此发布仅供参考。它在某些方面更简洁一些,因为它对标记使用了一些直接解析,而不是创建标记列表然后解析标记列表。(它不使用任何序列列表,因为不需要它们)
\ExplSyntaxOn
\tl_const:Nn \tlc_Digits {0123456789.}
\tl_const:Nn \tlc_Space { }
\bool_new:N \b_EON
\bool_new:N \b_FirstDigit
\NewDocumentCommand\ParseCmd{m}
{\tl_if_blank:nTF {#1} {}{
% Initialize Variables
\bool_set_false:N \b_EON
\bool_set_true:N \b_FirstDigit
% Parse Tokens
\tl_map_inline:nn {#1}
{
% Checks to see if the token is a digit or not
\tl_if_in:NnTF \tlc_Digits {##1}
{
\bool_set_false:N \b_EON
\bool_if:NTF \b_FirstDigit { [ } { , }
##1
\bool_set_false:N \b_FirstDigit
}
{ % Token not a digit
\bool_set_true:N \b_FirstDigit
\tl_if_in:NnTF \tlc_Space {##1} { } { }
\bool_if:NTF \b_EON { } { ], }
\bool_set_true:N \b_EON
}
}
% used for very last token
\bool_if:NTF \b_EON { } { ] }
}}
\ExplSyntaxOff