TransWikia.com

searching for strings

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.

3 Answers

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

enter image description here

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

enter image description here

Answered by Steven B. Segletes on December 26, 2020

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP