Como posso remover a lista técnica de um arquivo UTF-8?

64

Eu tenho um arquivo na codificação UTF-8 com a BOM e quero remover a BOM. Existem ferramentas de linha de comando do linux para remover a lista técnica do arquivo?

$ file test.xml
test.xml:  XML 1.0 document, UTF-8 Unicode (with BOM) text, with very long lines
m13r
fonte
11
Eu criei uma ferramenta bastante simples para fazer isso há alguns meses: oskog97.com/read/?path=/small-scripts/killbom&referer=/… Pode valer a pena instalar algo parecido em / usr / local / bin se você tem muitos arquivos codificados em UTF-8 com BOMs.
Oskar Skog

Respostas:

76

Se você não tiver certeza se o arquivo contém uma BOM UTF-8, isso (assumindo a implementação GNU de sed) removerá a BOM, se existir, ou não fará alterações, se não existir.

sed '1s/^\xEF\xBB\xBF//' < orig.txt > new.txt

Você também pode substituir o arquivo existente com a -iopção:

sed -i '1s/^\xEF\xBB\xBF//' orig.txt
CSM
fonte
4
isso pode não funcionar em um código de idioma utf8, mas o prefixo de uma substituição de código de idioma para c ou posix sempre funcionará.
Hildred
3
@hildred Eu testei com a en_US.UTF-8localidade e funcionou. Quando isso vai falhar?
M13r 24/07/19
2
@ m13r, Depende da versão das opções sed e compile. No caso de falha, uma versão muito nova do sed com classes de caracteres Unicode trará a sequência de três bytes como um único caractere que não corresponde à sequência de três caracteres. No entanto, nesse caso, você pode fazer uma correspondência de caracteres de dezesseis bits. No entanto, este é um novo recurso e não está universalmente presente. Se você quiser testar, recomendo compilar a versão mais recente.
Hildred
4
Para corrigi-lo para trabalhar com um sed habilitados para unicode fazer LC_ALL = C sed '1s / ^ \ XEF \ xbb \ XBF //'
Joshua
11
@mazunki, 1s/significa apenas pesquisar na primeira linha; outras linhas não são afetadas. Os ^meios correspondem apenas no início da (primeira) linha. \xEF\xBB\xBFé a lista técnica UTF-8 (cadeia hexadecimal com escape). //significa substituir por nada. Eu poderia ter adicionado 1ao final (para 1s/^xEF\xBB\xBF//1), o que significaria apenas corresponder à primeira ocorrência do padrão na linha. Mas, como a pesquisa está ancorada ^, isso não fará nenhuma diferença. Se o arquivo não tiver a lista técnica no início da primeira linha, o padrão não corresponderá e, portanto, nenhuma alteração será feita.
CSM
64

Uma lista técnica não faz sentido no UTF-8. Esses são geralmente adicionados por engano por softwares falsos nos sistemas operacionais da Microsoft.

dos2unix irá removê-lo e também cuidar de outras idiossincrasias dos arquivos de texto do Windows.

dos2unix test.xml
Stéphane Chazelas
fonte
17
Concordo que uma lista técnica codificada em UTF-8 não faz sentido, mas acredite ou não, muitas pessoas acham que é uma ótima idéia que ajuda a diferenciar UTF-8 de outras codificações de 8 bits. Portanto, é uma questão de gosto. O bloco de notas do Windows adiciona uma lista técnica de propósito.
Johan Myréen
17
O que importa se faz sentido ou não, quando o contexto é apenas uma pergunta sobre como removê-lo? Segundo a Wikipedia, o Bloco de notas exige que a BOM reconheça um arquivo como UTF-8, e o Google Docs também o adiciona ao exportar um arquivo como texto. Duvido que todos façam isso por engano .
23917 ilkkachu
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
terdon
11
Existe uma maneira de não converter as terminações de linha e apenas remover a lista técnica com dos2unix?
M13r
2
@ m13r Em seguida, use o script sed nesta resposta . Isso removerá apenas o bom (se existir), nada mais será alterado.
Arrow
27

