Як я можу видалити BOM з файлу UTF-8?


63

У мене є файл кодування UTF-8 з BOM і хочу видалити BOM. Чи є інструменти командного рядка Linux для видалення BOM з файлу?

$ file test.xml
test.xml:  XML 1.0 document, UTF-8 Unicode (with BOM) text, with very long lines


1
Я зробив надзвичайно простий інструмент зробити це саме кілька місяців тому: oskog97.com/read/?path=/small-scripts/killbom&referer=/… Можливо, варто встановити щось подібне до / usr / local / bin, якщо у вас багато файлів, закодованих UTF-8, з BOM.
Оскар ского

Відповіді:


76

Якщо ви не впевнені, чи файл містить BOM UTF-8, тоді це (якщо припустити реалізацію GNU sed) видалить BOM, якщо він існує, або не внесе жодних змін, якщо він не буде.

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

Ви також можете перезаписати наявний файл за допомогою -iпараметра:

sed -i '1s/^\xEF\xBB\xBF//' orig.txt

4
це може не працювати в utf8-локалі, але попереджаючи переопределення локалі на c або posix, завжди буде працювати.
hildred

3
@hildred Я протестував це з en_US.UTF-8локальним і він працював. Коли це не вдасться?
m13r

2
@ m13r, Це залежить від версії sed і параметрів компіляції. У випадку відмови дуже нова версія sed з класами символів Unicode приведе трибайтову послідовність у вигляді одного символу, який не відповідає три послідовності символів. Однак у такому випадку ви можете зіграти шістнадцять бітових символів. Однак це нова особливість і не є загальноприйнятою. Якщо ви хочете протестувати, рекомендую скласти останню версію.
hildred

4
Щоб виправити це для роботи з увімкненим кодом sed do LC_ALL = C sed '1s / ^ \ xEF \ xBB \ xBF //'
Джошуа

@CSM приємно, але для одного особливого випадку це не працює: Bevore: -<U+FEFF>\chapter{xxx}Після: +\chapter{xxx}^M Пояснення: Використання MS-word для друку в латекс-файлі. Латекс під Linux показує згадані помилки. Вихід - із системи git. Як я міг змінити вираз, щоб уникнути і цього особливого випадку?
Cutton Eye

64

BOM не має сенсу в UTF-8. Вони, як правило, додаються помилково зловмисним програмним забезпеченням на ОС Microsoft.

dos2unix видалить його, а також подбає про інші ідіосинкразії текстових файлів Windows.

dos2unix test.xml

17
Я погоджуюся, що BOM, кодований UTF-8, не має сенсу, але вірите чи ні, є багато людей, які вважають, що це чудова ідея, яка допомагає диференціювати UTF-8 від інших 8-бітових кодувань. Тож це справа смаку. Блокнот Windows спеціально додає BOM.
Йохан Мірен

17
Що має значення, чи має це сенс чи ні, коли контекст - це лише питання про те, як його зняти? Згідно з Вікіпедією, Блокнот вимагає від BOM розпізнавати файл як UTF-8, а Google Docs також додає його під час експорту файлу у вигляді тексту. Я сумніваюся, що вони все роблять помилково .
ilkkachu

Коментарі не для розширеного обговорення; ця розмова переміщена до чату .
terdon

1
Чи є спосіб не перетворити закінчення рядків і просто видалити BOM dos2unix?
m13r

2
@ m13r Потім у цій відповіді використовуйте сценарій sed . Це видалить лише бомбу (якщо вона існує), більше нічого не зміниться.
Стрілка

25

Можна видалити BOM з файлу tailкомандою:

tail -c +4 withBOM.txt > withoutBOM.txt

2
Чому 4? BOM має 3 байти.
deviantfan

10
@deviantfan Ось чому вам потрібно почати з 4-го байти, якщо ви хочете пропустити його.
Стефан Шазелас

9
tailвикористовує 1 індексацію на основі ?! WTF!
CodesInChaos

5
@CodesInChaos tail -c -1або tail -c 1(для чого tailзазвичай використовується) - це вміст, починаючи з останнього байта, tail -c +1починаючи з першого байта. tail -c 0/ tail -c +0бо це було б набагато неінтуїтивніше.
Стефан Шазелас

2
@deviantfan : (dd bs=1 count=3 of=/dev/null; cat) <input >output. Або з GNU (head -c3 >/dev/null; cat)- навіть у UTF8 або іншій не однобайтовій локалі; Голова GNU робить 'char' = байт.
dave_thompson_085

20

Використання VIM

  1. Відкрити файл у VIM:

    vi text.xml
    
  2. Видалити кодування BOM:

    :set nobomb
    
  3. Збережіть і вийдіть:

    :wq
    

Як не дивно з vim 8 на mac, у мене є файл csv utf-8, створений Excel, і він починається з <feff>, але :set nobombне змінює і не видаляє його.
dlamblin

5

Можна використовувати

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

для видалення позначки порядку байтів з початку файлу, якщо він є, а також перетворення будь-яких нових рядків CR LF лише в LF. LANG=C LC_ALL=CКаже оболонці ви хочете запустити команду в локалі за замовчуванням C (також відомий як POSIX локаллю за замовчуванням), де три байта , що утворюють Byte Order Mark обробляється як кількість байтів. -iВаріант СЕД означає на місці. Якщо ви використовуєте -i.old, то sed зберігає вихідний файл як filename.old, а новий файл (з модифікаціями, якщо такі є) як filename.


Мені особисто подобається, щоб це було ~/bin/fix-ms; наприклад, як

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

так що якщо мені потрібно застосувати це, щоб сказати всі вихідні файли C та заголовки (наприклад, мій старий код з епохи MS-DOS!), я просто запускаю

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

або, якщо я просто хочу переглянути такий файл, не змінюючи його, я можу запустити

~/bin/ms-fix < filename | less

і не бачити потворного <U+FEFF>в моєму терміналі UTF-8.


Чому б не просто sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//' -i -- "$@"?
Стефан Шазелас

@ StéphaneChazelas: Тому що я хочу, щоб сценарій негайно вийшов, якщо виникає проблема із заміною, що sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//' -i -- "$@"не робить; він повертає код виходу, але він обробляє всі файли, перелічені в списку аргументів, перш ніж виходити.
Номінальна тварина

@ StéphaneChazelas: Звичайно, --перш ніж ім'я файлів (файлів): без нього імена файлів, що починаються з тире, можуть вважатися варіантами sed. Я змінив їх у свою відповідь; дякую за нагадування!
Номінальна тварина

0

Нещодавно я знайшов цей крихітний інструмент командного рядка, який додає або видаляє BOM у довільних файлах, кодованих UTF-8: UTF BOM Utils ( нове посилання на github)

Невеликий недолік, ви можете завантажити лише звичайний вихідний код C ++. Ви повинні створити makefile (наприклад, із CMake ) та скласти його самостійно, бінарні файли на цій сторінці не надаються.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.