RE hatası: Mac OS X'te geçersiz bayt sırası


184

İOS'a çapraz derleme için Mac OS X'te Makefile'de bir dizeyi değiştirmeye çalışıyorum. Dizede çift tırnak işareti bulunur. Komut:

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

Ve hata:

sed: RE error: illegal byte sequence

Ben çift tırnak, virgül, tire ve iki nokta üst üste sevinç olmadan kaçmayı denedim. Örneğin:

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

Sorunu ayıklamak için zaman geçiriyorum. Herkes sedyasadışı bayt sırasının konumunu yazdırmak için nasıl bilebilir ? Yoksa yasadışı bayt dizisinin ne olduğunu bilen var mı?


2
Geçersiz bayt dizisi, 8-bit ascii'yi utf-8'i bekleyen bir şeyle beslerken aldığınız bir şey gibi geliyor.
Klas Lindbäck

36
Deneyebilir misin:LC_CTYPE=C && LANG=C && sed command
Anubhava

5
Teşekkürler millet. Onun idi LANGşey. İç
geçirin

3
@ user2719058: BSD sed(OS X'te de kullanıldığı gibi), -i ''yedek dosya olmadan yerinde güncelleme için (ayrı, boş dize seçenek bağımsız değişkeni) gerektirir; GNU ile sed, sadece -ikendi başına çalışır - bkz. stackoverflow.com/a/40777793/45375
mklement0

1
Artı bir LANG şey için. İyi keder, belirsiz, açık olmayan ve şaşırtıcı derecede araştırılması zor.
Spudley

Yanıtlar:


301

Belirti gösteren bir örnek komut: sed 's/./@/' <<<$'\xfc'başarısız olur, çünkü bayt 0xfcgeçerli bir UTF-8 karakteri değildir.
Bunun tersine, GNU'nun sed (Linux, ancak macOS'a da yüklenebilir) bir hata bildirmeden geçersiz baytı ilettiğini unutmayın.

Daha önce kabul edilen cevabı kullanmak, gerçek yerel ayarınız için destek kaybetmemeniz durumunda bir seçenektir (bir ABD sistemindeyseniz ve asla yabancı karakterlerle uğraşmanız gerekmiyorsa, bu iyi olabilir.)

Ancak, aynı etki yalnızca tek bir komut için geçici olabilir :

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

Not: Önemli olan bir olan etkili LC_CTYPE ayarı C, bu nedenle LC_CTYPE=C sed ...olur normalde eğer ayrıca iş, ama LC_ALL(dışında bir set olur C), bireysel geçersiz kılar LC_*gibi -Kategoriler değişkenleri LC_CTYPE. Böylece, en sağlam yaklaşım ortaya koymaktır LC_ALL.

Ancak, (etkili) ayarı LC_CTYPEiçin Cdavranır dizeleri her bayt olsaydı kendi karakter olarak ( hayır ile kodlama kurallara dayalı yorumlama yapılır), hiçbir konuda için - multibyte-on-demand - UTF-8 kodlama OS X varsayılan olarak istihdam ettiğini burada yabancı karakterlerin çok baytlı kodlamaları vardır .

Özetle: ayar LC_CTYPEiçinC nedenler sadece temel İngilizce harfleri harfleri (7 bitlik ASCII aralığında olanlar) tanımak kabuk ve yardımcı programları, böylece yabancı karakter. harf olarak değerlendirilmez ve örneğin büyük / küçük harf dönüşümlerinin başarısız olmasına neden olur.

Yine, gibi çok baytlı kodlanmış karakterlerle eşleşmeniz gerekmiyorsa éve bu karakterleri iletmek istiyorsanız bu iyi olabilir .

Bu yetersizse ve / veya orijinal hatanın nedenini (soruna hangi girdi baytlarının neden olduğunu belirleme dahil) anlamak ve talep üzerine kodlama dönüşümleri gerçekleştirmek istiyorsanız , aşağıda okuyun .


