TeX - LaTeX Asked by David Handelman on December 26, 2020
Is there a TeX macro (in plain or AmS-TeX) that wil search for a specific string of characters? Specifically, I want a macro which will be part of an if/then macro of the form, if a given string xyz appears in a word, then …, otherwise something else.
This is a copy of expl3
's tl_if_in:nn
(IfContainedIn
) and str_if_in:nn
(StrIfContainedIn
). The former takes into account differences in catcodes, while the latter treats everything as catcode-12 (except for spaces, which are catcode-10). The syntax is StrIfContainedIn{<string>}{<substring>}{<true>}{<false>}
. The macro defines an auxiliary which is delimited by the <substring>
, then uses that to check if the <string>
contains the <substring>
. There is extra safety against using this macro in table cells and for empty arguments (see the documentation of l3tl
for implementation details).
If you are using a TeX engine with ε-TeX (detokenize
is needed to make a string out of the argument and for a robust IfEmpty
test), then:
catcode`@=11
def@firstoftwo#1#2{#1}
def@secondoftwo#1#2{#2}
defreturn@if@false{expandafter@secondoftworomannumeral}
defreturn@if@true{expandafter@firstoftworomannumeral}
chardefexp@end=0
%
defIfEmpty#1{%
ifrelaxdetokenize{#1}relax
return@if@true
else
return@if@false
fiexp@end}
%
defIfContainedIn#1#2{%
relaxiffalse{fi
defif@contained@aux##1#2{}%
expandafterIfEmptyexpandafter{if@contained@aux#1{}{}#2}%
{return@if@false}{return@if@true}%
iffalse}fi
exp@end}
%
defStrIfContainedIn#1#2{%
edef@@tmpa{noexpandIfContainedIn
{detokenize{#1}}{detokenize{#2}}}@@tmpa
{return@if@true}{return@if@false}%
exp@end}
catcode`@=12
StrIfContainedIn{hello}{el}{T}{F}
StrIfContainedIn{hello}{le}{T}{F}
bye
Otherwise, if you are using Knuth's TeX, then you need a poorman's detokenize
:
def@firstoftwo#1#2{#1}
def@secondoftwo#1#2{#2}
defstrip@prefix#1>{}
newtoksmyscratchtoks
longdefpoorman@detokenize#1#2{%
myscratchtoks{#2}edef#1{themyscratchtoks}%
edef#1{expandafterstrip@prefixmeaning#1}}%
defreturn@if@false{expandafter@secondoftworomannumeral}
defreturn@if@true{expandafter@firstoftworomannumeral}
chardefexp@end=0
%
defIfEmpty#1{%
poorman@detokenize@@tmpa{#1}%
ifrelax@@tmparelax
return@if@true
else
return@if@false
fiexp@end}
%
defIfContainedIn#1#2{%
relaxiffalse{fi
defif@contained@aux##1#2{}%
expandafterIfEmptyexpandafter{if@contained@aux#1{}{}#2}%
{return@if@false}{return@if@true}%
iffalse}fi
exp@end}
%
defStrIfContainedIn#1#2{%
poorman@detokenize@@tmpa{#1}%
poorman@detokenize@@tmpb{#2}%
edef@@tmpa{noexpandIfContainedIn
{@@tmpa}{@@tmpb}}@@tmpa
{return@if@true}{return@if@false}%
exp@end}
catcode`@=12
StrIfContainedIn{hello}{el}{T}{F}
StrIfContainedIn{hello}{le}{T}{F}
bye
Answered by Phelype Oleinik on December 26, 2020
In plain TeX
input listofitems
deffindstring#1#2#3#4{%
setsepchar{#1}%
readlistmylist{#2}%
ifnummylistlen>1relax#3else#4fi
}
findstring{abc}{My string ab*c does not contain it}{Found}{Not Found}
findstring{abc}{My string abc does contain it}{Found}{Not Found}
bye
Answered by Steven B. Segletes on December 26, 2020
A different approach from my other answer. This uses tokcycle
, instead of listofitems
. The expanded capability here is the ability to look deep down inside group content for the desired string, which is not possible with my other simpler answer. Likewise, macro names and groups can be part of the search string.
input tokcycle
deffindinstring#1#2#3#4{begingroup%
stripgroupingtrue
runcount=0relax%
tokcycle{nextctltok{##1}}
{nextctltok{opengroup}processtoks{##1}nextctltok{closegroup}}
{nextctltok{##1}}
{nextctltok{tcspace}}{#1}%
edefnumlet{theruncount}%
expandafterdefexpandaftersearchwordexpandafter{thecytoks}%
aftertokcycle{expandafterifxmatchfound T#3else#4fi}%
runcount=0relax%
defmatchfound{F}%
tokcycle{nextcmptok{##1}}
{nextcmptok{opengroup}processtoks{##1}nextcmptok{closegroup}}
{nextcmptok{##1}}
{nextcmptok{tcspace}}{#2}%
endgroup}
newcountruncount
catcode`@=11
defrotcytoks#1{cytoksexpandafterexpandafterexpandafter{%
expandaftertc@gobblethecytoks#1}}
catcode`@=12
deftestmatch#1{ifx#1searchwordgdefmatchfound{T}fi}%
defrotoradd#1#2{runcount=numexprruncount+1relax%
ifnumtheruncount>numletrelax#1else#2fiexpandafter
defexpandaftertmpexpandafter{thecytoks}}
defnextcmptok#1{rotoradd{rotcytoks{#1}}{addcytoks{#1}}testmatch{tmp}}
defnextctltok#1{runcount=numexprruncount+1relaxaddcytoks{#1}}
findinstring{anotmymac{b c}}{gf{vf{amymac{b c}g}gh}hn}{Found}{Not Found}
findinstring{amymac{b c}}{gf{vf{amymac{b c}g}gh}hn}{Found}{Not Found}
bye
Answered by Steven B. Segletes on December 26, 2020
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP