0

In my library, I need to be able to append some text to a file. The problem is that that file may contain the # symbol, and it turns out that every time I append something to a file containing a #, each # is turned into two sharps: ##, so my library is lost since \# is really different from \## (issue here).

Is there a way to append something to a file containing #? For now I'm using this command:

\NewDocumentCommand\appendtofile{m+m}{%
  \begingroup
  \IfFileExists{#1}%
  {\CatchFileDef{\filecontent}{#1}{\catcode`\\=12 \endlinechar=`^^J\catcode\endlinechar=12\relax}}% keep existing end-of-lines
  {\let\filecontent\empty}%
  \immediate\openout\appendwrite=#1\relax
  \immediate\write\appendwrite{\detokenize\expandafter{\filecontent}#2}%
  \immediate\closeout\appendwrite
  \endgroup
}

MWE

\documentclass{article}
\usepackage{catchfile} % To append to a file.
\usepackage{etoolbox}

\makeatletter \newwrite\appendwrite

%%% This command allows the user to append things to a file. But the content may contain macros that %%% will be evaluated before writing them to the file. % The first argument is the file name % The second argument is the text to write % https://tex.stackexchange.com/questions/11796/how-can-i-open-a-file-in-append-mode \NewDocumentCommand\appendtofile{m+m}{% \begingroup \IfFileExists{#1}% {\CatchFileDef{\filecontent}{#1}{\catcode\\=12 \endlinechar=^^J\catcode\endlinechar=12\relax}}% keep existing end-of-lines {\let\filecontent\empty}% \immediate\openout\appendwrite=#1\relax \immediate\write\appendwrite{\detokenize\expandafter{\filecontent}#2}% \immediate\closeout\appendwrite \endgroup }

%%% Add some rules and title before and after the text. %% The first argument is the name of the file, %% The second argument is the text to add. \NewDocumentCommand\appendEnclosed{m+m}{ \appendtofile{#1}{% This part may contain macros, potentially expanded like \myTitle: \string\par\string\noindent\string\rule{\string\linewidth}{2mm}\par% \string\begin{center}\myTitle\string\end{center} \string\par\string\noindent\string\rule{\string\linewidth}{2mm}\par% \detokenize{#2}%%<-- this part should be printed as closely as possible to the original text, including # and if possible new lines. \string\par\string\noindent\string\rule{\string\linewidth}{.5mm}\par% } }

%% Erase the file at the beginning to avoid accumulating things from previous runs. \immediate\openout\appendwrite=mynewfile.tex% \immediate\write\appendwrite{}% \immediate\closeout\appendwrite%

\makeatother

\begin{document} \def\myTitle{myTitle}

\appendEnclosed{mynewfile}{ Hello

I would love to preserve lines, but if it's not possible I can live without if latex at least adds par. I would like to be able to add macros like \textbf{a bold one}. }

\appendEnclosed{mynewfile}{ But what I would love even more is the ability to add sharp like #. This work if I write a single sharp, but every time I append something to the file, each sharp is replaced with two sharps. Try to uncomment the next lines to see by yourself. } % \appendEnclosed{mynewfile}{ % The number of sharp depends on the number of time we happened something to the file. I guess when the file is read, each sharp is turned into two sharps. % } % \appendEnclosed{mynewfile}{ % The number of sharp depends on the number of time we happened something to the file. I guess when the file is read, each sharp is turned into two sharps. % } % \appendEnclosed{mynewfile}{ % The number of sharp depends on the number of time we happened something to the file. I guess when the file is read, each sharp is turned into two sharps. % }

\section{Content of the file} \def\myTitle{} % Just to make sure that myTitle was properly expanded when writing to the file. \def\mymacro{MyMacro} \input{mynewfile}

\end{document}

EDIT I just realized that the answer propose here https://tex.stackexchange.com/a/13289/116348 does not have this same issue. I managed to adapt it to my usecase. Please, let me know if I'm missing some subtle edgecases.

\documentclass{article}
\usepackage{catchfile} % To append to a file.
\usepackage{etoolbox}

\makeatletter

%% https://tex.stackexchange.com/a/13289/116348 \newwrite\appendwrite \newcount\pratend@count@makeallother

%%% Loop to make sure all chars are normal letters, including #. \newcommand{\makeallother}{% \pratend@count@makeallother0\relax \loop\ifnum\pratend@count@makeallother<255\relax \catcode\pratend@count@makeallother12\relax \advance\pratend@count@makeallother by 1\relax \repeat }

%%% This command allows the user to append things to a file. But the content may contain macros that %%% will be evaluated before writing them to the file. %%% TODO: Efficiency may be improved by writing to the file only before the printProof. % The first argument is the file name % The second argument is the text to write \NewDocumentCommand\appendtofile{m+m}{% \begingroup %% Read the content \begingroup% \IfFileExists{#1}{% \newlinechar\endlinechar% \makeallother% Turn all chars into normal letters. \everyeof{\noexpand}% \edef\fileContent{@@input #1 }% }{% \let\fileContent\empty }% %% Open the file to write in it: \immediate\openout\appendwrite #1\relax% %% Write the text \immediate\write\appendwrite{\fileContent}% \endgroup% \immediate\write\appendwrite{#2}% %% Close the file \immediate\closeout\appendwrite% \endgroup }

\NewDocumentCommand\appendEnclosed{m+m}{ \appendtofile{#1}{% This part may contain macros, potentially expanded like \myTitle: \string\par\string\noindent\string\rule{\string\linewidth}{2mm}\par% \string\begin{center}\myTitle\string\end{center} \string\par\string\noindent\string\rule{\string\linewidth}{2mm}\par% \detokenize{#2}%%<-- this part should be printed as closely as possible to the original text, including # and if possible new lines. \string\par\string\noindent\string\rule{\string\linewidth}{.5mm}\par% } }

%% Erase the file at the beginning to avoid accumulating things from previous runs. \immediate\openout\appendwrite=mynewfile.tex% \immediate\write\appendwrite{}% \immediate\closeout\appendwrite%

\makeatother

\begin{document} \def\myTitle{myTitle}

\appendEnclosed{mynewfile.tex}{ Hello

I would love to preserve lines, but if it's not possible I can live without if latex at least adds par. I would like to be able to add macros like \textbf{a bold one}. }

\appendEnclosed{mynewfile.tex}{ But what I would love even more is the ability to add sharp like this: #. This work if I write a single sharp, but every time I append something to the file, each sharp is replaced with two sharps. Try to uncomment the next lines to see by yourself. }

\appendEnclosed{mynewfile.tex}{ 3333 }

% \appendEnclosed{mynewfile}{ % The number of sharp depends on the number of time we happened something to the file. I guess when the file is read, each sharp is turned into two sharps. % } % \appendEnclosed{mynewfile}{ % The number of sharp depends on the number of time we happened something to the file. I guess when the file is read, each sharp is turned into two sharps. % } % \appendEnclosed{mynewfile}{ % The number of sharp depends on the number of time we happened something to the file. I guess when the file is read, each sharp is turned into two sharps. % }

\section{Content of the file} \def\myTitle{} % Just to make sure that myTitle was properly expanded when writing to the file. \def\mymacro{MyMacro} \input{mynewfile}

\end{document}

tobiasBora
  • 8,684
  • Looks like you solved the issue already, but if you want to learn what catcode is and what it has to do with this question I recommend TeX by Topic. – user202729 Jan 28 '22 at 10:38
  • It seems like it si overcomplicated. Why do you read file contents first, then remove the file by \openout, then put the file contents and then put appended data? More simple is: open the file by \openput only once and then append repeatedly the data by \write. – wipet Oct 25 '22 at 20:12

1 Answers1

1

I finally solved the problem based on this answer https://tex.stackexchange.com/a/13289/116348 , I'm not sure if it's the best solution but it seems to work so far ^^

\documentclass{article}
\usepackage{catchfile} % To append to a file.
\usepackage{etoolbox}

\makeatletter

%% https://tex.stackexchange.com/a/13289/116348 \newwrite\appendwrite \newcount\pratend@count@makeallother

%%% Loop to make sure all chars are normal letters, including #. \newcommand{\makeallother}{% \pratend@count@makeallother0\relax \loop\ifnum\pratend@count@makeallother<255\relax \catcode\pratend@count@makeallother12\relax \advance\pratend@count@makeallother by 1\relax \repeat }

%%% This command allows the user to append things to a file. But the content may contain macros that %%% will be evaluated before writing them to the file. %%% TODO: Efficiency may be improved by writing to the file only before the printProof. % The first argument is the file name % The second argument is the text to write \NewDocumentCommand\appendtofile{m+m}{% \begingroup %% Read the content \begingroup% \IfFileExists{#1}{% \newlinechar\endlinechar% \makeallother% Turn all chars into normal letters. \everyeof{\noexpand}% \edef\fileContent{@@input #1 }% }{% \let\fileContent\empty }% %% Open the file to write in it: \immediate\openout\appendwrite #1\relax% %% Write the text \immediate\write\appendwrite{\fileContent}% \endgroup% \immediate\write\appendwrite{#2}% %% Close the file \immediate\closeout\appendwrite% \endgroup }

\NewDocumentCommand\appendEnclosed{m+m}{ \appendtofile{#1}{% This part may contain macros, potentially expanded like \myTitle: \string\par\string\noindent\string\rule{\string\linewidth}{2mm}\par% \string\begin{center}\myTitle\string\end{center} \string\par\string\noindent\string\rule{\string\linewidth}{2mm}\par% \detokenize{#2}%%<-- this part should be printed as closely as possible to the original text, including # and if possible new lines. \string\par\string\noindent\string\rule{\string\linewidth}{.5mm}\par% } }

%% Erase the file at the beginning to avoid accumulating things from previous runs. \immediate\openout\appendwrite=mynewfile.tex% \immediate\write\appendwrite{}% \immediate\closeout\appendwrite%

\makeatother

\begin{document} \def\myTitle{myTitle}

\appendEnclosed{mynewfile.tex}{ Hello

I would love to preserve lines, but if it's not possible I can live without if latex at least adds par. I would like to be able to add macros like \textbf{a bold one}. }

\appendEnclosed{mynewfile.tex}{ But what I would love even more is the ability to add sharp like this: #. This work if I write a single sharp, but every time I append something to the file, each sharp is replaced with two sharps. Try to uncomment the next lines to see by yourself. }

\appendEnclosed{mynewfile.tex}{ 3333 }

% \appendEnclosed{mynewfile}{ % The number of sharp depends on the number of time we happened something to the file. I guess when the file is read, each sharp is turned into two sharps. % } % \appendEnclosed{mynewfile}{ % The number of sharp depends on the number of time we happened something to the file. I guess when the file is read, each sharp is turned into two sharps. % } % \appendEnclosed{mynewfile}{ % The number of sharp depends on the number of time we happened something to the file. I guess when the file is read, each sharp is turned into two sharps. % }

\section{Content of the file} \def\myTitle{} % Just to make sure that myTitle was properly expanded when writing to the file. \def\mymacro{MyMacro} \input{mynewfile}

\end{document}

tobiasBora
  • 8,684