The following provides a (La)TeX-based approach.
A (La)TeX-based approach might contribute to reducing the amount of external programs needed.
Whether reducing the amount of external programs needed implies efficiency is another question.
Whether the following approach to the matter can be considered efficient in whatsoever way depends highly on your workflow and on the other tools available and in use anyway.
From time to time I work with data base management systems like MariaDB where data can be exported in various ways, e.g., in csv format. Data in csv format can in LaTeX be processed by means of the datatool package.
Sometimes I use PHP scripts for obtaining data from the database server and creating .tex-files containing the (La)TeX source code for the complete document that is about to be created.
There are scenarios where using such external programs/tools seems more efficient to me than tinkering about with all the possible pitfalls and all kinds of more or less intriguing trickery of macro programming and input parsing in (La)TeX.
But there are many scenarios where using things like database management servers and PHP scripting seems to me like cracking nuts with a sledgehammer. ;-)
You can have (La)TeX allocate a new handle for reading input files (let's call it \MyReadHandle) via \newread\MyReadHandle.
You can have (La)TeX make an input file accessible for reading by having (La)TeX associate that input file to a handle for reading input files.
You can haxe (La)TeX associate to \MyReadHandle an input file whose name is MyInputFile.txt via \openin\MyReadHandle=MyInputFile.txt.
(Some TeX plattforms where file systems can have file names with spaces allow nesting the file name in quotes: \openin\MyReadHandle="MyInputFile.txt".)
You can have (La)TeX define the macro \LineJustRead to expand to the content of the next line of the input file MyInputFile.txt via \read\MyReadHandle to\LineJustRead.
You can have (La)TeX check whether the end of the file associated to \MyReadHandle is reached via \ifeof\MyReadHandle..\else..\fi.
You can have (La)TeX disassociate the file currently associated to \MyReadHandle via \closein\MyReadHandle.
Thus you can have (La)TeX iteratively read your Excel-created-file line-wise to \LineJustRead for parsing the expansion of \LineJustRead via some parsing-macro: \expandafter\MyParsingMacro\LineJustRead.
As (La)TeX usually collapses several consecutive spaces from the input into one space token, you can take lines like
2006 Q7
2006 Q24
2007 Q11
2007 Q24
2009 Q17
2009 Q23
for instances of the pattern
<year><space token><question number>
You can, e.g., attach a catcode-12-return and the pattern becomes:
<year><space token><question number>^^M
, so you can parse with a macro
\def\MyParsingMacro#1 #2^^M{%
#1 is the year.
#2 is the question number.
}
\expandafter\MyParsingMacro\LineJustRead^^M
Seems in the scenario provided by you in your question, you won't need routines for removing leading/trailing space tokens from your argument(s).
With other similar scenarios you may need them some day.
Therefore I pasted some routines for space removal with this example although in this specific scenario they are not needed.
In case you don't want them,
just remove the entire section
EXPANDABLE REMOVAL OF LEADING AND TRAILING SPACES.
within the section
PARSING FILES WITH LISTS CREATED BY THE EXCEL FILTER
remove the two lines with calls to \UD@TrimAllSurroundSpace
and uncomment the lines below them.
\documentclass{article}
\usepackage[draft]{graphicx}
% verbatim-package is used only for \verbatiminput
% of newly created files.
\usepackage{verbatim}
\makeatletter
% patch \verbatim@processline to also show line-breaks and thus
% also visualize empty lines:
\def\verbatim@processline{%
\the\verbatim@line
{\normalfont\textit{$\mathit{\langle}$linebreak$\mathit{\rangle}$}}%
\par
}
\makeatother
\makeatletter
%%//////////////////////////////////////////////////////////////////////////////
%% SECTION LICENCE AND COPYRIGHT
%%
%% Copyright (C) 2007-2018 by Ulrich W. Diez (ud.usenetcorrespondence@web.de)
%%..............................................................................
%% This work may be distributed and/or modified under the conditions of the
%% LaTeX Project Public Licence (LPPL), either version 1.3 of this license or
%% (at your option) any later version.
%% (The latest version of this license is in:
%% http://www.latex-project.org/lppl.txt
%% and version 1.3 or later is part of all distributions of
%% LaTeX version 1999/12/01 or later.)
%% The author of this work is Ulrich Diez.
%% This work has the LPPL maintenance status ‘not maintained’.
%% Usage of any/every component of this work is at your own risk.
%% There is no warranty — neither for probably included documentation nor for
%% any other part/component of this work.
%% If something breaks, you usually may keep the pieces.
%%
%% EOF SECTION LICENCE AND COPYRIGHT
%%//////////////////////////////////////////////////////////////////////////////
%%
%%
%%//////////////////////////////////////////////////////////////////////////////
%% SECTION PARAPHERNALIA:
%%
%% \UD@firstoftwo, \UD@secondoftwo, \UD@Exchange, \UD@CheckWhetherNull,
%% \UD@CheckWhetherBlank
%%==============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@Exchange[2]{#2#1}%
%%------------------------------------------------------------------------------
%% Check whether argument is empty:
%%..............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
%%
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
\UD@secondoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@firstoftwo}%
}%
%%------------------------------------------------------------------------------
%% Check whether argument is blank (empty or only spaces):
%%..............................................................................
%% -- Take advantage of the fact that TeX discards space tokens when
%% "fetching" _un_delimited arguments: --
%% \UD@CheckWhetherBlank{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that
%% argument which is to be checked is blank>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not blank}%
\newcommand\UD@CheckWhetherBlank[1]{%
\romannumeral\expandafter\expandafter\expandafter\UD@secondoftwo
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo#1{}.}%
}%
%%
%% EOF SECTION PARAPHERNALIA
%%//////////////////////////////////////////////////////////////////////////////
%%
%%
%%//////////////////////////////////////////////////////////////////////////////
%% SECTION EXPANDABLE REMOVAL OF LEADING AND TRAILING SPACES.
%%
%% - REQUIRES SECTION PARAPHERNALIA.
%%
%% The obscure case of removing several leading/trailing spaces was taken
%% into consideration.
%%
%% Removal of spaces was implemented in a way where no brace-stripping from
%% the arguments takes place.
%% Explicit-catcode-1/2-character-token-pairs remain untouched.
%%
%% Spaces interspersing the argument or hidden within braces will be left in
%% place.
%%
%% The arguments themselves do not get expanded.
%%
%% (For some obscure reason I don't remember any more I needed this in the
%% past.)
%%
%%==============================================================================
%% Check whether brace-balanced argument starts with a space-token
%%..............................................................................
%% \UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is a
%% space-token>}%
%% {<Tokens to be delivered in case <argument
%% which is to be checked>'s 1st token is not
%% a space-token>}%
\newcommand\UD@CheckWhetherLeadingSpace[1]{%
\romannumeral0\UD@CheckWhetherNull{#1}%
{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
{\expandafter\UD@secondoftwo\string{\UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
\newcommand\UD@CheckWhetherLeadingSpaceB{}%
\long\def\UD@CheckWhetherLeadingSpaceB#1 {%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@secondoftwo#1{}}%
{\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
{\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
\expandafter\expandafter\expandafter}\expandafter\expandafter
\expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%==============================================================================
%% \UD@TrimAllLeadSpace{<action>}{<argument>}
%%..............................................................................
%% expandably removes all leading spaces from <argument> in case at least
%% one leading space is present.
%% Then
%% <action>{<argument without leading spaces>}
%% is performed.
%%==============================================================================
\newcommand\UD@TrimAllLeadSpace[2]{%
\romannumeral0\UD@TrimAllLeadSpaceLoop{#2}{#1}%
}%
\newcommand\UD@TrimAllLeadSpaceLoop[2]{%
\UD@CheckWhetherLeadingSpace{#1}%
{%
\expandafter\UD@TrimAllLeadSpaceLoop
\expandafter{\UD@removespace#1}{#2}%
}%
{ #2{#1}}%
}%
\newcommand\UD@removespace{}\UD@firstoftwo{\def\UD@removespace}{} {}%
%%==============================================================================
%% \UD@TrimAllTrailSpace{<action>}{<argument>}
%%..............................................................................
%% expandably removes all trailing spaces from <argument> in case at least
%% one trailing space is present.
%% Then
%% <action>{<argument without trailing spaces>}
%% is performed.
%%==============================================================================
\newcommand\UD@TrimAllTrailSpace[2]{%
\romannumeral0\UD@TrimTrailSpaceLoop{#2}{#1}%
}%
%%------------------------------------------------------------------------------
%% \UD@TrimTrailSpaceLoop{<list of space-delimited arguments>}{<action>}
%%..............................................................................
%% both extracts the first space-delimited argument from the <list of space-
%% delimited arguments> as {<current argument with one trailing space
%% removed>} and removes it from the <list of space-delimited arguments> for
%% obtaining the <remaining list of space delimited arguments> and passes
%% these two things and an empty list of <arguments preceding the current
%% argument gathered so far> and the <action> to perform at the end of the
%% iteration to \UD@CheckWhetherLastSpaceDelimitedItem.
%%
%% \UD@CheckWhetherLastSpaceDelimitedItem in turn does choose the next
%% action.
\newcommand\UD@TrimTrailSpaceLoop[2]{%
\UD@ObtainFirstSpaceDelimitedTokenSetLoop{.#1 \UD@SeLDom}{%
\expandafter\UD@CheckWhetherLastSpaceDelimitedItem
\expandafter{\UD@RemoveTokensTillNextSpace.#1 }%
}{}{#2}%
}%
%%------------------------------------------------------------------------------
%% Macros for \UD@ObtainFirstSpaceDelimitedTokenSetLoop.
%%
\newcommand*\UD@RemoveTokensTillNextSpace{}%
\long\def\UD@RemoveTokensTillNextSpace#1 {}%
\newcommand*\UD@BraceStripRemoveNextSpace{}%
\long\def\UD@BraceStripRemoveNextSpace#1 {#1}%
\newcommand*\UD@GetFirstSpaceDelimitedTokenSet{}%
\long\def\UD@GetFirstSpaceDelimitedTokenSet#1 #2\UD@SeLDom{#1 }%
\newcommand\UD@gobbledot{}%
\def\UD@gobbledot.{}%
%%------------------------------------------------------------------------------
%% \UD@ObtainFirstSpaceDelimitedTokenSetLoop%
%% {<list of space delimited arguments>}%
%% {<action>}%
%%
%% -> <action>{<first element of list of space delimited arguments>}%
%%...............................................................................
%% \UD@ObtainFirstSpaceDelimitedTokenSetLoop does--without unwanted brace-re-
%% moval--append the first space delimited argument from a
%% <list of space delimited arguments> as brace-delimited argument behind
%% a set of tokens given as <action>.
\newcommand\UD@ObtainFirstSpaceDelimitedTokenSetLoop[1]{%
\expandafter\UD@CheckWhetherNull
\expandafter{\UD@RemoveTokensTillNextSpace#1}{%
\expandafter\expandafter\expandafter\UD@Exchange
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\expandafter{%
\expandafter\UD@gobbledot\UD@BraceStripRemoveNextSpace#1}}%
}{%
\expandafter\UD@ObtainFirstSpaceDelimitedTokenSetLoop
\expandafter{\UD@GetFirstSpaceDelimitedTokenSet#1}%
}%
}%
%%------------------------------------------------------------------------------
%% \UD@CheckWhetherLastSpaceDelimitedItem
%% {<remaining list of space delimited arguments>}%
%% {<current argument with one trailing space removed>}%
%% {<arguments preceding the current argument gathered
%% so far>}%
%% {<action>}%
%%..............................................................................
%% Case 1: <remaining list of space delimited arguments> is
%% empty.
%% We are done: Thus:
%% <space>% for terminating \romannumeral-expansion, and
%% <action>{<arguments preceding the current argument gathered so
%% far><current argument with one trailing space removed>}
%% Case 2: <remaining list of space delimited arguments> consists of a single
%% space.
%% A trailing space was removed. There may be more. Thus:
%% \UD@TrimTrailSpaceLoop{%
%% <arguments preceding the current argument gathered so
%% far><current argument with one trailing space removed>%
%% }{<action>}%
%% Neither case 1 nor case 2:
%% The <current argument with one trailing space removed> is not the
%% last argument of the list, thus:
%% For the next iteration
%% - attach it and a trailing space to the <arguments preceding the
%% current argument gathered so far>,
%% - get the first space delimited argument of the <remaining list of
%% space delimited arguments> as <current argument with one trailing
%% space removed>
%% - remove that first space delimited argument from the <remaining list
%% of space delimited arguments>
\newcommand\UD@CheckWhetherLastSpaceDelimitedItem[4]{%
\UD@CheckWhetherNull{#1}{ #4{#3#2}}{%
\UD@CheckWhetherLeadingSpace{#1}{%
\expandafter\UD@CheckWhetherNull
\expandafter{\UD@removespace#1}{\UD@firstoftwo}{\UD@secondoftwo}%
}{\UD@secondoftwo}%
{\UD@TrimTrailSpaceLoop{#3#2}{#4}}%
{%
\UD@ObtainFirstSpaceDelimitedTokenSetLoop{.#1\UD@SeLDom}{%
\expandafter\UD@CheckWhetherLastSpaceDelimitedItem
\expandafter{\UD@RemoveTokensTillNextSpace.#1}%
}{#3#2 }{#4}%
}%
}%
}%
%%==============================================================================
%% \UD@TrimAllSurroundSpace{<action>}{<argument>}
%%..............................................................................
%% expandably removes all leading and trailing spaces from <argument> in
%% case at least one leading space is present.
%% Then
%% <action>{<argument without leading and trailing spaces>}
%% is performed.
%%==============================================================================
\newcommand\UD@TrimAllSurroundSpace[2]{%
\romannumeral\UD@TrimAllLeadSpace{\UD@TrimAllTrailSpace{0 #1}}{#2}%
}%
%%
%% EOF SECTION EXPANDABLE REMOVAL OF LEADING AND TRAILING SPACES.
%%//////////////////////////////////////////////////////////////////////////////
%%
%%
%%//////////////////////////////////////////////////////////////////////////////
%% SECTION PARSING FILES WITH LISTS CREATED BY THE EXCEL FILTER
%%
%% - REQUIRES MACRO \UD@TrimAllSurroundSpace FROM
%% SECTION EXPANDABLE REMOVAL OF LEADING AND TRAILING SPACES.
%%
%% - REQUIRES SECTION PARAPHERNALIA.
%% (Even when removing the calls to \UD@TrimAllSurroundSpace.)
%%
%%==============================================================================
%% \CreateSectioningFileFromExcelList{%
%% <phrase before sectioning titles>
%% }{%
%% <phrase behind sectioning titles>
%% }{%
%% <phrase before the name of the pdf-file>
%% }{%
%% <phrase bhind the name of the pdf-file>
%% }{%
%% <name of file with sectionings that is to be created>
%% }{%
%% <name of existing file that comes from the excel-filter>
%% }%
%%..............................................................................
%%
\newcommand\UD@LineFromFile{}
\newcommand\UD@YearInPreviousLine{}
\newcommand\UD@YearInThisLine{}
\newcommand\UD@ParseLine{}%
\newwrite\UD@SectioningFile
\newread\UD@TextFileByExcelFilter
\newif\ifUD@dontstopreading
%%
\begingroup
\endlinechar=-1\relax
\catcode`\^^M=12\relax
\UD@secondoftwo{}{%
\endgroup
\newcommand\CreateSectioningFileFromExcelList[6]{%
\begingroup
\renewcommand\UD@LineFromFile{}%
\renewcommand\UD@YearInPreviousLine{}%
\UD@dontstopreadingtrue
\let\do=\@makeother
\dospecials
\catcode`\ =10 %
\catcode`\^^M=10 %
\openin\UD@TextFileByExcelFilter=#6 %
\ifeof\UD@TextFileByExcelFilter
\expandafter\UD@firstoftwo
\else
\expandafter\UD@secondoftwo
\fi
{%
\@latex@warning@no@line{No file #6.}%
}{%
\IfFileExists{#5}{%
\@latex@warning@no@line{%
File `#5' already exists on the system.\MessageBreak
Not generating it from this source%
}%
}{%
\immediate\openout\UD@SectioningFile=#5 %
\loop
\ifeof\UD@TextFileByExcelFilter\UD@dontstopreadingfalse\fi
\ifUD@dontstopreading
\read\UD@TextFileByExcelFilter to\UD@LineFromFile
\expandafter\UD@CheckWhetherBlank\expandafter{\UD@LineFromFile}{}{%
\expandafter\UD@Exchange
\expandafter{\UD@LineFromFile^^M}{\UD@ParseLine{#1}{#2}{#3}{#4}}%
}%
\repeat
\immediate\closeout\UD@SectioningFile
}%
}%
\closein\UD@TextFileByExcelFilter
\endgroup
}%
%%
%%
\long\def\UD@ParseLine#1#2#3#4#5 #6^^M{%
\UD@TrimAllSurroundSpace{\def\UD@YearInThisLine}%
%\def\UD@YearInThisLine
{#5}%
\ifx\UD@YearInThisLine\UD@YearInPreviousLine
\expandafter\UD@firstoftwo
\else
\expandafter\UD@secondoftwo
\fi
{}{%
\immediate\write\UD@SectioningFile{%
#1\UD@YearInThisLine#2%
}%
}%
\immediate\write\UD@SectioningFile{%
#3%
\UD@YearInThisLine
\UD@CheckWhetherNull{#6}{}{%
\string_%
\UD@TrimAllSurroundSpace{\UD@secondoftwo{}}{#6}%
%#6%
}%
#4%
}%
\let\UD@YearInPreviousLine=\UD@YearInThisLine
}%
}%
\relax
%%
%% EOF SECTION PARSING FILES WITH LISTS CREATED BY THE EXCEL FILTER
%%//////////////////////////////////////////////////////////////////////////////
\makeatother
\edef\endgroupchar{\string}}%
\edef\begingroupchar{\string{}%
\begin{filecontents*}{FromExcel.txt}
2006
2006 Q7
2006 Q24
2007
2007 Q11
2007 Q24
2009 Q17
2009 Q23
\end{filecontents*}
\begin{document}
This is the content of FromExcel.txt
\verbatiminput*{FromExcel.txt}
\CreateSectioningFileFromExcelList{^^J\string\subsection\string*\begingroupchar}%
{\endgroupchar}%
{\string\includegraphics[scale=0.9]\begingroupchar}%
{\endgroupchar\space\string\\[2ex]}%
{MyPictureList.tex}%
{FromExcel.txt}
This is the content of MyPictureList.tex
\verbatiminput*{MyPictureList.tex}
%\input{MyPictureList.tex}
\end{document}

listingsis wrong here as thelistingspackage is not related to this question. Note thedatatoolpackage can read CSV files, and loop over them, so that can probably be useful for some of this. Other than that, it is sometimes easier to use a different language to generate the latex code, for examplepython– daleif Aug 07 '18 at 13:23\\[2ex]after the last entry in each paragraph, otherwise tex will complain about underfull box badness 10000 and make very poor output – David Carlisle Aug 07 '18 at 13:23datatool. – Chen Stats Yu Aug 07 '18 at 13:25copy-and-pasteof two columns after applying the filter from Excel 2016. – Chen Stats Yu Aug 07 '18 at 13:37