2

I have a macro for a word count in my overleaf document, its currently in each .tex section of my document, but I want to be able to add the results of them together (i.e. the word count of all sections) I've tried a few ways but can't get any to work, how would this be done? I really don't want to put all my sections in my main document

My macro is below

\newcommand{\quickwordcount}[1]{%
  \immediate\write18{texcount -1 -sum -merge #1.tex > #1-words.sum }%
  \input{#1-words.sum}words%
}
1

You can define an \inputcount command, so you don't need to change the code in the section files.

A final \totalcount command will print a summary of the document based on the computed numbers of words.

\documentclass{article}
\usepackage[a4paper,margin=2cm]{geometry} % to fit just one page
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\inputcount}{m}
 {
  \file_input:n { #1 }
  \aimee_count:n { #1 }
 }

\NewDocumentCommand{\totalcount}{}
 {
  \aimee_count_total:
 }

\tl_new:N \g_aimee_count_tl
\seq_new:N \g_aimee_count_file_seq
\seq_new:N \g_aimee_count_words_seq

\cs_new_protected:Nn \aimee_count:n
 {
  \seq_gput_right:Nx \g_aimee_count_file_seq { \tl_to_str:n { #1 } }
  \sys_shell_get:nnN { texcount~-1~-sum~-merge~#1.tex } {} \l__aimee_count_temp_tl
  \seq_gput_right:NV \g_aimee_count_words_seq \l__aimee_count_temp_tl
  % print the count at the end
  \par\bigskip
  Section~\tl_to_str:n { #1 }~has~\l__aimee_count_temp_tl\ words
 }

\cs_new_protected:Nn \aimee_count_total:
 {
  \tl_gclear:N \g_aimee_count_tl
  \int_step_inline:nn { \seq_count:N \g_aimee_count_file_seq }
   {
    \tl_gput_right:Nx \g_aimee_count_tl
     {
      \seq_item:Nn \g_aimee_count_file_seq { ##1 }
      &
      \seq_item:Nn \g_aimee_count_words_seq { ##1 }
      \exp_not:N \\
     }
   }
   \begin{tabular}{@{}lr@{}}
   Section & \multicolumn{1}{c@{}}{\#} \\
   \hline
   \g_aimee_count_tl
   \hline
   & \int_eval:n { 0+\seq_use:Nn \g_aimee_count_words_seq { + } } \\
   \end{tabular}
 }

\ExplSyntaxOff

\begin{document}

\inputcount{first}
\inputcount{second}
\inputcount{third}

\section*{Total count}

\totalcount

\end{document}

The section files first.tex, second.tex and third.tex just contain the text; in my example, first.tex is

\section{First}

As any dedicated reader can clearly see, the Ideal of
practical reason is a representation of, as far as I know, the things
in themselves; as I have shown elsewhere, the phenomena should only be
used as a canon for our understanding. The paralogisms of practical
reason are what first give rise to the architectonic of practical
reason. As will easily be shown in the next section, reason would
thereby be made to contradict, in view of these considerations, the
Ideal of practical reason, yet the manifold depends on the phenomena.
Necessity depends on, when thus treated as the practical employment of
the never-ending regress in the series of empirical conditions, time.
Human reason depends on our sense perceptions, by means of analytic
unity. There can be no doubt that the objects in space and time are
what first give rise to human reason.

Output

enter image description here

Final table

enter image description here

Caveat

It won't work on Overleaf, sorry. The problem is that Overleaf still runs the outdated TeX Live 2016 release and maybe they'll switch to the likewise outdated TL 2018 in an undetermined “near future”.

  • Thanks to your solution, I learnt about \sys_shell_get:nnN, nice. From reading the documentation of \int_eval:n, it wasn't obvious to me that it would expand “arbitrary” macros like \seq_use:Nn, hence my somewhat convoluted way of computing the sum: I first write the computation to a tl, then use the tl in the argument of \int_eval:n (which does expand integer and token list variables according to interface3.pdf). Is it by design that \seq_use:Nn gets expanded inside the argument of \int_eval:n? – frougon May 5 at 13:21
  • 1
    @frougon Yes,full expansion is performed in order to compute the expression's value. – egreg May 5 at 13:24
1

Welcome to TeX.SE!

My solution assumes your LaTeX code is going to create a file (let's call it word_count.txt) containing one number per line: the number of words for a given .tex file. My code ignores blank lines in word_count.txt, so you can do something like:

\immediate\write18{echo > word_count.txt}
\immediate\write18{texcount -1 -sum -merge file1.tex >> word_count.txt}
\immediate\write18{texcount -1 -sum -merge file2.tex >> word_count.txt}

...

\immediate\write18{texcount -1 -sum -merge lastfile.tex >> word_count.txt}

The point of the first command is to allow all .tex files to be treated the same way, with >> in order to append the per-file word count as a new line of word_count.txt. Otherwise, if you are okay with special-casing the first file, this is quite fine too (note that the first line has > instead of >>, in order to empty the file before writing the first per-file word count):

\immediate\write18{texcount -1 -sum -merge file1.tex > word_count.txt}
\immediate\write18{texcount -1 -sum -merge file2.tex >> word_count.txt}

...

\immediate\write18{texcount -1 -sum -merge lastfile.tex >> word_count.txt}

Of course, every such command may be in its own file if you want. If the commands are run with a POSIX shell (e.g., on Linux), \immediate\write18{: > word_count.txt} can be used instead of \immediate\write18{echo > word_count.txt} (: should be one picosecond faster than echo, or so :-).

Now that you've placed all these commands in your .tex files, compile your document. This has to execute the \immediate\write18 ... for all of your .tex files, whether these commands are all located in a “master file” or scattered among the various .tex files; and the first of these commands must empty word_count.txt, as shown above. You should obtain a word_count.txt file that looks like this:

100
252
12
15

(There will be an initial blank line if you chose the approach with >> for every .tex file and an echo or : command (to empty word_count.txt) before all of them; my code has been designed to cope with this, no problem. It can even cope with a blank-only word_count.txt file.)

So, now that we have a proper word_count.txt file with one per-file word count per line (and possibly blank lines), you can use the following:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\msg_new:nnn { mycountwords } { file-not-found }
  { File~'#1'~not~found. }

\ior_new:N \g_my_stream_ior

% #1: file name containing one word count per line (blank lines are ignored;
% if the file is empty, this counts as 0 words).
\cs_new_protected:Npn \my_count_words:n #1
  {
    \ior_open:NnF \g_my_stream_ior {#1}
      { \msg_error:nnn { mycountwords } { file-not-found } {#1} }

    \tl_clear:N \l_tmpa_tl
    \ior_str_map_inline:Nn \g_my_stream_ior
      {
        \tl_if_blank:nF {##1}   % ignore blank lines
          {
            \tl_if_empty:NF \l_tmpa_tl % prepend a + unless it's the first count
              { \tl_put_right:Nn \l_tmpa_tl { ~+~ } }
            \tl_put_right:Nn \l_tmpa_tl {##1}
          }
      }
    \ior_close:N \g_my_stream_ior

    % Handle the case where the file contained nothing but whitespace
    \tl_if_blank:VT \l_tmpa_tl
      { \tl_set:Nn \l_tmpa_tl { 0 } }

    % Print the word count calculation as an inline mathematical formula
    $ \l_tmpa_tl = \int_eval:n { \l_tmpa_tl } $
  }

\NewDocumentCommand \mycountwords { m }
  {
    \my_count_words:n {#1}
  }

\ExplSyntaxOff

\begin{document}

Number of words: \mycountwords{word_count.txt}.

\end{document}

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.