RE 오류 : Mac OS X에서 잘못된 바이트 시퀀스


184

iOS 로의 크로스 컴파일을 위해 Mac OS X의 Makefile에서 문자열을 바꾸려고합니다. 문자열에는 큰 따옴표가 포함되어 있습니다. 명령은 다음과 같습니다.

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잘못된 바이트 시퀀스의 위치를 ​​인쇄하는 방법을 알고 있습니까? 아니면 불법 바이트 시퀀스가 ​​무엇인지 아는 사람이 있습니까?


2
잘못된 바이트 시퀀스는 8 비트 ASCII를 utf-8이 필요한 것으로 공급할 때 얻는 것과 같습니다.
Klas Lindbäck

36
당신은 시도 할 수 있습니다 :LC_CTYPE=C && LANG=C && sed command
anubhava

5
고마워요 그게 문제였습니다 LANG. 한숨 ..
jww

3
@ user2719058 : BSD sed(OS X에서도 사용됨 -i '')는 백업 파일없이 전체 업데이트를 위해 (별도의 빈 문자열 옵션 인수)를 필요로합니다. GNU와 sed-i작동 자체 - 볼 stackoverflow.com/a/40777793/45375
mklement0

1
LANG에 대한 하나 더. 좋은 슬픔, 그것은 모호하고, 명백하지 않으며 놀랍게도 연구하기 어렵다.
Spudley

답변:


300

sed 's/./@/' <<<$'\xfc'바이트 0xfc가 유효한 UTF-8 문자가 아니기 때문에 증상을 나타내는 샘플 명령이 실패합니다 .
대조적으로 GNU는 sed (Linux, macOS에서도 설치 가능)는 오류를보고하지 않고 유효하지 않은 바이트를 전달합니다.

실제 로케일에 대한 지원을 잃어 버리지 않으 려면 이전에 승인 된 답변을 사용하는 것이 옵션 (미국 시스템 인 경우 외국 문자를 다룰 필요가없는 경우에는 괜찮을 수 있습니다).

그러나 동일한 명령단일 명령 에만 임시 로 적용될 수 있습니다 .

LC_ALL=C sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure

참고 : 어떤 중요한 것은입니다 효과 LC_CTYPE 의 설정 C, 그래서 LC_CTYPE=C sed ...일반적으로 경우에도 작동하지만 LC_ALL(이외로 설정을 될 일 C)은 개별 우선합니다 LC_*같은 - 종류 변수를 LC_CTYPE. 따라서 가장 강력한 방법은을 설정하는 것 LC_ALL입니다.

다만, (유효) 설정 LC_CTYPEC문자열을 처리하는 각 바이트 인 경우 자신의 자로 ( 아니오 로 부호화 규칙에 기초한 해석이 수행된다) 는 관계없이 대 - 멀티 주문형 - UTF-8 인코딩 OS X는 기본적으로 이용하는 것을 여기서 외국 문자 에는 멀티 바이트 인코딩이 있습니다.

간단히 말해서 : 설정 LC_CTYPEC 원인 기본적인 영어 문자로 문자 (7 비트 ASCII 범위에있는 것)를 인식 할 수있는 쉘 및 유틸리티를, 그래서 외국 문자. 예를 들어 대문자 / 소문자 변환이 실패 하는 문자로 취급되지 않습니다.

다시 말하지만, 와 같은 멀티 바이트 인코딩 문자를 일치 시킬 필요가없고 é단순히 이러한 문자전달 하려는 경우에는 문제가 없습니다. .

이것이 불충분하거나 원래 오류 의 원인 (문제를 일으킨 입력 바이트 결정 포함) 을 이해하고 필요할 때 인코딩 변환수행 하려면 아래를 읽으 십시오.


문제는 입력 파일의 인코딩이 쉘의 인코딩과 일치하지 않는다는 것입니다.
보다 구체적으로, 입력 파일에는 UTF-8에서 유효하지 않은 방식으로 인코딩 된 문자가 포함되어 있습니다 (@Klas Lindbäck가 주석에 언급 한 바와 같이). 이것이 sed오류 메시지가에 의해 말하려고하는 것입니다 invalid byte sequence.

입력 파일은 다음과 같은 단일 바이트 8 비트 인코딩을 사용합니다.ISO-8859-1 "서유럽"언어를 인코딩하는 데 자주 사용되는 사용합니다.

예:

악센트 문자의 à유니 코드 코드 포인트 0xE0(224) 는와 동일합니다 ISO-8859-1. 그러나 UTF-8 인코딩 의 특성으로 인해이 단일 코드 포인트는 2 바이트 로 표시 0xC3 0xA0되지만 UTF-8 에서는 단일 바이트 를 전달하려는 시도 0xE0유효하지 않습니다 .

여기서 A의 문제점을 보여주는 캐릭터를 사용 voilà로서 부호화 ISO-8859-1à같이 표시 (AN ANSI-C 인용 배시 문자열 (비아 바이트 $'...'용도가 있음) \x{e0}바이트를 작성하기가) :

주의 sed명령은 단순히 통해 입력을 전달하는 어떤 조합 효과적으로 없지만, 우리가 오류를 자극해야합니다

  # -> 'illegal byte sequence': byte 0xE0 is not a valid char.
sed 's/.*/&/' <<<$'voil\x{e0}'

단순히 문제무시 하기 위해 위의 LCTYPE=C접근 방식을 사용할 수 있습니다.

  # No error, bytes are passed through ('á' will render as '?', though).
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'

당신이 할 경우 결정하는 문제의 원인 입력의 일부 , 다음을 시도하십시오

  # Convert bytes in the 8-bit range (high bit set) to hex. representation.
  # -> 'voil\x{e0}'
iconv -f ASCII --byte-subst='\x{%02x}' <<<$'voil\x{e0}'

출력은 16 비트 형식으로 상위 비트 세트 (7 비트 ASCII 범위를 초과하는 바이트)를 갖는 모든 바이트를 표시합니다. (그러나 정확하게 인코딩 된 UTF-8 멀티 바이트 시퀀스도 포함합니다. UTF-8에서 유효하지 않은 UTF-8 바이트를 구체적으로 식별하려면보다 정교한 접근 방식이 필요합니다.)


요청시 인코딩 변환 수행 :

표준 유틸리티 iconv를 사용하여 ( -t) 및 / 또는 ( -f) 인코딩 을 변환 할 수 있습니다 . iconv -l지원되는 모든 것을 나열합니다.

예 :

변환 ISO-8859-1(기반 셸에 적용되는 인코딩 LC_CTYPE되며, UTF-8위의 예제에 기본적으로 기반) :

  # Converts to UTF-8; output renders correctly as 'voilà'
sed 's/.*/&/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')"

변환을 통해 외래 문자를 올바르게 일치시킬 수 있습니다 .

  # Correctly matches 'à' and replaces it with 'ü': -> 'voilü'
sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')"

ISO-8859-1처리 후 입력 BACK을 변환하려면 결과를 다른 iconv명령으로 파이프하면됩니다 .

sed 's/à/ü/' <<<"$(iconv -f ISO-8859-1 <<<$'voil\x{e0}')" | iconv -t ISO-8859-1

4
나는 이것이 훨씬 더 나은 옵션이라고 말하고 싶다. 첫째, 모든 터미널에서 다국어 지원을 잃고 싶지 않습니다. 둘째, 받아 들여진 대답은 지역 문제에 대한 세계적인 해결책 인 것 같습니다. 피해야 할 것입니다.
Alex

나는 이것에 몇 가지 작은 조정이있었습니다. 의견을 보내 주셔서 감사합니다. stackoverflow.com/a/35046218/9636
Heath Borders

LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'sed: RE error: illegal byte sequence시에라에 나를 위해 인쇄 합니다. FWIW를 echo $LC_ALL출력 en_US.UTF-8합니다.
ahcox

1
@ahcox : 예 . 대답에서 설명한대로 설정을 포함하여 다른 모든 변수를 LC_ALL 무시 하기 때문 입니다. LC_*LC_CTYPE
mklement0

2
@ mklement0 Cool, 작동합니다 : "LC_ALL = C sed 's /.*/&/'<<< $ 'voil \ x {e0}'". 내 동료 부주의 한 무시에 대한 우선 순위는 여기에서 설명했다 : pubs.opengroup.org/onlinepubs/7908799/xbd/envvar.html
ahcox

142

다음 줄을 파일 ~/.bash_profile이나 ~/.zshrc파일에 추가하십시오 .

export LC_CTYPE=C 
export LANG=C

29
실제로 작동하지만 이유를 설명해 주시겠습니까?
Hoang Pham

11
@HoangPham : 인코딩 규칙을 적용하지 않고 문자열의 각 바이트가 고유 문자가되도록 설정 LC_CTYPE합니다 C. (UTF-8) 인코딩 규칙을 위반하면 원래 문제가 발생 했으므로 문제가 해결됩니다. 그러나 쉘과 유틸리티는 기본 영어 문자 (7 비트 ASCII 범위의 문자) 만 문자로 인식합니다. 자세한 내용은 내 대답을 참조하십시오.
mklement0

6
쉘의 시작 파일에서 이것을 영구적으로 설정하면 많은 유용한 동작이 비활성화됩니다. 이것을 절대적으로 요구하는 개별 명령에 대해서만 이것을 넣고 싶습니다.
tripleee

4
너무 위험하면 예기치 않은 결과가 발생할 수 있습니다. LC_CTYPE=C sed …sed 명령에서만 사용할 수 있습니다 .
Yongwei Wu

2
이것은 쉘에서 유니 코드 문자에 대한 지원을 완전히 비활성화합니다. 작별 이모 지, 멋진 선 그리기 문자, 악센트가있는 문자 등 .. 다른 답변에서 설명한대로 sed 명령에 대해서만 설정하는 것이 훨씬 좋습니다.
asmeurer

6

내 해결 방법은 Perl을 사용하는 것입니다.

find . -type f -print0 | xargs -0 perl -pi -e 's/was/now/g'

이것은 잘 작동합니다. 그리고 나는 다른 것과 달리 특수 문자를 이스케이프하는 오류가 없었습니다. 앞의 것들에서 "sed : RE error : illegal byte sequence"또는 sed : 1 : "path_to_file": invalid command code와 같은 문제가 발생했습니다.
JMags1632

3

mklement0의 대답 은 훌륭하지만 약간의 조정이 있습니다.

bash사용할 때 명시 적으로 인코딩을 지정하는 것이 좋습니다 iconv. 또한 바이트 순서 표시 없이 UTF-8과 ASCII간에 합법적 인 혼동이있을 수 있으므로 바이트 순서 표시를 앞에 추가해야합니다 ( 유니 코드 표준에서는 권장하지 않더라도 ) . 불행히도 엔디안 ( 또는 ) 을 명시 적으로 지정할 때 바이트 순서 표시를 앞에 두지 않으므로 플랫폼 별 엔디안을 사용 하는 을 사용해야 합니다. 그런 다음 사용 하여 실제 엔디안을 찾습니다 .iconvUTF-16BEUTF-16LEUTF-16file --mime-encodingiconv

( iconv지원되는 모든 인코딩을 나열 할 때 모두 대문자이기 때문에 모든 인코딩을 iconv -l대문자로 만듭니다.)

# Find out MY_FILE's encoding
# We'll convert back to this at the end
FILE_ENCODING="$( file --brief --mime-encoding MY_FILE )"
# Find out bash's encoding, with which we should encode
# MY_FILE so sed doesn't fail with 
# sed: RE error: illegal byte sequence
BASH_ENCODING="$( locale charmap | tr [:lower:] [:upper:] )"
# Convert to UTF-16 (unknown endianness) so iconv ensures
# we have a byte-order mark
iconv -f "$FILE_ENCODING" -t UTF-16 MY_FILE > MY_FILE.utf16_encoding
# Whether we're using UTF-16BE or UTF-16LE
UTF16_ENCODING="$( file --brief --mime-encoding MY_FILE.utf16_encoding )"
# Now we can use MY_FILE.bash_encoding with sed
iconv -f "$UTF16_ENCODING" -t "$BASH_ENCODING" MY_FILE.utf16_encoding > MY_FILE.bash_encoding
# sed!
sed 's/.*/&/' MY_FILE.bash_encoding > MY_FILE_SEDDED.bash_encoding
# now convert MY_FILE_SEDDED.bash_encoding back to its original encoding
iconv -f "$BASH_ENCODING" -t "$FILE_ENCODING" MY_FILE_SEDDED.bash_encoding > MY_FILE_SEDDED
# Now MY_FILE_SEDDED has been processed by sed, and is in the same encoding as MY_FILE

1
++ 특히 file -b --mime-encoding파일 인코딩을 발견하고보고하는 데 유용한 기술이 필요합니다 . 그러나 해결해야 할 몇 가지 측면이 있지만 별도의 의견으로 할 것입니다.
mklement0

2
유닉스 세계가 현재 UTF-8을 받아 들였다고 말하는 것이 안전하다고 생각합니다. 기본값 LC_CTYPE은 보통 <lang_region>.UTF-8이므로 BOM (바이트 순서 표시)이 없는 모든 파일 은 UTF-8 파일로 해석됩니다. pseudo-BOM 이 사용되는 것은 Windows 환경 에서만입니다 . 정의상 UTF-8은 BOM이 필요 하지 않으며 (현상태대로) 권장하지 않습니다. 윈도우 세계 밖에,이 의사 BOM은 물건을 야기 휴식 . 0xef 0xbb 0xff
mklement0

2
Re Unfortunately, iconv doesn't prepend a byte-order mark when you explicitly specify an endianness (UTF-16BE or UTF-16LE): 의도적으로 설계된 것입니다. endianness를 명시 적으로 지정하면 BOM을 통해이를 반영 할 필요가 없으므로 추가되지 않습니다.
mklement0

1
Re LC_*/ LANG변수 : bash, kshzsh(아마도 다른 것은 아니지만 dash ) 문자 인코딩을 존중합니다. UTF-8 기반 로케일이있는 POSIX 유사 쉘에서 다음을 확인 v='ä'; echo "${#v}"하십시오. UTF-8 인식 쉘이보고해야합니다 1. 즉, 멀티 바이트 시퀀스 ä( 0xc3 0xa4)를 단일 문자 로 인식해야 합니다. 아마도 더욱 중요한 것은 다음 표준 유틸리티가 ( sed, awk, cut, ...) 또한 인코딩 인식 / 로케일 할 필요가 있고, 반면 대부분 에 그들의 현대 유닉스 플랫폼이 같은 예외가 있습니다 awkOSX에가, 그리고 cut리눅스에서.
mklement0

1
그것은 그 칭찬의 file가 UTF-8 의사 BOM을 인식하지만 문제는 처리 파일이 수행하는 것이 대부분의 유닉스 유틸리티입니다 없는 일에 직면했을 때 깨지거나 적어도 무례한 행동에서 일반적으로합니다. BOM이 없으면 file모든 7 비트 바이트 파일을 ASCII로, 유효한 UTF-8 멀티 바이트 문자가 UTF-8 인 파일을 올바르게 식별하십시오. UTF-8의 장점은 ASCII 의 상위 세트 라는 것입니다 . 유효한 ASCII 파일은 정의상 유효한 UTF-8 파일입니다 (반대의 경우도 마찬가지 임). ASCII 파일을 UTF-8로 취급하는 것이 안전합니다 (기술적으로는 멀티 바이트 문자는 포함되지 않습니다)
mklement0

2

sed 명령 전에 iconv 명령 을 파이프하면 됩니다. file.txt 입력이있는 Ex :

iconv -f ISO-8859-1 -t UTF8-MAC file.txt | sed 's / something / àéèêçùû / g'| .....

-f 옵션은 'from'코드 세트이고 -t 옵션은 'to'코드 세트 변환입니다.

웹 페이지는 일반적으로 <charset = iso-8859-1 "/>와 같이 소문자를 표시하고 iconv 는 대문자를 사용합니다. iconv -l 명령을 사용하여 시스템 에 iconv 지원 코드 세트 목록이 있습니다.

