Aqui está uma string de um arquivo de texto:
@™TdaŽ®Æ‚êƒ~ƒNƒXƒgƒŒ[ƒgEƒrƒLƒjver1.11d1.d2iƒrƒLƒjƒ‚ƒfƒ‹ver.1.1³Ž®”z•z”Åj
Inclui muitos caracteres não imprimíveis e é copiado aqui:https://pastebin.com/TUG4agN4
Usandohttps://2cyr.com/decode/?lang=en, podemos confirmar que isso se traduz no seguinte:
☆Tda式照れミクストレート・ビキニver1.11d1.d2(ビキニモデルver.1.1正式配布版)
Isso ocorre com codificação de origem = SJIS (shift-jis), exibida como Windows-1252.
Mas como podemos obter o mesmo resultado sem um site? A ferramenta relevante é o iconv, mas algo na cadeia de ferramentas está quebrado. Se eu tentar cat a partir do arquivo de texto de origem ou usá-lo como entrada padrão com '<' no bash, um dos 'iconv's na cadeia falha rapidamente. Se eu copiar a string acima do editor de texto gedit (lendo o arquivo como utf-16le) ou como saída por iconv com conversão de utf16 para utf8, o resultado será próximo, mas ainda errado:
@儺da式ニれミクストレ[トEビキニver1.11d1.d2iビキニモデルver.1.1ウ式配布版j
Algumas evidências de falha na cadeia de ferramentas:
$ gato 'utf8.txt' |head -1
@™TdaŽ®Æ‚êƒ~ƒNƒXƒgƒŒ[ƒgEƒrƒLƒjver1.11d1.d2iƒrƒLƒjƒ‚ƒfƒ‹ver.1.1³Ž®”z•z”Å
$ gato 'utf8.txt' |head -1| íconev -f utf8 -t utf16
���@�"!Tda}��� ��~�N�X�g�R�[�g�E�r�L�jver1.11d1.d2�i�r�L�j� �f �9 ver.1.1��}� z" z ��j
Observe três caracteres inválidos no início.
$ gato 'utf8.txt' |head -1| íconev -f utf8 -t utf16|iconv -f utf16 -t windows-1252
iconv: sequência de entrada ilegal na posição 2
$ echo "@™TdaŽ®Æ‚êƒ~ƒNƒXƒgƒŒ[ƒgEƒrƒLƒjver1.11d1.d2iƒrƒLƒjƒ‚ƒfƒ‹ver.1.1³Ž®”z•z”Åj"| íconev -f utf8 -t utf16
��@"!Tda}�� ��~�N�X�g�R[�gE�r�L�jver1.11d1.d2i�r�L�j� �f�9 ver.1.1�}� z" z j
Observe dois caracteres inválidos no início, outras diferenças. A sequência copiada do terminal corresponde à string exibida no editor de texto, confirmada por find (ctrl-F) correspondente, que é a mesma string que fornece o resultado correto em 2cyr.com.
Estender o último comando acima com '|iconv -f utf16 -t windows-1252|iconv -f shift-jis -t utf8' fornece o resultado próximo, mas incorreto, citado acima, em vez de gerar erros como a cadeia direta faz.
Se eu tentasse criar um arquivo chamado string de exemplo e usar a ferramenta convmv nele, convmv dizia que o nome do arquivo de saída continha "caracteres que não estão em conformidade com o sistema de arquivos POSIX! Isso pode resultar em perda de dados". A maioria dos nomes de arquivos inválidos com UTF-8 não fornecem esse aviso.
Existe alguma sequência de bits que a tubulação no bash não consegue controlar? Se não, por que a cadeia de ferramentas não está funcionando?
Aparentemente, a diferença é porque o bash não cola caracteres não imprimíveis (as caixas com números) na linha de comando; talvez 'readline' não consiga lidar com eles? Mas o resultado próximo sugere que a ordem de conversão no conjunto de ferramentas está correta, então por que não está funcionando?
O arquivo original, com seu nome embaralhado de forma diferente (expira após 30 dias):https://ufile.io/oorcq
Responder1
Pipes são um recurso do sistema operacional que funciona com buffers de bytes e não interpreta seu conteúdo de forma alguma. Então o texto canalizado não passa para o bash eespecialmentenunca através de 'readline'. O texto colado como argumentos de linha de comando faz isso. (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 como eles usam o bloco de caracteres de controle C1 (0x80..0x9F).
- ISO 8859-1 usa todo esse intervalo para caracteres de controle, e os bytes 0x80..0x9F correspondem aos pontos de código Unicode U+0080..U+009F.
- Windows-1252não poderepresentam caracteres de controle C1; ele usa a maior parte desse intervalo para caracteres imprimíveis e possui alguns "buracos" - ou seja, 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 escapados:
\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 mapeia 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 mapeia para 0x83 no Windows-1252, mas não existe na ISO 8859-1.
Portanto, o truque é usar o Windows-1252 quando possível e o ISO 8859-1 como substituto, decidindo individualmente para cada ponto de código. (a libiconv poderia fazer isso através de 'ICONV_SET_FALLBACKS', mas a iconv
ferramenta CLI 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
Observe que apenasmetadedo seuArquivo de entradaé Shift-JIS mal codificado. A outra metade (inglês) está perfeitamente bem em UTF-16; felizmente, o Shift-JIS passará por isso, portanto nenhuma divisão manual será necessária:
#!/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"))