
Aqui estão duas perguntas sobre filecontents
meio ambiente:
é possível adicionar conteúdo a um arquivo pré-existente ou, no mínimo, manter aberto o arquivo externo para anexar o conteúdo de vários
filecontents
ambientes ao mesmo arquivo externo?é possível saber quantas linhas foram gravadas no arquivo externo por um
filecontents
ambiente?
Responder1
Contar as linhas filecontents
é fácil porque o ambiente processa seu conteúdo linha por linha, lendo, verificando se o ambiente terminou e depois escrevendo. Você só precisa fazer um contador e adicioná-lo toda vez que uma linha for escrita.
Anexar é mais complicado. Em primeiro lugar, o TeX (pelo menos sem Lua) não pode abrir arquivos com permissão "anexar", então isso está descartado. A ideia de deixar o arquivo aberto e retomar a gravação mais tarde parece mais fácil no início, mas você precisaria salvar um monte de variáveis em ambientes e não seria capaz de gravar em file a
, then b
e depois anexar para a
.
No código abaixo, \filecontents@preappend
macro (quando a append
opção é usada) lê o arquivo no argumento para uma macro temporária e, em seguida, \filecontents@append
coloca a macro lida no fluxo de entrada antes do conteúdo real do ambiente, para que funcione como se você digitou tudo de uma vez. A append
opção implica as opções overwrite
e noheader
.
Eu estava tentando escrever o código como um patch, para compactação, mas aparentemente o patch de um comando perde os ^^J
caracteres e, em seguida, o cabeçalho se reduz a uma única linha, então esta é uma redefinição completa de \filec@ntents
, mas as linhas alteradas estão marcadas.
\documentclass{article}
\usepackage{expl3}
\ExplSyntaxOn
\cs_new_eq:NN \FileSize \file_size:n
\ExplSyntaxOff
\makeatletter
\begingroup%
\@tempcnta=1
\loop
\catcode\@tempcnta=12 %
\advance\@tempcnta\@ne %
\ifnum\@tempcnta<32 %
\repeat %
\catcode`\^^M\active
\catcode`\^^L\active\let^^L\relax
\catcode`\^^I\active
\gdef\filec@ntents#1{%
\set@curr@file{\filec@ntents@checkdir#1}%
\edef\q@curr@file{\expandafter\quote@name\expandafter{\@curr@file}}%
\filecontents@preappend% <- ADDED for appending
\gaborit@reset@counter% <- ADDED for counting lines
\gaborit@reset@append% <- ADDED for counting lines
\openin\@inputcheck\q@curr@file \space %
\ifeof\@inputcheck%
\@latex@warning@no@line%
{Writing file `\@currdir\@curr@file'}%
\chardef\reserved@c15 %
\ch@ck7\reserved@c\write%
\immediate\openout\reserved@c\q@curr@file\relax%
\else%
\if@filesw%
\@latex@warning@no@line%
{File `\@curr@file' already \filec@ntents@where.\MessageBreak%
Not generating it from this source}%
\let\gaborit@step@counter\@empty% <- ADDED for counting lines
\let\gaborit@count@header\@empty% <- ADDED for counting lines
\let\write\@gobbletwo%
\let\closeout\@gobble%
\else%
\edef\reserved@a{#1}%
\edef\reserved@a{\detokenize\expandafter{\reserved@a}}%
\edef\reserved@b{\detokenize\expandafter{\jobname}}%
\ifx\reserved@a\reserved@b%
\@fileswtrue%
\else%
\edef\reserved@b{\reserved@b\detokenize{.tex}}%
\ifx\reserved@a\reserved@b
\@fileswtrue%
\fi%
\fi%
\chardef\reserved@c15 %
\ch@ck7\reserved@c\write%
\if@filesw% % Foul ... trying to overwrite \jobname!
\@latex@error{Trying to overwrite `\jobname.tex'}{You can't %
write to the file you a reading from!\MessageBreak%
Data is written to screen instead.}%
\else%
\@latex@warning@no@line%
{Writing or overwriting file `\@currdir\@curr@file'}%
\immediate\openout\reserved@c#1\relax%
\fi%
\fi%
\fi%
\closein\@inputcheck%
\if@tempswa%
\gaborit@count@header% <- ADDED for counting lines
\immediate\write\reserved@c{%
\@percentchar\@percentchar\space%
\expandafter\@gobble\string\LaTeX2e file `\@curr@file'^^J%
\@percentchar\@percentchar\space generated by the %
`\@currenvir' \expandafter\@gobblefour\string\newenvironment^^J%
\@percentchar\@percentchar\space from source `\jobname' on %
\number\year/\two@digits\month/\two@digits\day.^^J%
\@percentchar\@percentchar}%
\fi%
\let\do\@makeother\dospecials%
\count@ 128\relax%
\loop%
\catcode\count@ 11\relax%
\advance\count@ \@ne%
\ifnum\count@<\@cclvi%
\repeat%
\edef\E{\@backslashchar end\string{\@currenvir\string}}%
\edef\reserved@b{%
\def\noexpand\reserved@b%
####1\E####2\E####3\relax}%
\reserved@b{%
\ifx\gaborit@reset@append##1\relax% <- ADDED for counting lines
\gaborit@reset@append% <- ADDED for counting lines
\else% <- ADDED for counting lines
\ifx\relax##3\relax%
\immediate\write\reserved@c{##1}%
\gaborit@step@counter% <- ADDED for counting lines
\else%
\edef^^M{\noexpand\end{\@currenvir}}%
\ifx\relax##1\relax%
\else%
\@latex@warning{Writing text `##1' before %
\string\end{\@currenvir}\MessageBreak as last line of \@curr@file}%
\immediate\write\reserved@c{##1}%
\gaborit@step@counter% <- ADDED for counting lines
\fi%
\ifx\relax##2\relax%
\else%
\@latex@warning{%
Ignoring text `##2' after \string\end{\@currenvir}}%
\fi%
\fi%
\fi% <- ADDED for counting lines
^^M}%
\catcode`\^^L\active%
\let\L\@undefined%
\def^^L{\expandafter\ifx\csname L\endcsname\relax\fi ^^J^^J}%
\catcode`\^^I\active%
\let\I\@undefined%
\def^^I{\expandafter\ifx\csname I\endcsname\relax\fi\space}%
\catcode`\^^M\active%
\edef^^M##1^^M{%
\noexpand\reserved@b##1\E\E\relax}%
\filecontents@append}% <- ADDED for appending
%
% Code for append
\gdef\gaborit@pre@append{%
\begingroup%
\catcode`\^^L\active%
\catcode`\^^I\active%
\catcode`\^^M\active%
\let\do\@makeother\dospecials%
\count@ 128\relax%
\loop%
\catcode\count@ 11\relax%
\advance\count@ \@ne%
\ifnum\count@<\@cclvi%
\repeat%
\let^^M\relax%
\edef\gaborit@tmpa{\FileSize{\q@curr@file}}%
\ifnum\expandafter\@car\gaborit@tmpa\@nil=0\relax%
\endgroup \def\gaborit@append{^^M\gaborit@reset@append}%
\else%
\everyeof{\noexpand}%
\edef\gaborit@tmpa{\@@input\q@curr@file \space}%
\edef\x{\endgroup%
\edef\noexpand\gaborit@append{%
\noexpand\gaborit@trim@EOF\gaborit@tmpa\noexpand\gaborit@EOF}}\x%
\fi%
\filec@ntents@overwrite%
\filec@ntents@noheader}%
\gdef\gaborit@trim@EOF#1^^M\gaborit@EOF{^^M#1%
^^M\noexpand\gaborit@reset@append}%
\endgroup%
\def\filec@ntents@append{%
\let\filecontents@preappend\gaborit@pre@append
\def\filecontents@append{\gaborit@append}}
\let\filecontents@preappend\@empty
\let\filecontents@append\@empty
%
% For counting lines
\newcounter{FC@total@lines}
\newcounter{FC@lines}
\def\gaborit@reset@counter{\setcounter{FC@total@lines}{0}}
\def\gaborit@reset@append{\setcounter{FC@lines}{0}}
\def\gaborit@step@counter{%
\stepcounter{FC@total@lines}%
\stepcounter{FC@lines}}
\def\gaborit@count@header{%
\addtocounter{FC@total@lines}{4}%
\addtocounter{FC@lines}{4}} % Number of lines in the header
\makeatother
\begin{filecontents}[overwrite]{testfile.tex}
a
b
c
\end{filecontents}
\typeout{\arabic{FC@total@lines} lines written; \arabic{FC@lines} appended}
\begin{filecontents}[append]{testfile.tex}
d
e
f
g
\end{filecontents}
\typeout{\arabic{FC@total@lines} lines written; \arabic{FC@lines} appended}
\begin{document}
\end{document}
Executando este código, o TeX imprime no terminal:
LaTeX Warning: Writing or overwriting file `./testfile.tex'.
7 lines written; 7 appended
(./testfile.tex)
LaTeX Warning: Writing or overwriting file `./testfile.tex'.
11 lines written; 4 appended
(o FC@total@lines
contém o número total de linhas no arquivo e o FC@lines
contador contém apenas o número de linhas anexadas) e o arquivo testfile.tex
contém:
%% LaTeX2e file `testfile.tex'
%% generated by the `filecontents' environment
%% from source `test' on 2019/12/20.
%%
a
b
c
d
e
f
g
Como você observou, quando você faz:
\begin{filecontents*}{testfile.tex}
\end{filecontents*}
%
\begin{filecontents}[append]{testfile.tex}
a
\end{filecontents}
(ou seja, não escreva nada e anexe uma linha com a
) o arquivo obtido é:
a
Isso porque quando o TeX lê um arquivo vazio, ele trata esse arquivo como se fosse um arquivo com uma única linha vazia 1 , portanto você não pode diferenciar facilmente entre esses dois casos e tem que escolher o mal menor: ter um vazio falso linha no início do arquivo ao anexar a um arquivo vazio ou remover uma linha vazia ao gravar em um arquivo que contém uma única linha vazia.
Porém, com os utilitários de arquivo introduzidos no pdfTeX (mas também disponíveis em outros motores hoje em dia) você pode consultar o tamanho (em bytes) de um arquivo, para ter um comportamento diferente para um arquivo vazio. No código usei expl3
para \file_size:n
evitar ter que lidar com arquivos inexistentes.
1. Você pode ver isso com este arquivo de teste:
\catcode`\@=11
\newwrite\test
\def\testit#1{\immediate\openout\test \jobname.testfile\relax #1%
{\catcode`\^^M=13 \everyeof{\noexpand}%
\edef\tmpa{\ifdefined\@@input \@@input \else \input \fi \jobname.testfile }\show\tmpa}%
\immediate\closeout\test}%
\testit{}% empty file
\testit{\immediate\write\test{}}% single empty line
\csname stop\endcsname\bye
Esse comportamento, eu acho, se deve ao modo como o TeX adiciona um \endlinechar
no final de cada linha. Quando ele lê um arquivo vazio, ele ainda lê uma linha (mesmo que vazia) e depois insere o \endlinechar
( ^^M
) e isso faz parecer que o arquivo tem uma única linha vazia.