UTF8-MAC 는 변환을위한 최신 OS Mac 코드 세트입니다.


iconv 메일 링리스트에서 iconv 및 문자 세트 이름 도 참조하십시오 .
jww

1

누구든지 잘못된 바이트 시퀀스의 위치를 ​​인쇄하기 위해 sed를 얻는 방법을 알고 있습니까? 아니면 불법 바이트 시퀀스가 ​​무엇인지 아는 사람이 있습니까?

$ uname -a
Darwin Adams-iMac 18.7.0 Darwin Kernel Version 18.7.0: Tue Aug 20 16:57:14 PDT 2019; root:xnu-4903.271.2~2/RELEASE_X86_64 x86_64

tr 을 사용하여 위의 답변에 대한 방법의 일부를 얻었습니다 .

신용 카드 명세서 인 .csv 파일이 있는데 Gnucash로 가져 오려고합니다. 스위스에 거주하고 있으므로 취리히와 같은 단어를 다루어야합니다. Gnucash가 숫자 필드에서 ""를 좋아하지 않는 것으로 의심되면 간단히 모든 것을 대체하기로 결정합니다.

; ;

;;

간다 :

$ head -3 Auswertungen.csv | tail -1 | sed -e 's/; ;/;;/g'
sed: RE error: illegal byte sequence

나는 od 를 사용 하여 약간의 빛을 흘렸다.이 od -c 출력의 절반 아래에 374가있다.

$ head -3 Auswertungen.csv | tail -1 | od -c
0000000    1   6   8   7       9   6   1   9       7   1   2   2   ;   5
0000020    4   6   8       8   7   X   X       X   X   X   X       2   6
0000040    6   0   ;   M   Y       N   A   M   E       I   S   X   ;   1
0000060    4   .   0   2   .   2   0   1   9   ;   9   5   5   2       -
0000100        M   i   t   a   r   b   e   i   t   e   r   r   e   s   t
0000120                Z 374   r   i   c   h                            
0000140    C   H   E   ;   R   e   s   t   a   u   r   a   n   t   s   ,
0000160        B   a   r   s   ;   6   .   2   0   ;   C   H   F   ;    
0000200    ;   C   H   F   ;   6   .   2   0   ;       ;   1   5   .   0
0000220    2   .   2   0   1   9  \n                                    
0000227

그런 다음 올바른 바이트 코드가 무엇이든 tr 을 374로 대체 하도록 설득하려고 시도 할 수 있다고 생각했습니다 . 그래서 먼저 간단한 것을 시도했지만 작동하지 않았지만 번거로운 바이트가 어디에 있는지 보여주는 부작용이있었습니다.

$ head -3 Auswertungen.csv | tail -1 | tr . .  ; echo
tr: Illegal byte sequence
1687 9619 7122;5468 87XX XXXX 2660;MY NAME ISX;14.02.2019;9552 - Mitarbeiterrest   Z

374 자에서 tr bail을 볼 수 있습니다 .

펄을 사용하면이 문제를 피할 수 있습니다

$ head -3 Auswertungen.csv | tail -1 | perl -pne 's/; ;/;;/g'
1687 9619 7122;5468 87XX XXXX 2660;ADAM NEALIS;14.02.2019;9552 - Mitarbeiterrest   Z?rich       CHE;Restaurants, Bars;6.20;CHF;;CHF;6.20;;15.02.2019

0

내 해결 방법은 gnu 사용했습니다 sed. 내 목적을 위해 잘 작동했습니다.


실제로, GNU는 sed 당신이 원하는 경우에 옵션입니다 무시 입력 스트림합니다 (에 대한 필요에 잘못된 바이트 LC_ALL=C sed ...GNU가 있기 때문에, 해결 방법을) sed단순히 통해 잘못된 바이트를 전달 하는 대신 오류를보고 있지만, 참고 제대로 과정을 모두 인식하고 싶다면 입력 문자열에 문자가 있으면 입력 인코딩을 먼저 변경하는 방법은 없습니다 (일반적으로 iconv).
mklement0
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.