5

This is a follow-up question to LaTeX count number of occurrences of a character in a string

This works fine for printing the value. Now what I need is to check if this command returns a specific value. I'd like to do something like

if \CountSubStr{~}{This~is a ~test} = 0 then A else B. 

Is it doable?

2 Answers2

5

Here's a fairly general macro:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\countsubstringtestTF}{mmmmm}
 {% #1 = substring to test for, #2 = string, #3 = test, #4 = true text, #5 = false text
  \seq_set_split:Nnn \l__salys_substring_seq { #1 } { #2 }
  \cs_set:Nn \__salys_substring_test:n
   {
    \int_compare:nTF { #3 } { #4 } { #5 }
   }
  \__salys_substring_test:n { \seq_count:N \l__salys_substring_seq - 1 }
 }
\seq_new:N \l__salys_substring_seq
\ExplSyntaxOff

\begin{document}

\countsubstringtestTF{~}{This is a test}{#1>2}{greater than 2}{not greater than 2}

\countsubstringtestTF{~}{This~is a~test}{#1>2}{greater than 2}{not greater than 2}

\countsubstringtestTF{~}{This~is~a~test}{#1>2}{greater than 2}{not greater than 2}

\countsubstringtestTF{~}{This is a test}{1 <= #1 <= 2}{between 1 and 2}{not between 1 and 2}

\countsubstringtestTF{~}{This~is a~test}{1 <= #1 <= 2}{between 1 and 2}{not between 1 and 2}

\countsubstringtestTF{~}{This~is~a~test}{1 <= #1 <= 2}{between 1 and 2}{not between 1 and 2}

\end{document}

The number of matches is denoted, in the third argument, by #1; you can do several sorts of comparisons, like the ones shown here.

enter image description here

If you want to allow the second argument to be a control sequence, you can define a variant.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\countsubstringtestTF}{smmmmm}
 {
  \IfBooleanTF { #1 }
   {
    \salys_countsubstringtest:nVnTF #2 { #3 } { #4 } { #5 } { #6 }
   }
   {
    \salys_countsubstringtest:nnnTF { #2 } { #3 } { #4 } { #5 } { #6 }
   }
 }
\cs_new_protected:Nn \salys_countsubstringtest:nnnTF
 {% #1 = substring to test for, #2 = string, #3 = test, #4 = true text, #5 = false text
  \seq_set_split:Nnn \l__salys_substring_seq { #1 } { #2 }
  \cs_set:Nn \__salys_substring_test:n
   {
    \int_compare:nTF { #3 } { #4 } { #5 }
   }
  \__salys_substring_test:n { \seq_count:N \l__salys_substring_seq - 1 }
 }
\seq_new:N \l__salys_substring_seq
\cs_generate_variant:Nn \salys_countsubstringtest:nnnTF { nV }
\ExplSyntaxOff

\newcommand{\mystringA}{This~is a~test} 
\newcommand{\mystringB}{This~is~a~test} 

\begin{document}

\countsubstringtestTF{~}{This is a test}{#1>2}{greater than 2}{not greater than 2}

\countsubstringtestTF*{~}{\mystringA}{#1>2}{greater than 2}{not greater than 2}

\countsubstringtestTF*{~}{\mystringB}{#1>2}{greater than 2}{not greater than 2}

\countsubstringtestTF{~}{This is a test}{1 <= #1 <= 2}{between 1 and 2}{not between 1 and 2}

\countsubstringtestTF{~}{This~is a~test}{1 <= #1 <= 2}{between 1 and 2}{not between 1 and 2}

\countsubstringtestTF{~}{This~is~a~test}{1 <= #1 <= 2}{between 1 and 2}{not between 1 and 2}

\end{document}
egreg
  • 1,121,712
  • What do I need to to to make this work if #2 string is defined at an earlier time as \def\mystring{This~is~a~test} and then called later in \countsubstringtestTF{~}{\mystring}{#1>2}{greater than 2}{not greater than 2} ? – Harry May 27 '17 at 12:11
  • 1
    @Harry I added it – egreg May 27 '17 at 12:36
  • Works like a charm. Grazie mille! – Harry May 27 '17 at 14:21
1

I don't really understand expl3, so may be better ways to do this. You can get this to work by adding by adding an \if_int_compare:w test to Werner's code:

\documentclass{article
\usepackage{xparse}

\ExplSyntaxOn
% \IfCountSubStrCompare{<substring>}{<string>}{=, < or >}{<int>}{<iftrue>}[<iffalse>]
\NewDocumentCommand{\IfCountSubStrCompare}{ m m m m m o }{
  \seq_set_split:Nnn \l_tmpa_seq { #1 } { #2 }
  \if_int_compare:w \int_eval:n {(\seq_count:N \l_tmpa_seq) - 1 } #3 #4
    #5 % true code
  \else
    \IfValueT{#6}{#6}
  \fi
}
\ExplSyntaxOff

\begin{document}

\IfCountSubStrCompare{~}{This~is a ~test}=0{Does not appear}[Appears]

\IfCountSubStrCompare{~}{This~is a ~test}=2{Appears twice}[Does not appear twice]

\IfCountSubStrCompare{~}{This~is a ~test}=3{Appears thrice}[Does not appear thrice]

\IfCountSubStrCompare{~}{This~is a ~test}>0{At least once }[Does not appear]

\IfCountSubStrCompare{~}{This~is a ~test}>1{More than once}[Once or less]

\IfCountSubStrCompare{~}{This~is a ~test}<2{Less than twice}[Twice or more]

\IfCountSubStrCompare{~}{This~is a ~test}<3{Less than thrice}[Thgrice or more]

\IfCountSubStrCompare{yes}{a yes b yes c yes deyesfg yehs ij}=4{Exactly four occurrences}

\end{document}

This produces the expected results:

enter image description here

The sytax of the \IfCountSubStrCompare command is:

\IfCountSubStrCompare{<substring>}{<string>}{=, < or >}{<int>}{<iftrue>}[<iffalse>]

So you need to give it a string to search for, a string to search in, a comparison operator (=, > or <), something to do if the comparison holds and, optionally, something to do if it doesn't.