19

I need to produce a list of all words that are emphasised in my LaTeX document (=which are enclosed by \emph{...}).

How can I use awk to process the .tex file and extract all patterns starting with \emph{ and ending with a } and which do not contain a closing curly bracket within (otherwise almost the whole document would be matching the pattern). Finally, the result of the search should be a list like this:

  1. FirstEmphasizedWord
  2. AnotherEmphasizedWord
  3. TheLastEmphasizedWord

(It would be nice if the list was already formatted as a numbered LaTeX list) What is the correct input for regular expression matching when I want to use awk for this job?

2
  • Hi Juri, Welcome to TeX.SE! Here's a link to a somewhat-related question http://tex.stackexchange.com/q/67709/6621
    – cmhughes
    Nov 1, 2012 at 0:14
  • 1
    Welcome to TeX.sx! Usually, we don't put a greeting or a "thank you" in our posts. While this might seem strange at first, it is not a sign of lack of politeness, but rather part of our trying to keep everything very concise. Upvoting is the preferred way here to say "thank you" to users who helped you.
    – Mensch
    Nov 1, 2012 at 0:21

4 Answers 4

24

If you are not stuck on using an external script to do this, you can just do it LaTeX by redefining the \emph{} macro:

enter image description here

Notes:

  • Note how nested usage of \emph{} is treated (items 4 and 5 in the list). This may or may not be the desired behavior, but this should be fairly easy to change depending on the desired outcome.

References:

Code:

\documentclass{article}
\usepackage{letltxmacro}

