Вопрос по awk, byte-order-mark, unicode – Использование awk для удаления метки порядка байтов

101

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

Спецификация:

print every line after the first (NR > 1) for the first line: If it starts with #FE #FF or #FF #FE, remove those and print the rest

Ваш Ответ

5   ответов
122

Использование GNUsed (в 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 или FreeBSDsed:-i Параметр означает «на месте» и будет обновлять файлы без необходимости перенаправлений или странных уловок.

На Mac:

этоawk solution in another answer works, ноsed Команда выше не работает. По крайней мере, на Mac (Сьерра)sed документация не упоминает поддержку шестнадцатеричного выхода из аля\xef.

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

awk '…' INFILE | sponge INFILE
Я попробовал вторую команду именно в Mac OS X, и результат был «успешным», но замены фактически не произошло.
Когда я попробовал вторую команду в OS X для файла, который использовал 0xef 0xbb 0xbf в качестве спецификации, он фактически не выполнял подстановку.
На OS X El Capitan10.11.6это не работает, но официальный ответstackoverflow.com/a/1068700/9636 работает отлично.
Стоит отметить, что эти команды заменяют одну конкретную последовательность байтов, котораяone of the possible byte-order-marks, Возможно, ваш файл имел другую последовательность спецификаций. (Я не могу помочь, кроме этого, так как у меня нет Mac)
В OSX я мог заставить это работать только через perl, как показано здесь:stackoverflow.com/a/9101056/2063546
110

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

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

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

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

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

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

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

-- ADDENDUM --

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

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 Байты спецификации из приведенной выше таблицы.

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

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

Batch Runner - & gt; Поиск (чтобы найти все файлы в подпапках) - & gt; Заменить шаблон - & gt; Бинарное удаление спецификации (для этого есть готовый шаблон поиска и замены).

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

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

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

tail -c +4 UTF8 > UTF8.nobom

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

hd -n 3 UTF8

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

hd недоступно в OS X (по состоянию на 10.8.2), поэтому для проверки там спецификации UTF-8 вы можете использовать следующее:head -c 3 file | od -t x1.
Спецификации - это 2 байта для UTF-16 и 4 байта для UTF-32, и, конечно же, они не имеют никакого отношения к UTF-8.
@KarolyHorvath Я имею в виду этоbreaks lots of programs, Разве это не то, что я сказал? Когда вы открываете поток в кодировках UTF-16 или UTF-32, декодер знает, что не нужно подсчитывать спецификацию. Когда вы используете UTF-8, декодеры представляют спецификацию как данные. Это синтаксическая ошибка в бесчисленных программах.Even Java’s decoder behaves this way, BY DESIGN! Спецификации на файлах UTF-8 неуместны и боль в заднице:they are an error! Они ломают много вещей. Даже простоcat file1.utf8 file2.utf8 file3.utf3 > allfiles.utf8 будет сломан. Никогда не используйте спецификацию на UTF-8. Период.
@KarolyHorvath Да, именно так. Его использование не рекомендуется. Это ломает вещи. Кодировка должна быть указана протоколом более высокого уровня.
@ tchrist: ты имеешь в виду, что он ломает сломанные вещи? :) правильные приложения должны быть в состоянии справиться с этой спецификацией.
20

В дополнение к преобразованию концов строк 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

Похожие вопросы