128

I have a macro which holds some text value, e.g. \macro{some text}. If it is empty, I would like for nothing to appear in the document, but it has some content of any kind which would produce text displayed in the document, I would like a message to appear, "Text: some text".

  • \macro{} would be considered empty.
  • \def\somedata{}, \macro{\somedata} would be considered empty.
  • \macro{0} would be considered not empty.

I have tried making a TeX conditional which can check if the value is not empty, but nothing I tried works, e.g.:

\ifx #1 {}
\else
    Text: #1
\fi

\ifx#1{}
\else
    Text: #1
\fi

\ifx #1 \nil
\else
    Text: #1
\fi

\ifx#1!=""
\else
    Text: #1
\fi

What is the correct syntax for creating a conditional in plain TeX with checks if a value is not empty?

4
  • 1
    Please, tell more clearly if "emptyness" is referred to the absence of tokens or to "no printed output": for instance, what should \mymacro{\empty} return?
    – egreg
    Commented Apr 24, 2012 at 8:21
  • Empty means no printed output. \mymacro{\empty} should be considered empty. \mymacro{} should also be considered empty.
    – Village
    Commented Apr 24, 2012 at 8:35
  • 1
    Please, edit the question, then: as it stands you're asking how to test for an "empty argument" which is a very different thing.
    – egreg
    Commented Apr 24, 2012 at 8:44
  • Related Question: Proper way to detect empty/blank text. Commented Sep 8, 2016 at 10:41

4 Answers 4

127

(1) Tests for empty token list as input

You can try

