Bem, Python para o resgate!
Confira este one-liner, que lê de STDIN e imprime para STDOUT, lidando com todos os possíveis "caret escapes" / "códigos C0" (como ^I
) e indicadores de fim de linha ( $
):
python3 -c 'import sys,re;print(re.sub(r"\^([A-Z?@[\\]^_])",lambda m:chr((ord(m.group(1))-64)&127),sys.stdin.read().replace("$\n","\n")))'
Na verdade, é compatível com python
(2) e python3
. Aqui está uma versão mais longa e legível, fazendo basicamente o mesmo:
#!/usr/bin/env python3
import sys, re
# read everything from stdin and remove line-end indicators
s = sys.stdin.read().replace("$\n", "\n"))
# replace caret escapes like ^I or ^M and output to stdout
print(re.sub(r"\^([A-Z?@[\\]^_])", lambda m: chr((ord(m.group(1)) - 64) & 127), s)
Primeiramente, removemos os indicadores de fim de linha $
.
Segundo, usamos o padrão de expressão regular \^([A-Z?@[\\]^_])
para encontrar todos os caracteres válidos após o carets e substituir ambos pelo caractere sem escape correto, de acordo com a Wikipedia em Caret notation e Códigos de controle C0 . Observe como somente as letras maiúsculas A
- Z
ou uma de ?@[\]^_
têm um significado especial.
Agora, para desativar o código C0, tomamos a posição no alfabeto do caractere sucessivo do cursor (encontrado em m.group(1)
), por exemplo, "A" é 1, "B" é 2 e assim por diante. Isto é igual ao seu valor ASCII menos o código ASCII de "A" mais um, o que compõe o -64, o que também explica, e. "@" (ASCII 64) sendo 0 ou "[" (ASCII 91) sendo ESC (ASCII 27). Fazemos uma operação binária AND neste número com 127 para considerar apenas os primeiros 7 bits de informação, de modo que, e. "?" (ASCII 63 == 64-1) envolve 127, representando o caractere DEL.
Finalmente, após todos esses cálculos altamente complexos serem feitos, simplesmente imprimimos a string resultante novamente em STDOUT.