Sorun, girdi dosyasının kodlamasının kabuğunkiyle eşleşmemesidir.
Daha spesifik olarak, girdi dosyası UTF-8'de geçerli olmayan bir şekilde kodlanmış karakterler içerir (bir açıklamada @Klas Lindbäck belirtildiği gibi) - sedhata mesajının söylemeye çalıştığı şey budur invalid byte sequence.

Büyük olasılıkla, giriş dosyası kullanır kodlayan tek bayt 8-bit gibi ISO-8859-1sık kodlamak "Batı Avrupa" dil için kullanılabilir.

Misal:

Aksanlı harf àUnicode kod noktasına 0xE0(224) sahiptir - ile aynıdır ISO-8859-1. Bununla birlikte, doğası gereği , UTF-8 kodlama bu tek kod noktası olarak temsil edilir 2 bayt - 0xC3 0xA0, geçmeye çalışırken ise tek bir bayt 0xE0 olup , geçersiz UTF-8 markası altında.

Aşağıda , bir bayt olarak temsil edilen ( bayt oluşturmak için kullanılan bir ANSI-C-alıntılı bash dizesi ( ) aracılığıyla) olarak kodlanan dizeyi kullanan sorunun bir gösterimi verilmiştir :voilàISO-8859-1à$'...'\x{e0}

O Not sedkomut sadece içinden girişini geçer no-op etkin bir, ama biz o hatayı kışkırtmak gerekir:

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

Sorunu basitçe göz ardı etmek için yukarıdaki LCTYPE=Cyaklaşım kullanılabilir:

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

Sorunun hangi bölümlerin soruna neden olduğunu belirlemek istiyorsanız aşağıdakileri deneyin:

  # 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}'

Çıktı, onaltılı biçimde yüksek bit ayarlı (7 bit ASCII aralığını aşan bayt) tüm baytları gösterir. (Bununla birlikte, bunun doğru şekilde kodlanmış UTF-8 çok baytlı dizileri de içerdiğine dikkat edin - UTF-8 baytında geçersiz olanları özel olarak tanımlamak için daha karmaşık bir yaklaşıma ihtiyaç duyulacaktır.)


İstek üzerine kodlama dönüşümleri gerçekleştirme :

Standart yardımcı program iconv( -t) ve / veya ( -f) kodlamalarına dönüştürmek için kullanılabilir ; iconv -ldesteklenenlerin tümünü listeler.

Örnekler:

Dönüştüğünde ISO-8859-1(göre kabuğu etkisi kodlama LC_CTYPEolup, UTF-8yukarıdaki örnek üzerine inşa varsayılan tabanlı):

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

Bu dönüşümün yabancı karakterleri doğru şekilde eşleştirmenize izin verdiğini unutmayın :

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

ISO-8859-1İşlemden sonra BACK girişini dönüştürmek için sonucu başka bir iconvkomuta yönlendirmeniz yeterlidir :

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

4
Bunun çok daha iyi bir seçenek olduğunu söyleyebilirim. İlk olarak, tüm Terminallerde çoklu dil desteğini kaybetmek istemem. İkincisi, kabul edilen cevap yerel bir soruna küresel bir çözüm gibi geliyor - kaçınılması gereken bir şey.
Alex

Bunun için birkaç küçük değişiklik yaptım. Geri bildirimi takdir ediyorum. stackoverflow.com/a/35046218/9636
Heath Borders

LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'sed: RE error: illegal byte sequenceSierra'da benim için baskı yapıyor. FWIW echo $LC_ALLçıkışları en_US.UTF-8.
ahcox

1
@ahcox: Evet, çünkü ayar yanıtta açıklandığı gibi diğer tüm değişkenleri LC_ALL geçersiz kılar . LC_*LC_CTYPE
mklement0

2
@ mklement0 Harika, bu işe yarıyor: "LC_ALL = C sed 's /.*/&/' <<< $ 'voil \ x {e0}'". Öncelik, dikkatsiz cahillerim
ahcox

142

Aşağıdaki satırları dosyalarınıza ~/.bash_profileveya ~/.zshrcdosyalarınıza ekleyin .

export LC_CTYPE=C 
export LANG=C

29
aslında işe yarıyor, ama nedenini açıklayabilir misiniz?
Hoang Pham