\def\temp{#1}\ifx\temp\empty
  <EMPTY>%
\else
  <NON EMPTY>%
\fi

If you know that a token, say \hfuzz, will not appear in #1, then

\ifx\hfuzz#1\hfuzz
  <EMPTY>%
\else
  <NON EMPTY>%
\fi

This, differently from the previous test, is expandable.

The safest test uses e-TeX:

\if\relax\detokenize{#1}\relax
  <EMPTY>%
\else
  <NON EMPTY>%
\fi

Using this last one shouldn't be a problem, but be warned that it doesn't work with "Knuth TeX".

Comments

You should know also that \ifx compares the two tokens that follow it and has no = sign. Similarly, \if compares the two tokens that follow it, but after having done complete expansion.

So, how does the last test work? With \detokenize{#1} the argument is transformed into a sequence of characters of category code 12, none of which is \if-equivalent to \relax. Thus with empty #1 the test would compare \relax with \relax and so return True; with #1 non empty, say abc, the code would be

\if\relax abc\relax<EMPTY>\else<NOT EMPTY>\fi

and the comparison between \relax and a returns False, so only the <NOT EMPTY> code would remain.

(2) Test for no printed output

In order to test for no printed output you can't have an expandable test:

\setbox0=\hbox{#1\unskip}\ifdim\wd0=0pt
  <EMPTY>%
\else
  <NOT EMPTY>%
\fi

This assumes that #1 doesn't contain vertical material, such as \vfill. Remove the \unskip if also "space output" should be considered. However, this can go wrong when \mymacro{\hskip1pt\hskip1pt} is called; it mostly depends on what you expect to go in the argument how to cope with limit cases.

If e-TeX is used, one can define a \foreverunspace macro that will remove all trailing spaces (assuming no whatsit appears):

\def\foreverunspace{%
  \ifnum\lastnodetype=11
    \unskip\foreverunspace
  \else
    \ifnum\lastnodetype=12
      \unkern\foreverunspace
    \else
      \ifnum\lastnodetype=13
        \unpenalty\foreverunspace
      \fi
    \fi
  \fi
}

and so the test can be

\setbox0=\hbox{#1\foreverunspace}\ifdim\wd0=0pt
  <EMPTY>%
\else
  <NOT EMPTY>%
\fi

that would remove any trailing combination of glues, kerns and penalties.

15
  • 3
    if there is a space between the braces in your second case, the width will be nonzero (although the output will not be visible in many contexts). better to use \ignorespaces and/or \unskip to get rid of such spaces. Commented Apr 24, 2012 at 14:55
  • 1
    Why do we need the second \relax in \if\relax\detokenize{#1}\relax? Could it be just a % instead, or does it have another role to play?
    – n.r.
    Commented Apr 27, 2015 at 16:16
  • 1
    This is really clever. So, \if compares the first \relax with either \detokenize{#1} or the second \relax depending on what #1 turns out to be.
    – n.r.
    Commented Apr 27, 2015 at 16:35
  • 1
    Your first five lines of your answer doesn't work, because the \temp macro is never empty because it includes \unskip primitive. I don't understand 36 votes with such obvious mistake in the answer.
    – wipet
    Commented May 7, 2015 at 19:59
  • 5
    @wipet Thanks, it probably was a copy paste problem. Since that's the worst method for checking emptyness, probably nobody noticed. Please, feel free to downvote, if you think an answer is bad (this isn't, of course, apart from the silly mistake that's easily fixed). And you're being rude.
    – egreg
    Commented May 7, 2015 at 20:02
45

The etoolbox package offers \ifdefempty (and its cousin \ifcsempty for control sequence names):

\documentclass{article}

\usepackage{etoolbox}

\def\mymacroA{}
\def\mymacroB{ }
\def\mymacroC{E}
\def\mymacroD{ E }

\begin{document}

\ifdefempty{\mymacroA}{YES}{NO}\par
\ifdefempty{\mymacroB}{YES}{NO}\par
\ifdefempty{\mymacroC}{YES}{NO}\par
\ifdefempty{\mymacroD}{YES}{NO}

\end{document}

Note that like the ifmtarg package commands pointed out by Werner, but unlike Caramdir's \ifx test, \ifdefempty will expand to "true" for macro arguments consisting of at least one space and nothing else.

2
  • 4
    etoolbox, version 2.5f (2018-08-18), has fixed the issue you mentioned. The revision history says, "(This version) Fix issue with \ifdefempty, \ifcsempty, \ifdefvoid and \ifcsvoid when applied to macros expanding to space tokens". Commented Aug 22, 2018 at 2:08
  • Note that when a macro is defined as \relax it will be nonempty for \ifdefempty. So better use \def\toto{} than \let\toto\relax if you want the emptyness test to be true.
    – yannis
    Commented Feb 4, 2020 at 18:21
20

Use the ifmtarg package:

enter image description here

\documentclass{article}
\usepackage{ifmtarg}% http://ctan.org/pkg/ifmtarg
\makeatletter
\newcommand{\isempty}[1]{%
  \@ifmtarg{#1}{YES}{NO}}
\newcommand{\isnotempty}[1]{%
  \@ifnotmtarg{#1}{YES}}
\makeatother
\begin{document}
\verb|\isempty{}   |: \isempty{} \par
\verb|\isempty{ }  |: \isempty{ } \par
\verb|\isempty{E}  |: \isempty{E} \par
\verb|\isempty{ E }|: \isempty{ E }

\bigskip

\verb|\isnotempty{}   |: \isnotempty{} \par
\verb|\isnotempty{ }  |: \isnotempty{ } \par
\verb|\isnotempty{E}  |: \isnotempty{E} \par
\verb|\isnotempty{ E }|: \isnotempty{ E }

\end{document}

It defines \@ifmtarg{<stuff>}{<true>}{<false>} and \@ifnotmtarg{<stuff>}{<true>}.

1
  • 11
    Note that \isempty{\empty} will output "NO". This may or may not be what the OP wants. Commented Apr 24, 2012 at 4:34
13

One way is to do:

\makeatletter
\def\test#1{%
    \def\tmp{#1}%
    \ifx\tmp\@empty%
        Nothing here!%
    \else%
        You passed: \texttt{#1}.%
    \fi%
}
\makeatother

Here \@empty is defined by LaTeX as \def\@empty{}.

1
  • 10
    \test{\empty} will give "You passed: .". AFAICS the author wants to test a macro for emptiness. Commented Apr 24, 2012 at 4:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .