12

With the commands \openout and \write I output text into a file. How can i output a tab ?

\documentclass{article}
\begin{document}
  \newwrite\tempfile
  \immediate\openout\tempfile="filename.txt"
  \immediate\write\tempfile{I want a tab at the beginning of this line}
  \immediate\closeout\tempfile
\end{document}

EDIT1

Following egreg's solution, i use \catcode`\^^I=12. This works if i use directly the code, but not if i enclose the code in a command :

\documentclass{article}
\begin{document}
      \newwrite\tempfile
      \immediate\openout\tempfile="filename.txt"
      \begingroup
        \catcode`\^^I=12 %
        \immediate\write\tempfile{^^II want a tab at the beginning of this line}
      \endgroup
      \immediate\closeout\tempfile
    
    \newcommand{\test}{
    \newwrite\tempfilee
    \begingroup\catcode`\^^I=12 %
    \immediate\openout\tempfilee="filename2.txt"
        \immediate\write\tempfilee{^^II want a tab at the beginning of this line}
    \endgroup
    }
      
    \test   
    coucou
\end{document}

EDIT2 . Thanks again to egreg. The 2nd solution works, but not if i call \test from inside another command, which is what i need :

\newwrite\tempfile
\immediate\openout\tempfile=\jobname.out

\def\test{\begingroup\catcode`\^^I=12 \testaux}
\def\testaux#1{\immediate\write\tempfile{#1}\endgroup}

\newcommand{\makeit}{
\test{^^I<-TAB again}
}

\test{        <-TAB} % WORKS
\test{^^I<-TAB} %WORkS
\makeit %DOES NOT WORK
\immediate\closeout\tempfile

EDIT 3. The second solution of egreg works ... but not if it is inside a AtEndDocument :

\documentclass{article}
\AtEndDocument{\test{^^Ifin du document}
\immediate\closeout\tempfile }

\begin{document}
\newwrite\tempfile
\immediate\openout\tempfile=output.txt
\catcode`\^^I=\active
\def^^I{}
\def\test#1{\begingroup
\edef^^I{\string^^I}\immediate\write\tempfile{#1}\endgroup}

\test{^^I<-coucou} %% WORKS
\def\foo{\test{^^I<-Jojo}}
\foo  %% WORKS
\end{document}

But the last sentence "fin du document" has no TAB.

3

2 Answers 2

11

Writing a literal TAB character in a file is, unfortunately, dependent on the TeX distribution you have and how the formats were initialized.

With a TeX Live distribution, the formats for pdftex, pdflatex and latex are prepared feeding the option -translate-file=cp227.tcx, that makes the TAB character "printable".

Compiling the input

\newwrite\tempfile
\immediate\openout\tempfile=\jobname.out
\begingroup
  \catcode`\^^I=12 %
  \immediate\write\tempfile{^^I<-TAB}
\endgroup
\immediate\closeout\tempfile
\csname @@end\endcsname\end % trick for stopping with Plain TeX and LaTeX

with tex and with pdftex (on a TeX Live distribution) will produce two different results (I report the "hex dump"):

  1. 5e 5e 49 3c 2d 54 41 42 0a (tex)

  2. 09 3c 2d 54 41 42 0a (pdftex)

Indeed tex is not set up with the TCX translation that makes the TAB character printable.

If one compiles the file with luatex or lualatex the result will be as in case 2. With xetex it's still different.

  1. 5e 5e 49 3c 2d 54 41 42 0a (xetex)

  2. 09 3c 2d 54 41 42 0a (xetex -8bit)

Thus the ^^I is written in this form with tex, xetex or xelatex, while it is written as a literal TAB with pdftex, pdflatex, luatex or lualatex, but also with xetex and xelatex provided they are called with the -8bit option.

The moral of the story is: don't use TABs and don't try writing them in files, for the result won't be predictable across systems and machines.


How to define a command that writes a line to a file respecting the TAB characters (assuming pdftex)?

One has to remember that the category codes are fixed once a token list has been read in by TeX. So one has to use a different strategy:

\newwrite\tempfile
\immediate\openout\tempfile=\jobname.out

\def\test{\begingroup\catcode`\^^I=12 \testaux}
\def\testaux#1{\immediate\write\tempfile{#1}\endgroup}

\test{        <-TAB}
\test{^^I<-TAB}

\immediate\closeout\tempfile

\csname @@end\endcsname\end

This will write in \jobname.out (again showing the hex dump)

09 3c 2d 54 41 42 0a 09 3c 2d 54 41 42 0a

so two literal TABS (the space in the first argument to \test is supposed to be a TAB, which on this system may be converted to spaces, unfortunately) are written.


If you need \test to work also in the argument to another command, then the trick above won't work: something like \foo{x \test{^^I<-TAB}}, where \foo takes one argument, would defeat the delayed grabbing of the argument done in \testaux rather than in \test: when TeX stores the argument to \foo it fixes the category codes.

However there's another problem: TeX normalizes ^^I to a space token as soon as it reads it (when ^^I has category code 10). So your problem is much more difficult to solve. If you don't need the TAB in your file other than for the purpose of writing it, you can do

\catcode`\^^I=\active
\def^^I{}

\newwrite\tempfile
\immediate\openout\tempfile=\jobname.out

\def\test#1{\begingroup
  \edef^^I{\string^^I}\immediate\write\tempfile{#1}\endgroup}

\test{        <-TAB}
\test{^^I<-TAB}

\def\foo#1{#1\test{#1}}

\foo{^^I<-TAB}

\immediate\closeout\tempfile

\csname @@end\endcsname\end

This has the limitation that the TAB won't print anything and a sequence <SPACE><TAB><SPACE> will print two spaces.

Consider very well not to proceed along this path.

If the commands where \test is needed are limited in number, one might proceed as in the case above giving a correct definition. If you want to allow \test as the argument to any command, you're out of luck.


If all you need is to do a write \AtEndDocument, you can say

\begingroup
\catcode`\^^I=12
\gdef\EndTest{\test{^^I<-TAB}}
\endgroup
\AtEndDocument{\EndTest}

One could do without defining a new command:

\begingroup
\lccode`?=`\^^I
\lowercase{\endgroup\AtEndDocument{\test{?fin du document}}}
\AtEndDocument{\immediate\closeout\tempfile}

You can use a different character than ?, what's important is that it's a category 12 character.

11
  • well, i understand the potential problems, but I need it, and i will use it always with pdftex. A biggest problem for me is : when i put your code in a command \test and i call \test, then the TAB is replaced by an empty space in the output file. Why ? Jun 6, 2012 at 9:33
  • You need to define \test in a context where ^^I already has category code 12. Please, update your question.
    – egreg
    Jun 6, 2012 at 9:39
  • i think a #1 ist missing in the def of \testaux, isnt it ? or maybe, i just do not understand the code... :( Jun 6, 2012 at 13:15
  • ok, i think i got it. Anyway, it works :) Great ! Jun 6, 2012 at 13:21
  • just to check wether i understood or not : if we put a #1 in the definition of ţextaux, it does not work anymore, because somehow, the replacement text of #1 ist expanded too early. Is it correct ? Jun 6, 2012 at 13:25
2

You may define a new macro \mytab providing the TAB character with category 12.

\begingroup
\catcode`\^^I=12
\gdef\mytab{^^I}
\endgroup

Then, use \mytab (with a space at end) instead of ^^I or <TAB>:

\documentclass{article}
% memoize TAB char
\begingroup
\catcode`\^^I=12
\gdef\mytab{^^I}
\endgroup
% open temp file
\newwrite\tempfile
\immediate\openout\tempfile=\jobname.out
% macro to write to temp file
\def\writetotemp#1{\immediate\write\tempfile{#1}}

\AtEndDocument{\writetotemp{\mytab<-TABEND}}
\AtEndDocument{\immediate\closeout\tempfile}
\begin{document}
\immediate\write\tempfile{\mytab <-TAB1}
\writetotemp{\mytab <-TAB2}
\end{document}

Here is the dump of \jobname.out:

0000000  \t   <   -   T   A   B   1  \n  \t   <   -   T   A   B   2  \n
           3c09    542d    4241    0a31    3c09    542d    4241    0a32
0000020  \t   <   -   T   A   B   E   N   D  \n
           3c09    542d    4241    4e45    0a44
0000032

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .