Bagaimana saya bisa menghapus BOM dari file UTF-8?

64

Saya memiliki file dalam pengkodean UTF-8 dengan BOM dan ingin menghapus BOM. Apakah ada alat baris perintah linux untuk menghapus BOM dari file?

$ file test.xml
test.xml:  XML 1.0 document, UTF-8 Unicode (with BOM) text, with very long lines
m13r
sumber
1
Saya telah membuat alat yang jauh sederhana untuk melakukan hal itu beberapa bulan yang lalu: oskog97.com/read/?path=/small-scripts/killbom&referer=/... Mungkin perlu menginstal sesuatu seperti itu di / usr / local / bin jika Anda memiliki banyak file berkode UTF-8 dengan BOM.
Oskar Skog

Jawaban:

76

Jika Anda tidak yakin apakah file tersebut mengandung BOM UTF-8, maka ini (dengan asumsi implementasi GNU sed) akan menghapus BOM jika ada, atau tidak membuat perubahan jika tidak.

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

Anda juga dapat menimpa file yang ada dengan -iopsi:

sed -i '1s/^\xEF\xBB\xBF//' orig.txt
CSM
sumber
4
ini mungkin tidak bekerja di lokal utf8, tetapi menambahkan sebuah lokal override ke c atau posix akan selalu berfungsi.
Hildred
3
@hildred Saya sudah mengujinya dengan en_US.UTF-8lokal dan berhasil. Kapan itu akan gagal?
m13r
2
@ m13r, Itu tergantung pada versi opsi sed dan kompilasi. Dalam kasus kegagalan sed versi yang sangat baru dengan kelas karakter Unicode akan membawa urutan tiga byte sebagai karakter tunggal yang tidak cocok dengan urutan tiga karakter. Namun dalam kasus seperti itu Anda dapat melakukan pertandingan karakter enam belas bit. Namun ini adalah fitur baru dan tidak ada secara universal. Jika Anda ingin menguji, saya sarankan untuk menyusun versi terbaru.
Hildred
4
Untuk memperbaikinya agar bekerja dengan sed yang diaktifkan unicode lakukan LC_ALL = C sed '1s / ^ \ xEF \ xBB \ xBF //'
Joshua
1
@mazunki, 1s/berarti hanya mencari baris pertama; jalur lain tidak terpengaruh. The ^berarti hanya cocok di awal (pertama) line. \xEF\xBB\xBFadalah BOM UTF-8 (lolos string hex). //berarti ganti dengan yang tidak ada. Saya bisa menambahkan 1ke akhir (untuk 1s/^xEF\xBB\xBF//1), yang berarti hanya cocok dengan kemunculan pertama dari pola di telepon. Tetapi karena pencarian itu berlabuh ^, ini tidak akan membuat perbedaan. Jika file tidak memiliki BOM di awal baris pertama, polanya tidak akan cocok, dan dengan demikian tidak ada perubahan yang dibuat.
CSM
64

BOM tidak masuk akal dalam UTF-8. Itu umumnya ditambahkan secara tidak sengaja oleh perangkat lunak palsu pada OS Microsoft.

dos2unix akan menghapusnya dan juga menangani keanehan lain dari file teks Windows.

dos2unix test.xml
Stéphane Chazelas
sumber
17
Saya setuju bahwa BOM yang dikodekan UTF-8 tidak masuk akal, tetapi percaya atau tidak, ada banyak orang yang berpikir itu adalah ide bagus yang membantu membedakan UTF-8 dari pengkodean 8-bit lainnya. Jadi itu masalah selera. Windows Notepad sengaja menambahkan BOM.
Johan Myréen
17
Apa bedanya jika itu masuk akal atau tidak, ketika konteksnya hanya pertanyaan tentang cara menghapusnya? Menurut Wikipedia, Notepad mengharuskan BOM untuk mengenali file sebagai UTF-8, dan Google Documents juga menambahkannya saat mengekspor file sebagai teks. Saya ragu mereka semua melakukannya karena kesalahan .
ilkkachu
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
terdon
1
Apakah ada cara untuk tidak mengubah akhir baris dan hanya menghapus BOM dos2unix?
m13r
2
@ m13r Lalu gunakan skrip sed dalam jawaban ini . Itu hanya akan menghapus bom (jika ada), tidak ada lagi yang akan diubah.
Panah
27

Dimungkinkan untuk menghapus BOM dari file dengan tailperintah:

tail -c +4 withBOM.txt > withoutBOM.txt
m13r
sumber
2
Mengapa 4? BOM memiliki 3 byte.
deviantfan
10
@deviantfan Itulah sebabnya Anda harus mulai dari byte ke-4 jika Anda ingin melewatkannya.
Stéphane Chazelas
9
tailmenggunakan pengindeksan berbasis 1 ?! WTF!
CodesInChaos
5
@CodesInChaos, tail -c -1atau tail -c 1(apa tailyang biasanya digunakan) adalah konten yang dimulai dengan byte terakhir, tail -c +1dimulai dengan byte pertama. tail -c 0/ tail -c +0untuk itu akan jauh lebih tidak intuitif.
Stéphane Chazelas
2
@deviantfan: (dd bs=1 count=3 of=/dev/null; cat) <input >output. Atau dengan GNU (head -c3 >/dev/null; cat)- bahkan di UTF8 atau lokal non-singlebyte lainnya; GNU head melakukan 'char' = byte.
dave_thompson_085
20

Menggunakan VIM

  1. Buka file dalam VIM:

    vi text.xml
    
  2. Hapus pengodean BOM:

    :set nobomb
    
  3. Simpan dan keluar:

    :wq
    
Joshua Pinter
sumber
Anehnya dengan vim 8 pada mac, saya memiliki file csv utf-8 yang dibuat oleh Excel dan dimulai dengan <feff>, namun :set nobombtidak mengubah atau menghapusnya.
dlamblin
5

Anda dapat gunakan

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

untuk menghapus tanda urutan byte dari awal file, jika ada, serta mengubah baris baru CR LF menjadi LF saja. The LANG=C LC_ALL=Cmemberitahu shell Anda ingin perintah untuk menjalankan dalam default C lokal (juga dikenal sebagai default POSIX lokal), di mana tiga byte membentuk Byte Order Mark diperlakukan sebagai byte. The -ipilihan untuk sed berarti di tempat. Jika Anda menggunakan -i.old, maka sed menyimpan file asli sebagai filename.old, dan file baru (dengan modifikasi, jika ada) sebagai filename.


Saya pribadi suka memiliki ini sebagai ~/bin/fix-ms; misalnya, sebagai

#!/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

jadi jika saya perlu menerapkan ini untuk mengatakan semua file sumber C dan header (kode lama saya dari era MS-DOS, misalnya!), saya hanya menjalankan

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

atau, jika saya hanya ingin melihat file seperti itu, tanpa memodifikasinya, saya dapat menjalankannya

~/bin/ms-fix < filename | less

dan tidak melihat jelek <U+FEFF>di terminal UTF-8 saya.

Hewan Nominal
sumber
Kenapa tidak sederhana saja sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//' -i -- "$@"?
Stéphane Chazelas
@ StéphaneChazelas: Karena saya ingin skrip segera keluar jika ada masalah dengan penggantinya, yang sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//' -i -- "$@"tidak berhasil; itu mengembalikan kode keluar, tetapi memproses semua file yang terdaftar dalam daftar argumen sebelum keluar.
Hewan Nominal
@ StéphaneChazelas: --Sebelum nama file, tentu saja, penting: tanpa itu, nama file yang dimulai dengan tanda hubung dapat dianggap sebagai opsi oleh sed. Saya mengeditnya menjadi jawaban saya; terima kasih atas pengingatnya!
Hewan Nominal
0

Baru-baru ini saya menemukan alat baris perintah kecil ini yang menambah atau menghapus BOM pada file UTF-8 yang disandikan arbiter : UTF BOM Utils ( tautan baru di github)

Sedikit kekurangan, Anda hanya dapat mengunduh kode sumber C ++ polos. Anda harus membuat makefile (dengan CMake , misalnya) dan mengompilasinya sendiri, biner tidak disediakan di halaman ini.

Wernfried Domscheit
sumber