I'm trying to put in a LaTeX3 string a text to write later in a file (I also do other processing on that string before writing it, like text replacement, string concatenation…), but this file might not be a LaTeX document, but an arbitrary text/code, hence I want to preserve precisely the input, including newlines, spaces, tabulations, and many characters like _{}$\ like:
Hello % This is a comment that should not be removed
I want precisely this spacing, and I want to be allowed to use any text, like 1 + (1*2), héhé,
or:
def mycode(my_variable_with_underscore="hey"): # This is a comment (with a single sharp)
print(my_variable_with_underscore)
or latex macros like $\array{a & b\\c & d}$. And ideally unbalanced {} but I heard it was difficult
(maybe by replacing special tokens like \MYCLOSINGBRACE and \MYOPENINGBRACE in the final string?).
For now, I tried to play with \tl_rescan:nn, trying to follow this answer without much success.
MWE:
\documentclass[options]{article}
\ExplSyntaxOn
\iow_new:N \g_my_write
\cs_generate_variant:Nn \iow_now:Nn { NV }
%% Creates a new category table
%% Inspired by https://github.com/latex3/latex3/blob/0b851165c1dba9625f7ab80bb5c4cbd27f3e9af7/l3kernel/l3cctab.dtx
\cctab_const:Nn \c_my_active_cctab
{
\cctab_select:N \c_initex_cctab
\int_set:Nn \tex_endlinechar:D { -1 }
\int_step_inline:nnn { 0 } { 127 }
{ \char_set_catcode_active:n {#1} }
}
\NewDocumentCommand{\myExactWrite}{m}{
\str_new:N \g_my_string
\iow_open:Nn \g_my_write {my-output.txt}
% === Fails: the final string contains \tl_rescan code:
% \str_gset:Nn \g_my_string {\tl_rescan:nn {\cctab_select:N \c_my_active_cctab} { #1 }}
% === Fails: I get errors about missing $
% \def\mygset{\str_gset:Nn}
% \def\mystring{\g_my_string}
% \tl_rescan:nn {\cctab_select:N \c_my_active_cctab} { \mygset \g_my_string {#1} }
% In my code the write might arrives much later, even in another function, hence the use of a string
% === See that the input is not the expected one:
\str_set:Nn \g_my_string {#1}
\iow_now:NV \g_my_write \g_my_string
\iow_close:N \g_my_write
}
\ExplSyntaxOff
\usepackage{verbatim}
\begin{document}
%%% Level 1
\myExactWrite{
Hello % This is a comment that should not be removed
I want precisely this spacing, and I want to be allowed to use any text
}
% %% Level 2
% \myExactWrite{
% Hello % This is a comment that should not be removed
% I want precisely this spacing, and I want to be allowed to use any text, like 1 + (1*2), héhé,
% or:
% def mycode(my_variable_with_underscore="hey"): # This is a comment (with a single sharp)
% print(my_variable_with_underscore)
% or latex macros like $\array{a & b\c & d}$. And ideally unbalanced {} but I heard it was difficult
% (maybe by replacing special tokens like \MYCLOSINGBRACE and \MYOPENINGBRACE in the final string?).
% }
The content of the file is:
\verbatiminput{my-output.txt}
\end{document}





\myExactWriteis not expected to work in the argument of another command, you could read the argument verbatim and write exact text (likefilecontents) so rescan not needed. If you do need it to work in arguments, you can use\scantokensor\detokenizeto make things approximately OK in different ways, but it can never exactly re-create the source, as the information has gone.\foo\bar,\foo \bar,{\catcode\!= 0 !foo!bar}` all will look the same. – David Carlisle Mar 21 '23 at 10:11\fbox{\exactwrite{file.txt}{%text}}will never work, since while getting the parameter for\fbox, the comment will be ignored and you'll end up with an error about unbalanced braces. – Slurp Mar 21 '23 at 15:04\fbox(which I'm sure it's not) you could define your own version like\def\newfbox{\bgroup\setbox0=\hbox\bgroup\aftergroup\newfboxA\let\next} \def\newfboxA{\fbox{\box0}\egroup}, which doesn't take any parameters and so you don't need to worry about tokenization. Similar solutions exist for other similar macros. And an exactwrite macro should work in an environment like align since it's an environment and not a macro, so it doesn't tokenize your stuff – Slurp Mar 21 '23 at 15:24alignis (really) a macro, it scans the body first so it can evaluate it in two passes, you can not use\verbetc – David Carlisle Mar 21 '23 at 15:42