Um problema típico do látex:
\SomeStyle{\otherstyle{this is the \textit{nested part} some more text...}}
Agora eu quero remover tudo \SomeStyle{...}
, mas não o conteúdo. O conteúdo contém chaves aninhadas. A linha acima deve se tornar:
\otherstyle{this is the \textit{nested part} some more text...}
Questões:
- Algum editor de LaTeX oferece uma maneira de fazer isso?
- Qual editor/script faz isso?
- Como fazer isso com sed? [🤓]
Minha solução é um script bash usando sed.
- preparar texto: marcar substituir string com ascii bell, adicionar nova linha após cada chave
- loop: find { -> adiciona X ao espaço de espera, find } -> remove X do espaço de espera, mantém o espaço vazio -> remove o fechamento }
- restaurar novas linhas e sino ascii para o anterior
O script funciona, mas falha com:
\badstyle{w}\badstyle{o}\badstyle{r}\badstyle{d}
Ele se tornará:
wo}rd}
a ramificação para :f parece não funcionar.
F=$(sed 's|\\|\\\\|g;s|{|\\{|g' <<< "$1" )
# mark all removestrings with ascii bell and newline
# add newline after each { and }
SEDpre='
s|'"$F"'|\a%\n|g
s|\{|\{\n|g
s|\}|\}\n|g
'
SEDpost='
:a;N;$!ba;
s|\a%\n||g
s|\{\n|\{|g
s|\}\n|\}|g
'
# count the brackets
SED='
/\a%/{
:a
n
:f
/\{/{x;s|$|X|;x;ba}
/\}/{x;
s|X||;
/^$/{x;bb}
x
ba
}
}
b
:b
/\}/{
s|\}||;
N;
s|\n||;
/\a%/bf
}
'
sed -r -E "$SEDpre" "$2" | sed -rE "$SED" | sed -rE "$SEDpost"
A abordagem típica é usar
perl
a capacidade de expressão regular recursiva de :Ou se você tiver que contabilizar chaves escapadas como
\{
(e\
escapadas como\\
)¹Onde substituímos
[^{}]*
por(?:\\.|[^{}\\])*
para corresponder\anycharacter
(incluindo\\
,\{
e\}
com os quais nos importamos aqui), além de caracteres diferentes de\
,{
, e}
.(?:...)
é a forma não capturável de(...)
.(adicione
-i
opção para editar o arquivoi
n-place).Acima
(?1)
é como inserir a expressão regular no primeiro par de(...)
, então(\{((?:(?1)|\\.|[^\\{}])*+)\})
nesse ponto.Se o
\SomeStyle{...}
s pode ser aninhado como em:Para ser alterado para:
Então mude para:
O que repetirá o processo, substituindo os externos primeiro até que nenhuma correspondência seja encontrada.
Para fazer isso para estilos e arquivos arbitrários:
Com
sed
, assumindo uma implementação onde toda a entrada pode caber no espaço de padrões, uma abordagem (também lidando com entradas aninhadas, começando com as internas neste caso) poderia ser:(mesmo tipo de abordagem usada em Removendo aspas de texto (possivelmente aninhadas) na linha de comando e alguns outros aqui).
Algumas
sed
implementações copiaram o Perl-i
para edição no local, mas esteja ciente de que em algumas (FreeBSD e derivados), você precisa-i ''
fazer edição no local sem fazer backup do original.-i.back
funcionaria em todas as implementações que têm um-i
(e em Perl) e salvam o original comofile.tex.back
.Parece que você
sed
é GNUsed
, pois está usando vários GNUismos, e o GNUsed
oferece suporte-i
a issoperl
e, até onde sei, não há um limite além da memória disponível no tamanho do espaço do padrão.Para contabilizar chaves escapadas como
\{
(e\
escapadas como\\
)¹, você pode usar a opção agora padrão-E
(preferível à específica do GNU-r
) para alternar para expressões regulares estendidas que têm um|
operador de alternância, embora observe que{
também se torna um operador regexp then e precisa ser escapado quando estiver fora de[...]
, e agrupar+capturar mudanças de\(...\)
para(...)
:¹ ainda ignorando a possibilidade de que possa haver
\\SomeStyle{something}
, não manipulando comentários ou\verb|...|
... Cobrir esses problemas e fazer uma tokenização TeX completa seria possível, mas pode não valer o esforço, dependendo da sua contribuição real.Usando Raku (anteriormente conhecido como Perl_6)
Combine o alvo desejado usando
<~~>
a notação regex recursiva do Raku:Exemplo de entrada:
Exemplo de saída:
O Raku fornece uma nova sintaxe Regex que algumas pessoas acham mais fácil de ler. O código foi tirado quase literalmente da página de documentação Regex do Raku . Aqui, simplesmente usamos
m///
o operador match do Raku, tornado global com o:g
parâmetro nomeado:\{ ~ \} <expression>
denota a sintaxe do til para estruturas aninhadas ,<-[{}]>*
denota uma classe de caracteres negativos personalizada contendo qualquer caractere, exceto{}
chaves. ICYMI,<+[{}]>*
ou mais simplesmente<[{}]>*
denotaria uma classe de caracteres positivos,<~~>
denota uma regex recursiva ,<(
… denota marcadores)>
de captura no Raku.Para processar um arquivo corrigindo as linhas ofensivas e exibindo as linhas não ofensivas na íntegra, use o operador ternário do Raku: Test
??
True!!
False .Infelizmente, no momento, todos os exemplos de código acima simplesmente retiram o - de nível superior
Style
(mais chaves associadas) de maneira linear, seja lá o queStyle
for. Vou trabalhar para corrigir essa falta de especificidade.Observadores astutos podem notar que todas as respostas acima usam
m///
o operador de correspondência do Raku. Para sua informação, tenho certeza de que há uma maneira de fazer isso coms///
o operador de substituição do Raku (em conjunto com os marcadores de captura do Raku<(
))>
, mas eu queria que essasm///
respostas de correspondência fossem publicadas primeiro.Aqui está um
sed
mecanismo possível. Para simplificar, assumimos que não há caracteres sublinhados, então podemos usar um como marcador. Isso é como seu sino ascii. Inserimos o marcador no início da linha e o movemos caractere por caractere até o final da linha. Cada vez que ele se move,{
adicionamos um+
sinal ao início da linha para atuar como um contador. Cada vez que ele se move,}
removemos a+
do início. Se não tivermos mais+
sinais, então equilibramos as chaves e podemos aplicar o substituto desejado, até o marcador.Caso a linha comece com
+
already, começamos adicionando!!
no início e removendo no final.Usando qualquer awk:
O exemplo acima não tenta lidar com escape
{
ou}
na entrada porque precisaria tratar\{
(escaped{
) de forma diferente de\\{
(escaped\
followed by{
) e isso requer mais reflexão do que estou disposto a colocar nisso, já que não aparece na entrada de exemplo e, portanto, provavelmente não é realmente um problema para o OP e eles sempre podem fazer uma pergunta complementar se for e eles ainda não tiverem uma maneira de lidar com isso.Atualização: Após discussão com @StéphaneChazelas nos comentários abaixo de sua resposta , acredito que você só precisa substituir
[^{}]
por(\\.|[^{}\\])
na expressão regular usada pormatch()
para manipular escapes{
ou}
na entrada.Ele assume que todo
\SomeStyle{
ou apenas{
tem um}
.Aqui está uma versão comentada do acima, pois pode não ser óbvio à primeira vista o que está fazendo:
O script verifica se a entrada da string de substituição está correta (sem chaves), verifica se há chaves de escape no arquivo de destino ({,}) e cria um backup.
abordagem sed/abordagem perl: