У меня есть текстовый файл с двумя байты не 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 оказываются ведущими байтами (они существуют во всем моем файле, хотя, по-видимому, всегда в начале line).
Я пытаюсь удалить эти байты с помощью 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.
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
Это приведет к удалению всех строк, которые начинаются с определенных байтов. FF FE
sed -e 's/\xff\xfe//g' hexquestion.txt
Причина, по которой ваши инвертированные регулярные выражения не работают, заключается в том, что [] указывает класс символов. sed предполагает определенный набор символов, вероятно, ascii. Эти символы в вашем файле не являются 7-битными символами ascii, поскольку оба они начинаются с F. sed не знает, как с ними бороться. В приведенном выше решении не используются классы символов, поэтому оно должно быть более переносимым между платформами и наборами символов.
Байты FF
и FE
в начале вашего файла - это так называемая "метка порядка байтов (BOM)". Он может появляться в начале текстовых потоков Unicode, чтобы указать на конечность текста. FF FE
указывает на UTF-16 в Little Endian
Вот выдержка из FAQ:
Q: Как мне следует обращаться с BOMами?
A: Вот некоторые рекомендации:
- Определенный протокол (например, соглашения Microsoft для
.txt
файлов) может потребовать использования BOM в определенных потоках данных Unicode, таких как файлы. Когда вам нужно соответствовать такому протоколу, используйте BOM.- Некоторые протоколы допускают необязательные BOM в случае немаркированного текста. В этих случаях,
- Если известно, что поток текстовых данных является обычным текстом, но неизвестна кодировка, BOM может использоваться в качестве подписи. Если BOM отсутствует, кодировка может быть любой.
- Если известно, что текстовый поток данных является обычным текстом Unicode (но не известно, в какой кодировке), то в качестве подписи можно использовать BOM. Если BOM нет, то текст следует интерпретировать как big-endian.
- Некоторые байт-ориентированные протоколы ожидают ASCII-символы в начале файла. Если в этих протоколах используется UTF-8, следует избегать использования BOM в качестве подписи формы кодирования.
- Если точный тип потока данных известен (например, Unicode big-endian или Unicode little-endian), BOM не следует использовать. В частности, когда поток данных объявлен как UTF-16BE, UTF-16LE, UTF-32BE или UTF-32LE, BOM не должен использоваться.
Вы можете получить шестнадцатеричные коды с помощью \ xff \ xfE и ничего не заменить.
Чтобы показать, что это не проблема спецификации Unicode, а проблема восьмибитных символов по сравнению с семибитными и привязана к языку, попробуйте следующее:
Показать все байты:
$ 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
удалил символы, которые не являются буквенно-цифровыми в локали C. Обратите внимание, что остается только «123abc»:
$ printf '123 abc\xff\xfe\x7f\x80'|LANG=C sed 's/[^[:alnum:]]//g' | hexdump -C
00000000 31 32 33 61 62 63 |123abc|
В OS X метка порядка байтов, вероятно, читается как одно слово. Попробуйте sed 's/^\xfffe//g'
или sed 's/^\xfeff//g'
в зависимости от эндиана.
В качестве альтернативы вы можете использовать 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