Pipes são um recurso do sistema operacional que funciona com buffers de bytes e não interpreta o conteúdo de forma alguma. Então o texto canalizado não passa por bash e especialmente nunca através de 'readline'. Texto colado como argumentos de linha de comando. (E sim, tanto o readline quanto o terminal podem filtrar caracteres de controle como medida de segurança.)
Seu arquivo é, na verdade, uma mistura de duas codificações, windows-1252
e iso8859-1
, devido às diferentes maneiras de usar o bloco de caracteres de controle C1 (0x80..0x9F).
- A ISO 8859-1 usa esse intervalo inteiro para caracteres de controle e os bytes 0x80..0x9F correspondem aos pontos de código Unicode U + 0080..U + 009F.
- O Windows-1252 não pode representar caracteres de controle C1; ele usa a maior parte desse intervalo para caracteres imprimíveis e possui alguns "buracos" - isto é, valores de bytes que não têm nada atribuído (0x81, 0x8D, 0x8F, 0x90, 0x9D).
- As duas codificações são idênticas nos intervalos 0x00..0x7F e 0xA0..0xFF.
Vamos pegar a primeira linha do seu arquivo de entrada "ruim", decodificado de UTF-16 para texto Unicode e com caracteres não-imprimíveis que escaparam:
\u0081@\u0081™TdaŽ®\u008FÆ‚êƒ~ƒNƒXƒgƒŒ\u0081[ƒg\u0081EƒrƒLƒjver1.11d1.d2\u0081iƒrƒLƒjƒ‚ƒfƒ‹ver.1.1\u0090³Ž®”z•z”Å\u0081j\n
- Você pode ver
\u0081
(U + 0081), que é mapeado para o byte 0x81 na ISO 8859-1, mas não pode ser codificado no Windows-1252. - Você também pode ver o símbolo
ƒ
(U + 0192), que é mapeado para 0x83 no Windows-1252, mas não existe na ISO 8859-1.
Assim, o truque é usar o Windows-1252 quando possível e a ISO 8859-1 como substituto, decidindo individualmente para cada ponto de código. (libiconv poderia fazer isso via 'ICONV_SET_FALLBACKS', mas a ferramenta CLI iconv
não pode.) É fácil escrever sua própria ferramenta:
#!/usr/bin/env python3
with open("/dev/stdin", "rb") as infd:
with open("/dev/stdout", "wb") as outfd:
for rune in infd.read().decode("utf-16"):
try:
chr = rune.encode("windows-1252")
except UnicodeEncodeError:
chr = rune.encode("iso8859-1")
outfd.write(chr)
# outputs shift-jis
Note que apenas metade do seu arquivo de entrada é codificado incorretamente como Shift-JIS. A outra metade (em inglês) é perfeitamente boa UTF-16; felizmente, o Shift-JIS passará por isso, então não é necessário dividir manualmente:
#!/usr/bin/env python3
with open("éΦé╟é▌üEé╓é╚é┐éσé▒éªéΦé⌐.txt", "r", encoding="utf-16") as infd:
with open("りどみ・へなちょこえりか.txt", "w", encoding="utf-8") as outfd:
buf = b""
for rune in infd.read():
try:
buf += rune.encode("windows-1252")
except UnicodeEncodeError:
try:
buf += rune.encode("iso8859-1")
except UnicodeEncodeError:
buf += rune.encode("shift-jis")
outfd.write(buf.decode("shift-jis"))