Я пытаюсь заменить строку в Makefile на Mac OS X для кросс-компиляции на iOS. Строка имеет двойные кавычки. Команда:
sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure
И ошибка:
sed: RE error: illegal byte sequence
Я пытался избежать двойных кавычек, запятых, тире и двоеточий без радости. Например:
sed -i "" 's|\"iphoneos-cross\"\,\"llvm-gcc\:\-O3|\"iphoneos-cross\"\,\"clang\:\-Os|g' Configure
У меня чертовски много времени на отладку проблемы. Кто-нибудь знает, как заставить sed
печатать позицию недопустимой последовательности байтов? Или кто-нибудь знает, что такое недопустимая последовательность байтов?
LC_CTYPE=C && LANG=C && sed command
LANG
вещь. Вздох ....sed
(как также используется в OS X) требует-i ''
(отдельный параметр-аргумент с пустой строкой) для обновления на месте без файла резервной копии; с GNUsed
, только-i
само по себе работает - см. stackoverflow.com/a/40777793/45375Ответы:
Пример команды, которая демонстрирует симптом:
sed 's/./@/' <<<$'\xfc'
терпит неудачу, потому что байт0xfc
не является допустимым символом UTF-8.Обратите внимание, что, напротив, GNU
sed
(Linux, но также устанавливается на macOS) просто пропускает недействительный байт, не сообщая об ошибке.Использование ранее принятого ответа - вариант, если вы не против потерять поддержку своего истинного языка (если вы работаете в системе США и вам никогда не нужно иметь дело с иностранными символами, это может быть хорошо).
Однако тот же эффект можно получить Ad-Hoc для одной команды только :
Примечание: важен эффективный
LC_CTYPE
параметрC
, поэтомуLC_CTYPE=C sed ...
он также обычно работает, но еслиLC_ALL
он установлен (что-то иное, чемC
), он переопределит отдельныеLC_*
переменные -категории, такие какLC_CTYPE
. Таким образом, наиболее надежный подход заключается в установкеLC_ALL
.Тем не менее, (фактически) настройки
LC_CTYPE
дляC
обрабатывают строки , как если бы каждый байт был свой характер ( не интерпретации , основанная на правилах кодирования выполняется), причем без учета для - многобайтового по требованию - UTF-8 , кодирующие , что OS X использует по умолчанию где иностранные символы имеют многобайтовые кодировки .В двух словах: заходящих
LC_CTYPE
наC
причины оболочку и утилиты только распознавать основные английские буквы как буквы (те , в 7-битном диапазоне ASCII), так что иностранные гольцы. не будут рассматриваться как буквы , что приведет, например, к неудачному преобразованию в верхний / нижний регистр.Опять же, это может быть хорошо, если вам не нужно сопоставлять многобайтовые символы, такие как
é
, и просто хотите пропустить такие символы .Если этого недостаточно и / или вы хотите понять причину исходной ошибки (включая определение того, какие входные байты вызвали проблему) и выполнить преобразования кодирования по требованию, читайте далее.
Проблема в том, что кодировка входного файла не совпадает с кодировкой оболочки.
В частности, входной файл содержит символы, закодированные таким образом, который недопустим в UTF-8 (как @Klas Lindbäck заявил в комментарии) - вот что
sed
пытается сказать сообщение об ошибкеinvalid byte sequence
.Скорее всего, ваш входной файл использует однобайтовую 8-битную кодировку, например
ISO-8859-1
, часто используемую для кодирования "западноевропейских" языков.Пример:
Буква с акцентом
à
имеет код Unicode0xE0
(224) - такой же, как и вISO-8859-1
. Однако из - за характера UTF-8 кодировке, этот единственный элемент кода представлен в виде 2 -х байт -0xC3 0xA0
, в то время как пытается передать один байт0xE0
является недействительным в соответствии с UTF-8.Вот демонстрация проблемы с использованием строки,
voilà
закодированной какISO-8859-1
, сà
представленным в виде одного байта (через строку bash в кавычках ANSI-C ($'...'
), которая используется\x{e0}
для создания байта):Обратите внимание, что эта
sed
команда по сути является no-op, которая просто пропускает ввод, но она нам нужна, чтобы вызвать ошибку:Чтобы просто игнорировать проблему ,
LCTYPE=C
можно использовать вышеуказанный подход:Если вы хотите определить, какие части ввода вызывают проблему , попробуйте следующее:
Вывод покажет вам все байты с установленным старшим битом (байты, которые превышают 7-битный диапазон ASCII) в шестнадцатеричной форме. (Тем не менее, обратите внимание, что это также включает в себя правильно закодированные многобайтовые последовательности UTF-8 - для более точной идентификации байтов invalid-in-UTF-8 потребуется более сложный подход.)
Выполнение кодирования преобразований по требованию :
Стандартная утилита
iconv
может использоваться для преобразования в (-t
) и / или из (-f
) кодировок;iconv -l
перечисляет все поддерживаемые.Примеры:
Преобразование FROM
ISO-8859-1
в действующую кодировку в оболочке (на основеLC_CTYPE
, котораяUTF-8
по умолчанию -base), основываясь на приведенном выше примере:Обратите внимание, что это преобразование позволяет вам правильно сопоставлять иностранные символы :
Чтобы преобразовать ввод BACK
ISO-8859-1
после обработки, просто передайте результат в другуюiconv
команду:источник
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'
печатаетsed: RE error: illegal byte sequence
для меня на Сьерре.echo $LC_ALL
выходыen_US.UTF-8
FWIW.LC_ALL
переопределяет все остальныеLC_*
переменные, в том числеLC_CTYPE
, как объяснено в ответе.Добавьте следующие строки в ваш
~/.bash_profile
или~/.zshrc
файл (ы).источник
LC_CTYPE
для того, чтобыC
каждый байт в строках был своим собственным символом без применения каких-либо правил кодирования. Поскольку нарушение правил кодирования (UTF-8) вызвало первоначальную проблему, это устраняет проблему. Однако цена, которую вы платите, заключается в том, что оболочка и утилиты распознают только буквы английского алфавита (те, что в 7-битном диапазоне ASCII). Смотрите мой ответ для более.LC_CTYPE=C sed …
, т.е. только по команде sed.Мой обходной путь использовал Perl:
источник
Ответ mklement0 великолепен, но у меня есть небольшие хитрости.
Кажется хорошей идеей явно указать
bash
кодировку при использованииiconv
. Кроме того, нам следует добавить метку порядка байтов ( даже если стандарт Юникода не рекомендует этого ), потому что между UTF-8 и ASCII могут быть допустимые путаницы без метки порядка байтов . К сожалению,iconv
не предшествует метке порядка байтов, когда вы явно указываете порядковый номер (UTF-16BE
илиUTF-16LE
), поэтому нам нужно использоватьUTF-16
, который использует платформенно-зависимый порядковый номер, а затем использоватьfile --mime-encoding
для обнаружения истинногоiconv
использованного порядкового номера .(Я пишу все мои кодировки в верхнем регистре, потому что, когда вы перечисляете все
iconv
поддерживаемые кодировкиiconv -l
, все они в верхнем регистре.)источник
file -b --mime-encoding
для обнаружения и сообщения о кодировке файла. Однако есть некоторые аспекты, на которые стоит обратить внимание, о чем я расскажу в отдельных комментариях.LC_CTYPE
обычно значением по умолчанию является значение<lang_region>.UTF-8
, поэтому любой файл без BOM (метка порядка байтов) поэтому интерпретируется как файл UTF-8. Только в мире Windows используется псевдо-спецификация0xef 0xbb 0xff
; по определению UTF-8 не нуждается в спецификации и не рекомендуется (как вы заявляете); вне мира Windows эта псевдо-BOM приводит к поломке .Unfortunately, iconv doesn't prepend a byte-order mark when you explicitly specify an endianness (UTF-16BE or UTF-16LE)
: это сделано по замыслу: если вы указываете порядковый номер явно , нет необходимости также отражать его через спецификацию, поэтому ничего не добавляется.LC_*
/LANG
переменные:bash
,ksh
, иzsh
(возможно , и другие, но неdash
) уважают кодировку; проверьте в POSIX-подобных оболочках с языком на основе UTF-8 сv='ä'; echo "${#v}"
: оболочка с поддержкой UTF-8 должна сообщать1
; т.е. он должен распознавать многобайтовую последовательностьä
(0xc3 0xa4
) как один символ. Возможно , что еще более важно, однако: стандартные утилиты (sed
,awk
,cut
...) также должны быть локаль / кодирующей-курс, и в то время как большинство из них на современных Unix-подобные платформы, есть исключения, например,awk
на OSX, иcut
в линуксе.file
распознается псевдо-BOM UTF-8, но проблема в том, что большинство утилит Unix, которые обрабатывают файл, этого не делают , и обычно ломаются или, по крайней мере, плохо себя ведут , когда сталкиваются с ним. Без спецификацииfile
правильно идентифицирует все 7-битные байтовые файлы как ASCII, а файл с действительными многобайтовыми символами UTF-8 - как UTF-8. Прелесть UTF-8 в том, что он является надмножеством ASCII: любой действительный файл ASCII по определению является действительным файлом UTF-8 (но не наоборот); совершенно безопасно обрабатывать ASCII-файл как UTF-8 (технически это так, он не содержит многобайтовых символов)Вы просто должны передать команду iconv перед командой sed . Например, с вводом file.txt:
Опция -f - это кодовый набор from, а опция -t - преобразование кодового набора в.
Позаботьтесь о случае, веб - страницы , как правило , показывают , что в нижнем регистре , как <кодировка = ISO-8859-1" /> и Iconv верхнего регистра. У вас есть список Iconv поддерживаемых кодировок в вас система с командой Iconv -l
UTF8-MAC - это современный OS Mac кодовый набор для конвертации.
источник
Я получил часть способа ответить на вышесказанное, просто используя tr .
У меня есть файл .csv, который является выпиской по кредитной карте, и я пытаюсь импортировать его в Gnucash. Я живу в Швейцарии, поэтому мне приходится иметь дело с такими словами, как Цюрих. Подозреваю, что Gnucash не любит "" в числовых полях, я решил просто заменить все
с участием
Поехали:
Я использовал od, чтобы пролить свет: обратите внимание на 374 на полпути вниз по этому выводу od -c
Тогда я подумал, что мог бы попытаться убедить tr заменить 374 на любой правильный байт-код. Итак, сначала я попробовал что-то простое, которое не сработало, но побочным эффектом показало, где находится проблемный байт:
Вы можете увидеть Tr поручительств в 374 символа.
Использование Perl, кажется, позволяет избежать этой проблемы.
источник
Мой обходной путь использовал гну
sed
. Работал нормально для моих целей.источник
sed
является опцией, если вы хотите игнорировать недопустимые байты во входном потоке (нет необходимости вLC_ALL=C sed ...
обходном пути), потому что GNUsed
просто пропускает недопустимые байты вместо сообщения об ошибке, но учтите, что если вы хотите правильно распознать и обработать все символов во входной строке, сначала нет способа изменить кодировку ввода (обычно с помощьюiconv
).