\includegraphics: signo numérico # en el nombre del archivo

\includegraphics: signo numérico # en el nombre del archivo

Tengo cientos de figuras con nombres de archivos que contienen "#" para incluir usando \includegraphics, he probado paquetes como grffileo currfile, pero ninguno funciona. ¿Alguien puede sugerir una solución real para no tener que cambiar cientos de nombres de archivos? Gracias.

Actualización: 1. Estoy usando Windows. 2. Cuando lo compilo, muestra "Número de parámetro ilegal en la definición"

Respuesta1

Puedes cambiar el catcode #antes de usarlo:

\documentclass[]{article}
\usepackage{graphicx}


\begin{document}
{\catcode`\#=12 \includegraphics{test#abc}}
\end{document}

Pero realmente es mejor evitar esos nombres.

Respuesta2

Una alternativa a los cambios de catcode, que pueden resultar complicados dentro de las macros:

\documentclass{article}
\usepackage{graphicx}

\begin{document}

\includegraphics{a\string##b.png}

\end{document}

Respuesta3

Se puede cambiar el código de categoría, consulte larespuestade Ulrike Fischer. O el hash se puede ocultar dentro de una macro:

\edef\hash{\string#}

\hashse define como una macro que se expande a un hash con un código de categoría (12/otro) de un carácter como dígitos.

Ejemplo:

\documentclass{article}
\usepackage{graphicx}
% \usepackage{grffile}

\newcommand*{\hash}{}% print error message if \hash is already defined
\edef\hash{\string#}

\begin{document}
  \includegraphics[width=.5\linewidth]{test\hash abc}% "test#abc"
\end{document}

Respuesta4

El problema se debe al procesamiento/análisis del nombre de archivo del paquete gráficos/graphicx:

El nombre de archivo proporcionado como argumento \includegraphicsse pasa a los textos de definición de macros temporales.

Cuando estas macros temporales se expandan, la cantidad de hashes consecutivos se reducirá a la mitad y se tomarán hashes únicos para los marcadores que deben preceder a los dígitos en el rango 1..9 para indicar argumentos.

Probablemente esto podría haberse resuelto mediante \toks@{..}...\edef..{..\the\toks@..}o \edef...{\unexpanded{..}..}.

En caso de que la \detokenizeprimitiva de las extensiones eTeX esté disponible, se puede implementar la verificación de si un token es un token de carácter explícito del código de categoría 6 (parámetro).

Por lo tanto, en caso de que la \detokenizeprimitiva de las extensiones eTeX esté disponible, se puede implementar una rutina para reemplazar recursivamente cada hash (cada token de carácter explícito del código de categoría 6) en el nombre del archivo por su cadena/por su colgante de código de categoría 12. :

% This example uses \detokenize and thus requires eTeX extensions.

\documentclass{article}
\usepackage{graphicx}

\makeatletter
%%=============================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo,
%%    \UD@PassFirstToSecond, \UD@Exchange, \UD@removespace
%%    \UD@CheckWhetherNull, \UD@CheckWhetherBrace,
%%    \UD@CheckWhetherLeadingSpace, \UD@ExtractFirstArg
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\newcommand\UD@removespace{}\UD@firstoftwo{\def\UD@removespace}{} {}%
%%-----------------------------------------------------------------------------
%% 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's first token is a catcode-1-character
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has leading
%%                        catcode-1-token>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has no leading
%%                        catcode-1-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
  \romannumeral0\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\expandafter\UD@firstoftwo{ }{}%
  \UD@firstoftwo}{\expandafter\expandafter\UD@firstoftwo{ }{}\UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% 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}%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%%   \UD@ExtractFirstArg{ABCDE} yields  {A}
%%
%%   \UD@ExtractFirstArg{{AB}CDE} yields  {AB}
%%.............................................................................
\newcommand\UD@RemoveTillUD@SelDOm{}%
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\newcommand\UD@ExtractFirstArg[1]{%
  \romannumeral0%
  \UD@ExtractFirstArgLoop{#1\UD@SelDOm}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  { #1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%=============================================================================
%% \ReplaceEveryHash{<argument>}%
%%
%%   Each explicit catcode-6(parameter)-character-token of the <argument> 
%%   will be replaced by its stringification.
%%
%%   You obtain the result after two expansion-steps, i.e., 
%%   in expansion-contexts you get the result after "hitting" 
%%   \ReplaceEveryHash by two \expandafter.
%%   
%%   As a side-effect, the routine does replace matching pairs of explicit
%%   character tokens of catcode 1 and 2 by matching pairs of curly braces
%%   of catcode 1 and 2.
%%   I suppose this won't be a problem in most situations as usually the
%%   curly braces are the only characters of category code 1 / 2...
%%
%%   This routine needs \detokenize from the eTeX extensions.
%%-----------------------------------------------------------------------------
\newcommand\ReplaceEveryHash[1]{%
  \romannumeral0\UD@ReplaceEveryHashLoop{#1}{}%
}%
\newcommand\UD@ReplaceEveryHashLoop[2]{%
  \UD@CheckWhetherNull{#1}{ #2}{%
    \UD@CheckWhetherLeadingSpace{#1}{%
       \expandafter\UD@ReplaceEveryHashLoop
       \expandafter{\UD@removespace#1}{#2 }%
    }{%
      \UD@CheckWhetherBrace{#1}{%
        \expandafter\expandafter\expandafter\UD@PassFirstToSecond
        \expandafter\expandafter\expandafter{%
        \expandafter\UD@PassFirstToSecond\expandafter{%
            \romannumeral0\expandafter\UD@ReplaceEveryHashLoop
            \romannumeral0\UD@ExtractFirstArgLoop{#1\UD@SelDOm}{}%
        }{#2}}%
        {\expandafter\UD@ReplaceEveryHashLoop
         \expandafter{\UD@firstoftwo{}#1}}%
      }{%
       \expandafter\UD@CheckWhetherHash
       \romannumeral0\UD@ExtractFirstArgLoop{#1\UD@SelDOm}{#1}{#2}%
      }%
    }%
  }%
}%
\newcommand\UD@CheckWhetherHash[3]{%
  \expandafter\UD@CheckWhetherLeadingSpace\expandafter{\string#1}{%
    \expandafter\expandafter\expandafter\UD@CheckWhetherNull
    \expandafter\expandafter\expandafter{%
    \expandafter\UD@removespace\string#1}{%
      \expandafter\expandafter\expandafter\UD@CheckWhetherNull
      \expandafter\expandafter\expandafter{%
      \expandafter\UD@removespace\detokenize{#1}}{% Something whose stringification yields a single space
        \expandafter\UD@ReplaceEveryHashLoop
        \expandafter{\UD@firstoftwo{}#2}{#3#1}%
      }{% Explicit space of catcode 6
        \expandafter\expandafter\expandafter\UD@PassFirstToSecond
        \expandafter\expandafter\expandafter{\expandafter\UD@Exchange
        \expandafter{\string#1}{#3}}{%
          \expandafter\UD@ReplaceEveryHashLoop
          \expandafter{\UD@firstoftwo{}#2}%
        }%
      }%
    }{% Something whose stringification has a leading space
      \expandafter\UD@ReplaceEveryHashLoop
      \expandafter{\UD@firstoftwo{}#2}{#3#1}%
    }%
  }{%
    \expandafter\expandafter\expandafter\UD@CheckWhetherNull
    \expandafter\expandafter\expandafter{%
    \expandafter\UD@firstoftwo
    \expandafter{\expandafter}\string#1}{%
      \expandafter\expandafter\expandafter\UD@CheckWhetherNull
      \expandafter\expandafter\expandafter{%
      \expandafter\UD@firstoftwo
      \expandafter{\expandafter}\detokenize{#1}}{% No Hash
        \expandafter\UD@ReplaceEveryHashLoop
        \expandafter{\UD@firstoftwo{}#2}{#3#1}%
      }{% Hash
        \expandafter\expandafter\expandafter\UD@PassFirstToSecond
        \expandafter\expandafter\expandafter{\expandafter\UD@Exchange
        \expandafter{\string#1}{#3}}{%
          \expandafter\UD@ReplaceEveryHashLoop
          \expandafter{\UD@firstoftwo{}#2}%
        }%
      }%
    }{% No Hash
      \expandafter\UD@ReplaceEveryHashLoop
      \expandafter{\UD@firstoftwo{}#2}{#3#1}%
    }%
  }%
}%
%----------------------------------------------------------------------
\makeatother

\begin{document}

\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\mytempa
\expandafter\expandafter\expandafter{\ReplaceEveryHash{F#m.png}}

\includegraphics[width=5cm]{\mytempa}

\end{document}

Pero simplemente reemplazar los hashes no es la mejor solución. Motivo: además de los hashes, un nombre de archivo también podría contener llaves desequilibradas y similares. Por lo tanto, podría ser útil una rutina que lea el archivo de entrada .tex y tokenice los macroargumentos bajo el régimen de código de categoría palabra por palabra.

Puedo ofrecer una macro \UDcollectverbargcon la siguiente sintaxis:

\UDcollectverbarg{⟨^^M-replacement⟩}{⟨mandatory 1⟩}{⟨mandatory 2⟩}⟨verbatimized argument⟩

cuyos rendimientos:

⟨mandatory 1⟩{⟨mandatory 2⟩{⟨verbatim argument⟩}}

, y cada carácter ^^Mque denota el final de una línea se reemplaza por la secuencia de token ⟨^^M-replacement⟩.

Los argumentos ⟨mandatory 1⟩y ⟨mandatory 2⟩son obligatorios. Si constan de varios tokens, estos tokens deben anidarse en un par de tokens de caracteres explícitos catcode-1/2 / entre llaves.
Si es necesaria la lectura y tokenización, esto se llevará a cabo bajo el régimen de código de categoría sin cambios. También
es ⟨verbatim argument⟩obligatorio. Debe leerse y tokenizarse según el régimen de códigos de categorías textuales. Si su primer carácter es una llave, se "asumirá" que el argumento está anidado entre llaves. De lo contrario, se asumirá que el final de ese argumento está delimitado por ese primer carácter, como el argumento de \verb.
Las líneas vacías no serán ignoradas.

Elegí esta sintaxis porque con esta sintaxis puedes recopilar argumentos palabra por palabra ⟨mandatory 2⟩anidando llamadas a \UDcollectverbarginside ⟨mandatory 1⟩.

P.ej,

\UDcollectverbarg{<^^M-replacement>}%
                 {\UDcollectverbarg{<^^M-replacement>}{\UDcollectverbarg{<^^M-replacement>}{<actionA>}}}% <- mandatory 1
                 {<actionB>}%                     <- mandatory 2
                 <verbatim argument 1><verbatim argument 2><verbatim argument 3>

rendimientos:

\UDcollectverbarg{<^^M-replacement>}{\UDcollectverbarg{<^^M-replacement>}{<actionA>}}% <- mandatory 1
                 {<actionB>{<verbatimd argument 1>}}%        <- mandatory 2
                 <verbatim argument 2><verbatim argument 3>

rendimientos:

\UDcollectverbarg{<^^M-replacement>}{<actionA>}% <- mandatory 1
                 {<actionB>{<verbatim argument 1>}{<verbatim argument 2>}}% <- mandatory 2
                 <verbatim argument 3>

rendimientos:

<actionA>{<actionB>{<verbatim argument 1>}{<verbatim argument 2>}{<verbatim argument 3>}}

Supongamos <actionA>= \@firstofone:

\@firstofone{<actionB>{<verbatim argument 1>}{<verbatim argument 2>}{<verbatim argument 3>}}

rendimientos:

<actionB>{<verbatim argument 1>}{<verbatim argument 2>}{<verbatim argument 3>}

% This example does not require eTeX extensions.

\errorcontextlines=10000

\makeatletter
%%<-------------------- Code for \UDcollectverbarg -------------------->
%% Copyright (C) 2007 - 2019 by Ulrich Diez ([email protected])
%%
%% 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.
%
\newcommand\UD@firstofone[1]{#1}%
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
%%
%% 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}%
}%
%%......................................................................
\begingroup
\catcode`\^^M=12 %
\UD@firstofone{%
  \endgroup%
  \newcommand\UDEndlreplace[2]{\romannumeral0\@UDEndlreplace{#2}#1^^M\relax{}}%
  \newcommand*\@UDEndlreplace{}%
  \long\def\@UDEndlreplace#1#2^^M#3\relax#4#5{%
    \UD@CheckWhetherNull{#3}%
    { #5{#4#2}}{\@UDEndlreplace{#1}#3\relax{#4#2#1}{#5}}%
  }%
}%
\newcommand\UDcollectverbarg[3]{%
  \begingroup
  \let\do\@makeother % <- this and the next line switch to
  \dospecials        %    verbatim-category-code-régime.
  \catcode`\{=1      % <- give opening curly brace the usual catcode so a 
                     %    curly-brace-balanced argument can be gathered in
                     %    case of the first thing of the verbatimized-argument 
                     %    being a curly opening brace.
  \catcode`\ =10     % <- give space and horizontal tab the usual catcode so \UD@collectverbarg
  \catcode`\^^I=10   %    cannot catch a space or a horizontal tab as its 4th undelimited argument.
                     %    (Its 4th undelimited argument denotes the verbatim-
                     %     syntax-delimiter in case of not gathering a
                     %     curly-brace-nested argument.)
  \kernel@ifnextchar\bgroup
  {% seems a curly-brace-nested argument is to be caught:
    \catcode`\}=2    % <- give closing curly brace the usual catcode also.
    \UD@collectverbarg{#1}{#2}{#3}{}%
  }{% seems an argument with verbatim-syntax-delimiter is to be caught:
    \do\{% <- give opening curly brace the verbatim-catcode again.
    \UD@collectverbarg{#1}{#2}{#3}%
  }%
}%
\newcommand\UD@collectverbarg[4]{%
  \do\ %   <- Now that \UD@collectverbarg has the delimiter or
  \do\^^I%    emptiness in its 4th arg, give space and horizontal tab
         %    the verbatim-catcode again.
  \do\^^M% <- Give the carriage-return-character the verbatim-catcode.
  \long\def\@tempb##1#4{%
    %\edef\@tempb{##1}%
    \def\@tempb{##1}%
    \@onelevel@sanitize\@tempb % <- Turn characters into their "12/other"-pendants.
                               %    This may be important with things like the 
                               %    inputenc-package which may make characters 
                               %    active/which give them catcode 13(active).
    \expandafter\UDEndlreplace\expandafter{\@tempb}{#1}{\def\@tempb}% <- this starts 
                               %    the loop for replacing endline-characters.
    \expandafter\UD@@collectverbarg\expandafter{\@tempb}{#2}{#3}% <- this "spits 
                               %    out the result.
  }%
  \@tempb
}%
\newcommand\UD@@collectverbarg[3]{%
  \endgroup
  #2{#3{#1}}%
}%
%%<---------------- End of code for \UDcollectverbarg ----------------->
\makeatother

\documentclass{article}
\usepackage{graphicx}

\begin{document}

\begingroup\catcode`\^^M=12\relax%
\UDcollectverbarg{^^M}{\endgroup\csname @firstofone\endcsname}{\def\mytempa}|F#m.png|%

\includegraphics[width=5cm]{\mytempa}

\end{document}

información relacionada