Удаление шестнадцатеричных байтов с помощью sed - нет совпадения - PullRequest
7 голосов
/ 08 августа 2010

У меня есть текстовый файл с двумя байтами, отличными от ascii (0xFF и 0xFE):

??58832520.3,ABC
348384,DEF

Гекс для этого файла:

FF FE 35 38 38 33 32 35 32 30 2E 33 2C 41 42 43 0A 33 34 38 33 38 34 2C 44 45 46

По совпадению FF и FE оказываются ведущими байтами (они существуют по всему моему файлу, хотя, по-видимому, всегда в начале строки).

Я пытаюсь удалить эти байты с помощью sed, но ничего, что я делаю, похоже, не соответствует им.

$ sed 's/[^a-zA-Z0-9\,]//g' test.csv 
??588325203,ABC
348384,DEF

$ sed 's/[a-zA-Z0-9\,]//g' test.csv 
??.

Основной вопрос: как мне удалить эти байты?
Дополнительный вопрос: два приведенных выше регулярных выражения являются прямыми отрицаниями, поэтому один из них должен логически отфильтровать эти байты, верно? Почему оба этих регулярных выражения совпадают с байтами 0xFF и 0xFE?

Обновление : прямой подход к удалению диапазона шестнадцатеричного байта (предложенный двумя ответами ниже), кажется, удаляет первый «допустимый» байт из каждой строки и оставляет те байты, которые я пытаюсь избавиться от:

$sed 's/[\x80-\xff]//' test.csv
??8832520.3,ABC
48384,DEF

FF FE 38 38 33 32 35 32 30 2E 33 2C 41 42 43 0A 34 38 33 38 34 2C 44 45 46 0A

Обратите внимание на пропущенные «5» и «3» в начале каждой строки, а новый 0A добавлен в конец файла.

Большое обновление : Эта проблема связана с системой. Проблема наблюдалась в OSX, но предложения (включая мое первоначальное утверждение sed выше) работают, как я ожидаю, в NetBSD.

Решение : Эта та же задача кажется достаточно простой через Perl:

$ perl -pe 's/^\xFF\xFE//' test.csv
58832520.3,ABC
348384,DEF

Однако я оставлю этот вопрос открытым, так как это всего лишь обходной путь, и не объясняет, в чем проблема с sed.

Ответы [ 7 ]

4 голосов
/ 08 августа 2010
sed 's/[^ -~]//g'

или, как следует из другого ответа

sed 's/[\x80-\xff]//g'

См. раздел 3.9 информационных страниц sed.Глава, озаглавленная «Побеги».

Редактировать для OSX, собственная настройка языка - en_US.UTF-8

try

LANG='' sed 's/[^ -~]//g' myfile

Это работает наздесь машина osx, я не совсем уверен, почему она не работает в UTF-8

3 голосов
/ 09 августа 2010

Это удалит все строки, начинающиеся с определенных байтов. FF FE

sed -e 's/\xff\xfe//g' hexquestion.txt

Причина, по которой ваши отрицательные регулярные выражения не работают, заключается в том, что [] указывает класс символов.sed принимает определенный набор символов, вероятно, ascii.Эти символы в вашем файле не являются 7-битными символами ascii, так как оба они начинаются с F. sed не знает, как с этим справиться.Приведенное выше решение не использует классы символов, поэтому оно должно быть более переносимым между платформами и наборами символов.

2 голосов
/ 08 августа 2010

FF и FE байтов в начале вашего файла - это то, что называется "меткой порядка байтов (BOM)".Он может появиться в начале текстовых потоков Unicode, чтобы указать порядковый номер текста.FF FE обозначает UTF-16 в Little Endian

Вот выдержка из часто задаваемых вопросов:

В: Как мне обращаться с спецификациями?

A: ЗдесьНиже приведены некоторые рекомендации:

  1. Для конкретного протокола (например, соглашения Microsoft для файлов .txt) может потребоваться использование спецификации в определенных потоках данных Unicode, таких как файлы.Если вам необходимо соответствовать такому протоколу, используйте спецификацию.
  2. Некоторые протоколы допускают использование дополнительных спецификаций в случае непомеченного текста.В этих случаях
    • Если известно, что поток текстовых данных представляет собой простой текст, но неизвестной кодировки, в качестве подписи может использоваться спецификация.Если спецификация отсутствует, кодировка может быть любой.
    • Если известно, что поток текстовых данных представляет собой простой текст в кодировке Unicode (но не с прямым порядковым номером), тогда спецификацию можно использовать в качестве подписи.Если спецификация отсутствует, текст следует интерпретировать как big-endian.
  3. Некоторые байтовые протоколы ожидают символы ASCII в начале файла.Если UTF-8 используется с этими протоколами, следует избегать использования спецификации в качестве подписи формы кодирования.
  4. Там, где известен точный тип потока данных (например, Unicode с прямым порядком байтов или Unicode с прямым порядком байтов), спецификация не должна использоваться.В частности, всякий раз, когда поток данных объявляется UTF-16BE, UTF-16LE, UTF-32BE или UTF-32LE, BOM не следует использовать.

Ссылки

См. Также

Смежные вопросы

1 голос
/ 09 августа 2010

В OS X метка порядка байтов, вероятно, читается как одно слово. Попробуйте либо sed 's/^\xfffe//g', либо sed 's/^\xfeff//g' в зависимости от порядка байтов.

0 голосов
/ 09 августа 2010

В качестве альтернативы вы можете использовать ed (1):

printf '%s\n' H $'g/[\xff\xfe]/s///g' ',p' | ed -s test.csv

printf '%s\n' H $'g/[\xff\xfe]/s///g' wq | ed -s test.csv  # in-place edit
0 голосов
/ 09 августа 2010

Чтобы показать, что это не проблема спецификации Unicode, а проблема 8-битных и 7-битных символов и привязана к локали, попробуйте следующее:

Показать все байты:

$ printf '123 abc\xff\xfe\x7f\x80' | hexdump -C
00000000  31 32 33 20 61 62 63 ff  fe 7f 80                 |123 abc....|

У sed есть символы, которые не являются буквенно-цифровыми в локали пользователя.Обратите внимание, что пробел и 0x7f удалены:

$ printf '123 abc\xff\xfe\x7f\x80'|sed 's/[^[:alnum:]]//g' | hexdump -C
00000000  31 32 33 61 62 63 ff fe  80                       |123abc...|

У sed удалены символы, которые не являются буквенно-цифровыми в локали Си.Обратите внимание, что остается только «123abc»:

$ printf '123 abc\xff\xfe\x7f\x80'|LANG=C sed 's/[^[:alnum:]]//g' | hexdump -C
00000000  31 32 33 61 62 63                                 |123abc|
0 голосов
/ 08 августа 2010

Вы можете получить шестнадцатеричные коды с помощью \ xff \ xfE и заменить их ничем.

...