% https://tex.stackexchange.com/questions/14393/how-keep-a-running-list-of-strings-and-then-process-them-one-at-a-time
\def\ListOfEmphText{}
\makeatletter
\newcommand{\AddToListOfEmphText}[1]{%
    \g@addto@macro\ListOfEmphText{\item {#1}}%
}
\makeatother

\LetLtxMacro\OldEmph\emph
\DeclareRobustCommand{\emph}[1]{\AddToListOfEmphText{#1}\OldEmph{#1}}

\begin{document}
\emph{First emphasized Text,  with a comma} followed by
some normal text.
\emph{Second emphasized Text} followed by
some more normal text.

Some normal text before
\emph{Third emphasized Text} also followed by
some normal text

Here is some nested usage: \emph{abc \emph{def} ghi}, just to test that it works.

\bigskip\noindent
The macro \verb|\emph{}| was used on:
\begin{enumerate}
    \ListOfEmphText
\end{enumerate}

\end{document}
2
  • I'm not sure, but I believe you don't want the new \emph to be robust, because then if you use it in \section argument and print \tableofcontents, it will appear twice in the list, won't it? (Of course, it depends on what is the desired behaviour)
    – yo'
    Nov 2, 2012 at 10:07
  • @tohecz: Yes that does appear to be the case, but using \DeclareRobustCommand was suggested by egreg. An easy fix if that was not desired would be to move the redefinition of \emph to be after \tableofcontents. Nov 2, 2012 at 17:33
6

Doing this with awk will be tricky as (AFAIK) it struggles with non-greedy regular expressions. Note that we'll need them to be non-greedy because of lines such as

\emph{multiple}  occurrences on the \emph{same line}

Perl can handle this pretty easily; you run the following script as

perl emphasize.pl myfile.tex

which will output to the terminal. Of course, you can output it into another file, such as

perl emphasize.pl myfile.tex> emphasizedtext.tex

Here's a test case:

myfile.tex

\documentclass{article}
\begin{document}

\emph{first} word is \emph{emphasized} and here's another \emph{one}.

here's another \emph{word} that is \emph{also} emphacized
\begin{enumerate}
  \item \emph{should} also work
  \item anywhere \emph{else too with multiple words}
\end{enumerate}
will struggle with nested: \emph{here is a \emph{nest}}
\end{document}

emhpasize.pl

#!/usr/bin/perl

use strict;
use warnings;

# for emphasized content
my @emphcontent=();
my $emphphrase='';
my $counter=0;

# loop through the lines in the INPUT file
while(<>)
{
    # check for 
    #   \emph{}...
    # and make sure not to match
    #   %\emph{}...
    # which is commented
    if($_ =~ m/\\emph{(.*?)}/ and $_ !~ m/^%/)
    {
      # store the emphasized content
      while($_ =~ m/\\emph{(.*?)}/g){
        push(@emphcontent,$1);
      }
    }
}

# output the emphasized content
foreach $emphphrase (@emphcontent)
{
    $counter++;
    print $counter,". ",$emphphrase,"\n";
}

exit

output

1. first
2. emphasized
3. one
4. word
5. also
6. should
7. else too with multiple words
8. here is a \emph{nest

Or else you might prefer

# start the enumerate environment
print "\\begin{enumerate}\n";

# output the emphasized content
foreach $emphphrase (@emphcontent)
{
    print "\\item ",$emphphrase,"\n";
}
# end the enumerate environment
print "\\end{enumerate}\n";

which gives

\begin{enumerate}
\item first
\item emphasized
\item one
\item word
\item also
\item should
\item else too with multiple words
\item here is a \emph{nest
\end{enumerate}

Limitations

As you'll see, this script fails with nested {} and I have no idea how to fix it without some very convoluted code.

3
  • Your regex also fails in a case like \\emph{foo} (which hardly happens) that should not be matched... unless it's \\\emph{foo} unless.... It also fails with \emph {foo} (and maybe other cases I can't think of). Regexes are evil :( Nov 5, 2012 at 12:57
  • @gniourf_gniourf I could fix \emph {foo} pretty easily, but when would \\emph{foo} ever arise?
    – cmhughes
    Nov 5, 2012 at 16:22
  • For \\emph{foo}, I don't know! but it's valid (La)TeX code. Oh, also how about \emph{the set $\{42\}$ is non empty}? Regexes are evil :( Nov 5, 2012 at 17:41
6

Here's a LuaLaTeX solution using pattern matching. It can handle nesting easily (and by deleting a line can ignore nesting if desired). In addition, it can be turned on and off as desired. On the downside, since it uses the process_input_buffer callback, commented \emph's are included as well. I'm not sure how much of an issue this would be for you.

  • \startgetemph turns on emph collection.
  • \stopgetemph turns off emph collection.
  • \printemphs returns a numbered list of emphs.

Output


enter image description here


\documentclass{article}
\usepackage{luacode,luatexbase}
\begin{luacode*}

emphs = {}
getemph = function(head)
    for hit in string.gmatch(head,"\emph(%b{})") do
        emphs[#emphs + 1] = hit
        -- if you don't want nesting, then delete the following line
        getemph(hit)
    end
    return true
end

printemphs = function()
    tex.print("\\begin{enumerate}")
    for i = 1, #emphs do
        tex.print("\\item "..emphs[i])
    end
    tex.print("\\end{enumerate}")
end

\end{luacode*}

\def\startgetemph{\directlua{luatexbase.add_to_callback("process_input_buffer", getemph, "getemph")}}
\def\stopgetemph{\directlua{luatexbase.remove_from_callback("process_input_buffer","getemph")}}
\def\printemphs{\directlua{printemphs()}}

\begin{document}

\startgetemph

\noindent
Here is some text where some of it is \emph{emphasized}.  Other parts will be \emph{emphasized \emph{twice}}.
%\emph{Oops, this is commented emphasized text!}
\stopgetemph
After using \verb=\stopgetemph=, \emph{this final emphasized text} will not be grabbed.

\printemphs
\end{document}
1

In Textwrangler (mac), do a search/replace for \\emph\{[a-z ]+\} Click "find all", you can then copy/paste the results into a new file. Do a new search/replace for all the newlines, inserting \\item. Very low-end.

1

You must log in to answer this question.

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