Использование awk для удаления метки порядка байтов

105

Как бы awk выглядеть сценарий (предположительно однострочный) для удаления спецификации ?

Технические характеристики:

  • печатать каждую строку после первой (NR > 1 )
  • для первой строки: если она начинается с #FE #FFили #FF #FE, удалите их и распечатайте остальные
Болдевин
источник

Ответы:

114

Попробуй это:

awk 'NR==1{sub(/^\xef\xbb\xbf/,"")}{print}' INFILE > OUTFILE

В первой записи (строке) удалите символы спецификации. Распечатайте каждую запись.

Или немного короче, зная, что действие по умолчанию в awk - это распечатать запись:

awk 'NR==1{sub(/^\xef\xbb\xbf/,"")}1' INFILE > OUTFILE

1 - кратчайшее условие, которое всегда оценивается как истинное, поэтому печатается каждая запись.

Наслаждайтесь!

- ДОБАВЛЕНИЕ -

Часто задаваемые вопросы по метке порядка байтов Unicode (BOM) включают следующую таблицу, в которой перечислены точные байты спецификации для каждой кодировки:

Bytes         |  Encoding Form
--------------------------------------
00 00 FE FF   |  UTF-32, big-endian
FF FE 00 00   |  UTF-32, little-endian
FE FF         |  UTF-16, big-endian
FF FE         |  UTF-16, little-endian
EF BB BF      |  UTF-8

Таким образом, вы можете увидеть, как \xef\xbb\xbfсоответствует EF BB BF UTF-8байтам спецификации из приведенной выше таблицы.

Бартош
источник
1
Кажется, что точка в середине подвыражения слишком много (по крайней мере, моя awk на это жалуется). Кроме того, это именно то, что я искал, спасибо!
Boldewyn 01
5
Однако это решение работает только для файлов в кодировке UTF-8. Для других, таких как UTF-16, см. Соответствующее представление спецификации в Википедии: en.wikipedia.org/wiki/Byte_order_mark
Boldewyn 01
2
Итак: awk '{if(NR==1)sub(/^\xef\xbb\xbf/,"");print}' INFILE > OUTFILEи убедитесь, что INFILE и OUTFILE разные!
Стив Клэй,
1
Если вы использовали, perl -i.orig -pe 's/^\x{FFFE}//' badfileвы можете полагаться на свои переменные PERL_UNICODE и / или PERLIO для кодирования. PERL_UNICODE = SD будет работать с UTF-8; для остальных вам понадобится PERLIO.
tchrist
1
Может быть, немного более короткая версия:awk 'NR==1{sub(/^\xef\xbb\xbf/,"")}1'
TrueY 06
122

Используя GNU sed(в Linux или Cygwin):

# Removing BOM from all text files in current directory:
sed -i '1 s/^\xef\xbb\xbf//' *.txt

На FreeBSD:

sed -i .bak '1 s/^\xef\xbb\xbf//' *.txt

Преимущество использования GNU или FreeBSD sed: -iпараметр означает «на месте» и будет обновлять файлы без необходимости перенаправления или странных уловок.

На Mac:

Это awkрешение в другом ответе работает , но приведенная sedвыше команда не работает. По крайней мере, в документации Mac (Sierra) sedне упоминается поддержка шестнадцатеричного экранирования ala \xef.

Подобного трюка можно достичь с помощью любой программы, подключивsponge инструмент к инструменту из moreutils :

awk '…' INFILE | sponge INFILE
Денилсон Са Майя
источник
5
Я попробовал вторую команду именно на Mac OS X, и результат был «успех», но на самом деле подстановки не произошло.
Trejkaz
1
Стоит отметить, что эти команды заменяют одну конкретную последовательность байтов, которая является одной из возможных меток порядка байтов . Возможно, у вашего файла была другая последовательность спецификации. (Я ничего не могу поделать, потому что у меня нет Mac)
Денилсон Са Майя
3
Когда я попробовал вторую команду в OS X для файла, который использовал 0xef 0xbb 0xbf в качестве спецификации, на самом деле замена не производилась.
Джон Уайзман,
В OSX я мог заставить это работать только через perl, как показано здесь: stackoverflow.com/a/9101056/2063546
Ян,
В OS X El Capitan 10.11.6это не работает, но официальный ответ stackoverflow.com/a/1068700/9636 работает нормально.
Хит Бордерс,
42