11
@HoangPham: Ayar LC_CTYPEiçin Cdizelerinde nedenleri her byte herhangi bir kodlama kuralları uygulamadan kendi karakteri olmak. (UTF-8) kodlama kurallarının ihlali orijinal soruna neden olduğundan, bu sorunu ortadan kaldırır. Ancak, ödediğiniz fiyat, kabuk ve yardımcı programların yalnızca temel İngilizce harflerini (7 bit ASCII aralığındaki harfleri) harf olarak tanımasıdır. Daha fazla bilgi için cevabımı görün.
mklement0

6
Kabuğunuzun başlangıç ​​dosyalarında bunu kalıcı olarak ayarlamak, birçok yararlı davranışı devre dışı bırakır. Bunu sadece kesinlikle gerektiren bireysel komutlar için koymak istiyorsunuz.
Üçlü

4
Çok tehlikeli, beklenmedik sonuçlara neden olabilir. Birisi LC_CTYPE=C sed …sadece sed komutunda kullanılabilir.
Yongwei Wu

2
Bu, kabuğunuzdaki Unicode karakter desteğini tamamen devre dışı bırakır. Güle güle emojileri, süslü çizgi çizme karakterleri, aksanlı harfler, .... Diğer cevaplarda açıklandığı gibi bunu sadece sed komutu için ayarlamak çok daha iyi.
asmeurer

6

Geçici çözüm Perl kullanıyordum:

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

Bu harika çalışıyor. Ve diğerlerinden farklı olarak özel karakterlerden kaçarken hata yapmadım. Önceki olanlar bana "sed: RE hatası: geçersiz bayt dizisi" veya sed: 1: "path_to_file": geçersiz komut kodu gibi sorunlar verdi.
JMags1632

3

mklement0'ın cevabı harika, ama bazı küçük ayarlamalar var.

bashKullanırken kodlamasını açıkça belirtmek iyi bir fikir gibi görünüyor iconv. Ayrıca, bayt sırası işareti ( unicode standardı bunu önermese de) başına eklemeliyiz çünkü UTF-8 ile ASCII arasında bayt sırası işareti olmadan meşru karışıklıklar olabilir . Maalesef, iconvaçıkça bir endianness ( UTF-16BEveya UTF-16LE) belirttiğinizde bir bayt sırası işareti önüne geçmez , bu nedenle UTF-16platforma özgü endianness kullanan ve daha sonra kullanılan file --mime-encodinggerçek endiannessi keşfetmek için kullanmamız gerekir iconv.

(Tüm kodlamalarımı büyük harflerle yazıyorum çünkü iconvdesteklenen tüm kodlamaları listelediğinizde iconv -lhepsi büyük harf.)

# 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
++ yararlı teknikler için, özellikle file -b --mime-encodingbir dosyanın kodlamasını keşfetmek ve raporlamak için. Bununla birlikte, ayrı yorumlarda yapacağım, ele almaya değer bazı yönler var.
mklement0

2
Unix dünyasının bu noktada UTF-8'i benimsediğini söylemek güvenli: varsayılan LC_CTYPEdeğer genellikle <lang_region>.UTF-8, bu yüzden BOM (bayt sırası işareti) olmayan herhangi bir dosya bu nedenle UTF-8 dosyası olarak yorumlanır. Sadece Windows dünyasında sözde BOM 0xef 0xbb 0xff kullanılır; tanımı gereği, UTF-8 ürün ağacına ihtiyaç duymaz ve tavsiye edilmez (siz belirttiğiniz gibi); Windows dünyasının dışında, bu sahte BOM bazı şeylerin bozulmasına neden olur .
mklement0

2
Re Unfortunately, iconv doesn't prepend a byte-order mark when you explicitly specify an endianness (UTF-16BE or UTF-16LE): bu tasarım gereğidir: endianness'i açıkça belirtirseniz, onu bir Malzeme Listesiyle de yansıtmanıza gerek yoktur, bu nedenle hiçbiri eklenmez.
mklement0

1
Re LC_*/ LANGdeğişkenleri: bash,ksh ve zsh(muhtemelen diğerleri, ama değil dash ) karakter kodlamasını saygı yapmak; POSIX benzeri kabuklarda UTF-8 tabanlı bir yerel ayar ile aşağıdakileri doğrulayın v='ä'; echo "${#v}": UTF-8 farkında bir kabuk rapor etmelidir 1; yani, çok baytlı diziyi ä( 0xc3 0xa4) tek bir karakter olarak tanımalıdır . Belki daha da önemlisi, ancak: standart programları ( sed, awk, cut, ...) ayrıca kodlayan tanıyan / yerel olmak gerekir, ve süre en onlarla modern Unix benzeri platformlardır gibi istisnalar vardır awkOSX üzerinde, ve cutLinux'ta.
mklement0

1
fileUTF-8 sözde BOM'u tanıyan övgüye değer , ancak sorun, dosyayı işleyen çoğu Unix yardımcı programının, biriyle karşılaştığında genellikle bozulmaması veya en azından yanlış davranmasıdır . BOM olmadan, filetüm 7 bitlik bir bayt dosyasını ASCII ve geçerli UTF-8 çok baytlık karakterlere sahip olan dosyayı UTF-8 olarak doğru olarak tanımlar. UTF-8'in güzelliği, ASCII'nin bir üst kümesidir : geçerli herhangi bir ASCII dosyası tanım olarak geçerli bir UTF-8 dosyasıdır (ancak tam tersi değildir); bir ASCII dosyasını UTF-8 olarak işlemek mükemmeldir (teknik olarak, sadece çok baytlı karakter içermez.)
mklement0

2

Sed komutundan önce bir iconv komutu vermeniz yeterlidir . Ex file.txt girişi ile:

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

-f seçeneği 'from' kod kümesi ve -t seçeneği 'to' kod kümesi dönüşümüdür.

Davanın özen, web sayfaları genellikle böyle küçük harfli göstermek <charset = iso-8859-1" /> ve iconv büyük harf kullanır. Sen listesi var iconv sende komutuyla sistemi desteklenen codesets iconv -l

UTF8-MAC dönüşüm için modern OS Mac kod kümesidir.


Ayrıca iconv posta listesindeki iconv ve charset adlarına bakın .
jww

1

Herkes yasadışı bayt dizisinin konumunu yazdırmak için sed almak için nasıl biliyor mu? Yoksa yasadışı bayt dizisinin ne olduğunu bilen var mı?

$ 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

Yukarıdakileri sadece kullanarak cevaplamanın yolunu buldum tr .

Kredi kartı ekstresi olan bir .csv dosyam var ve bunu Gnucash'a aktarmaya çalışıyorum. İsviçre'de yaşıyorum, bu yüzden Zürih gibi kelimelerle uğraşmak zorundayım. Şüphelendiğiniz Gnucash sayısal alanlarda "" gibi değil, sadece hepsini değiştirmeye karar verdim

; ;

ile

;;

İşte gidiyor:

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

Biraz ışık saçmak için od kullandım : 374 bu od -c çıkışının yarısına

$ 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

Sonra ben tr ikna etmeye çalışacağımı düşündüm doğru bayt kodu ne olursa olsun 374 yerine . İlk önce basit bir şey denedim, ki bu işe yaramadı, ama bana zahmetli baytın nerede olduğunu göstermenin yan etkisi vardı:

$ 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

Görebilirsin 374 karakterinde tr kefaletlerini .

Perl kullanmak bu sorunu önlüyor gibi görünüyor

$ 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

Benim geçici çözüm GNU kullanıyordum sed. Benim için iyi çalıştı.


Gerçekten de, GNU sed isterseniz bir seçenektir görmezden giriş akışı (gerek geçersiz bayt LC_ALL=C sed ...GNU çünkü geçici çözüm) sedbasitçe yoluyla geçersiz bayt geçer yerine bir hata raporlama, ancak not o düzgün süreç tüm tanımak ve isterseniz karakterleri girilirse, önce girişin kodlamasını değiştirmenin bir yolu yoktur (genellikle, ile iconv).
mklement0
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.