É possível remover a lista técnica de um arquivo com o tailcomando:

tail -c +4 withBOM.txt > withoutBOM.txt
m13r
fonte
2
Por que 4? A lista técnica tem 3 bytes.
Deviantfan
10
@deviantfan É por isso que você precisa iniciar no 4º byte se deseja ignorá-lo.
Stéphane Chazelas
9
tailestá usando 1 indexação baseada ?! WTF!
CodesInChaos
5
@CodesInChaos, tail -c -1ou tail -c 1(para o que tailgeralmente é usado) é o conteúdo que começa com o último byte, tail -c +1começando com o primeiro byte. tail -c 0/ tail -c +0para isso seria muito mais intuitivo.
Stéphane Chazelas
2
@deviantfan: (dd bs=1 count=3 of=/dev/null; cat) <input >output. Ou com o GNU (head -c3 >/dev/null; cat)- mesmo em UTF8 ou outro local não de byte único; O cabeçalho GNU faz 'char' = byte.
Dave_thompson_085
20

Usando o VIM

  1. Abrir arquivo no VIM:

    vi text.xml
    
  2. Remova a codificação da BOM:

    :set nobomb
    
  3. Salve e saia:

    :wq
    
Joshua Pinter
fonte
Estranhamente com o vim 8 em um mac, eu tenho um arquivo csv utf-8 criado pelo Excel e ele começa com <feff>, mas :set nobombnão o modifica ou remove.
dlamblin 9/10
5

Você pode usar

LANG=C LC_ALL=C sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//' -i -- filename

para remover a marca de ordem de bytes do início do arquivo, se houver, e converter qualquer nova linha de CR LF em LF apenas. O LANG=C LC_ALL=Ccomando diz ao shell que você deseja que o comando seja executado no código de idioma C padrão (também conhecido como código de idioma POSIX padrão), onde os três bytes que formam a Marca de Ordem de Byte são tratados como bytes. A -iopção para sed significa no local. Se você usar -i.old, o sed salva o arquivo original como filename.olde o novo arquivo (com as modificações, se houver) como filename.


Eu pessoalmente gosto de ter isso como ~/bin/fix-ms; por exemplo, como

#!/bin/dash
export LANG=C LC_ALL=C
if [ $# -gt 0 ]; then
    for FILE in "$@" ; do
        sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//' -i -- "$FILE" || exit 1
    done
else
    exec sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//'
fi

para que, se eu precisar aplicar isso para dizer todos os arquivos e cabeçalhos de origem C (meu código antigo da era do MS-DOS, por exemplo!), eu apenas corro

find . -name '*.[CHch]' -print0 | xargs -r0 ~/bin/ms-fix

ou, se eu quiser apenas ver um arquivo desse tipo, sem modificá-lo, posso executar

~/bin/ms-fix < filename | less

e não ver o feio <U+FEFF>no meu terminal UTF-8.

Animal Nominal
fonte
Por que não simplesmente sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//' -i -- "$@"?
Stéphane Chazelas
@ StéphaneChazelas: Porque eu quero que o script saia imediatamente se houver um problema com uma substituição, o que sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//' -i -- "$@"não ocorre; ele retorna um código de saída, mas processa todos os arquivos listados na lista de argumentos antes de sair.
Animal Nominal
@ StéphaneChazelas: O --antes do (s) nome (s) do arquivo é, obviamente, importante: sem ele, os nomes de arquivo que começam com um hífen podem ser considerados opções pelo sed. Eu os editei na minha resposta; obrigado pela lembrança!
Animal Nominal
0

Recentemente, encontrei esta pequena ferramenta de linha de comando que adiciona ou remove a lista técnica em arquivos codificados UTF-8 arbitrários: UTF BOM Utils ( novo link no github)

Pouco inconveniente, você pode baixar apenas o código-fonte C ++ simples. Você precisa criar o makefile (com o CMake , por exemplo) e compilá-lo sozinho, os binários não são fornecidos nesta página.

Wernfried Domscheit
fonte