Не на awk, а попроще:

tail -c +4 UTF8 > UTF8.nobom

Чтобы проверить спецификацию:

hd -n 3 UTF8

Если есть спецификация, вы увидите: 00000000 ef bb bf ...

Стив Клэй
источник
6
Спецификации составляют 2 байта для UTF-16 и 4 байта для UTF-32, и, конечно, не имеют никакого отношения к UTF-8.
tchrist
2
@KarolyHorvath Да, именно так. Его использование не рекомендуется. Это ломает вещи. Кодировка должна определяться протоколом более высокого уровня.
tchrist
1
@tchrist: вы имеете в виду, что он ломает сломанные вещи? :) правильные приложения должны уметь обрабатывать эту спецификацию.
Karoly Horvath
7
@KarolyHorvath Я имею в виду, что он ломает множество программ . Разве я не так сказал? Когда вы открываете поток в кодировках UTF-16 или UTF-32, декодер не учитывает спецификацию. Когда вы используете UTF-8, декодеры представляют спецификацию как данные. Это синтаксическая ошибка в бесчисленных программах. Даже декодер Java ведет себя так, ПО ДИЗАЙНУ! Спецификации в файлах UTF-8 размещены не на своем месте, и проблема в том, что они являются ошибкой! Они ломают многое. Даже просто cat file1.utf8 file2.utf8 file3.utf3 > allfiles.utf8сломается. Никогда не используйте спецификацию в UTF-8. Период.
tchrist
6
hdне доступна на OS X (по состоянию на 10.8.2), поэтому проверить для UTF-8 BOM там вы можете использовать следующее: head -c 3 file | od -t x1.
mklement0
21

Помимо преобразования окончаний строк CRLF в LF, dos2unixтакже удаляются спецификации:

dos2unix *.txt

dos2unix также конвертирует файлы UTF-16 с спецификацией (но не файлы UTF-16 без спецификации) в UTF-8 без спецификации:

$ printf '\ufeffä\n'|iconv -f utf-8 -t utf-16be>bom-utf16be
$ printf '\ufeffä\n'|iconv -f utf-8 -t utf-16le>bom-utf16le
$ printf '\ufeffä\n'>bom-utf8
$ printf 'ä\n'|iconv -f utf-8 -t utf-16be>utf16be
$ printf 'ä\n'|iconv -f utf-8 -t utf-16le>utf16le
$ printf 'ä\n'>utf8
$ for f in *;do printf '%11s %s\n' $f $(xxd -p $f);done
bom-utf16be feff00e4000a
bom-utf16le fffee4000a00
   bom-utf8 efbbbfc3a40a
    utf16be 00e4000a
    utf16le e4000a00
       utf8 c3a40a
$ dos2unix -q *
$ for f in *;do printf '%11s %s\n' $f $(xxd -p $f);done
bom-utf16be c3a40a
bom-utf16le c3a40a
   bom-utf8 c3a40a
    utf16be 00e4000a
    utf16le e4000a00
       utf8 c3a40a
Lri
источник
3

Я знаю, что вопрос был направлен на unix / linux, подумал, что стоит упомянуть хороший вариант для unix-проблемных (в Windows с пользовательским интерфейсом).
Я столкнулся с той же проблемой в проекте WordPress (спецификация вызывала проблемы с RSS-потоком и проверкой страницы), и мне пришлось просмотреть все файлы в довольно большом дереве каталогов, чтобы найти тот, который был с спецификацией. Нашел приложение под названием Replace Pioneer и в нем:

Batch Runner -> Search (чтобы найти все файлы в подпапках) -> Replace Template -> Binary remove BOM (для этого есть готовый шаблон поиска и замены).

Это было не самое элегантное решение и требовало установки программы, что является недостатком. Но как только я узнал, что происходит вокруг меня, это сработало как шарм (и нашел 3 файла из примерно 2300, которые были с спецификацией).

Арнон Замир
источник
1
Я так счастлив, когда нашел ваше решение, однако у меня нет привилегии устанавливать программное обеспечение на компьютер компании. Сегодня потребовалось много времени, пока я не придумал альтернативу: использование Notepad ++ с плагином PythonScript. superuser.com/questions/418515/… Все равно спасибо!
Hoàng Long,