Próbuję zastąpić ciąg znaków w pliku Makefile w systemie Mac OS X w celu kompilacji krzyżowej do systemu iOS. Ciąg ma osadzone podwójne cudzysłowy. Polecenie to:
sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure
Błąd to:
sed: RE error: illegal byte sequence
Próbowałem uciec od podwójnych cudzysłowów, przecinków, myślników i dwukropków bez radości. Na przykład:
sed -i "" 's|\"iphoneos-cross\"\,\"llvm-gcc\:\-O3|\"iphoneos-cross\"\,\"clang\:\-Os|g' Configure
Mam problem z debugowaniem problemu. Czy ktoś wie, jak sed
wydrukować pozycję nielegalnej sekwencji bajtów? A może ktoś wie, co to jest nielegalna sekwencja bajtów?
LC_CTYPE=C && LANG=C && sed command
LANG
. Westchnienie ....sed
(jak również używane w OS X) wymaga-i ''
(osobnego argumentu opcji-pustego ciągu) do aktualizacji w miejscu bez pliku kopii zapasowej; z GNU działased
tylko-i
sam - patrz stackoverflow.com/a/40777793/45375Odpowiedzi:
Przykładowa komenda, która wykazuje objaw:
sed 's/./@/' <<<$'\xfc'
kończy się niepowodzeniem, ponieważ bajt0xfc
nie jest prawidłowym znakiem UTF-8.Zauważ, że dla kontrastu, GNU
sed
(Linux, ale także instalowalny na macOS) po prostu przesyła nieprawidłowy bajt bez zgłaszania błędu.Użycie poprzednio przyjętej odpowiedzi jest opcją, jeśli nie masz nic przeciwko utracie wsparcia dla prawdziwych ustawień regionalnych (jeśli korzystasz z systemu amerykańskiego i nigdy nie musisz zajmować się obcymi postaciami, może to być w porządku).
Jednak sam efekt można było ad-hoc dla pojedynczego polecenia tylko :
Uwaga: Liczy się to skuteczne
LC_CTYPE
ustawienieC
, takLC_CTYPE=C sed ...
by normalnie też praca, ale jeśliLC_ALL
dzieje się zestaw (do czegoś innego niżC
), to zastąpi poszczególneLC_*
zmienne -category takie jakLC_CTYPE
. Zatem najbardziej niezawodnym podejściem jest ustawienieLC_ALL
.Jednak (skutecznie) ustawienie
LC_CTYPE
doC
traktuje ciągi jakby każdy bajt był jego własny charakter ( nie interpretacja w oparciu o zasady kodowania jest wykonana), ze bez względu na - wielobajtowych-on-demand - kodowanie UTF-8 , że OS X wykorzystuje domyślnie , w których znaki obce mają kodowanie wielobajtowe .W skrócie: ustawienie
LC_CTYPE
doC
przyczyn skorupę i narzędzia do rozpoznawania tylko podstawowe litery angielskich jak listy (te w 7-bitowego zakresu ASCII), dzięki czemu obcych znaków. nie będą traktowane jak litery , co spowoduje na przykład konwersję wielkich / małych liter.Ponownie, może to być w porządku, jeśli nie musisz dopasowywać znaków zakodowanych w wielobajtach, takich jak
é
, i po prostu chcesz przepuścić takie znaki .Jeśli jest to niewystarczające i / lub chcesz zrozumieć przyczynę pierwotnego błędu (w tym określić, które bajty wejściowe spowodowały problem) i wykonać konwersje kodowania na żądanie, przeczytaj poniżej.
Problem polega na tym, że kodowanie pliku wejściowego nie jest zgodne z kodowaniem powłoki.
Mówiąc dokładniej, plik wejściowy zawiera znaki zakodowane w sposób, który nie jest poprawny w UTF-8 (jak stwierdził @Klas Lindbäck w komentarzu) - to właśnie
sed
próbuje przekazać komunikat o błędzieinvalid byte sequence
.Najprawdopodobniej plik wejściowy używa 8-bitowego kodowania jednobajtowego, na przykład
ISO-8859-1
często używanego do kodowania języków „zachodnioeuropejskich”.Przykład:
Akcentowana litera
à
ma kod Unicode0xE0
(224) - taki sam jak wISO-8859-1
. Jednak ze względu na naturę kodowania UTF-8 ten pojedynczy punkt kodowy jest reprezentowany jako 2 bajty -0xC3 0xA0
podczas gdy próba przekazania pojedynczego bajtu0xE0
jest nieprawidłowa w UTF-8.Oto demonstracja problemu przy użyciu łańcucha
voilà
zakodowanego jakoISO-8859-1
, zà
reprezentowanym jako jeden bajt (za pomocą ciągu bash cytowanego w ANSI-C ($'...'
), który używa\x{e0}
do utworzenia bajtu):Zauważ, że
sed
polecenie to faktycznie nie działa, po prostu przekazuje dane wejściowe, ale potrzebujemy go, aby wywołać błąd:Aby po prostu zignorować problem ,
LCTYPE=C
można zastosować powyższe podejście:Jeśli chcesz ustalić, które części danych wejściowych powodują problem , spróbuj wykonać następujące czynności:
Dane wyjściowe pokażą wszystkie bajty, które mają ustawiony wysoki bit (bajty przekraczające 7-bitowy zakres ASCII) w postaci szesnastkowej. (Należy jednak pamiętać, że obejmuje to również poprawnie zakodowane wielobajtowe sekwencje UTF-8 - potrzebne byłoby bardziej wyrafinowane podejście do konkretnej identyfikacji bajtów typu „nieprawidłowa w UTF-8”).
Przeprowadzanie konwersji kodowania na żądanie :
Standardowego narzędzia
iconv
można użyć do konwersji na kodowanie (-t
) i / lub z (-f
);iconv -l
wyświetla wszystkie obsługiwane.Przykłady:
Konwertuj FROM
ISO-8859-1
na kodowanie obowiązujące w powłoce (na podstawieLC_CTYPE
, która jestUTF-8
domyślnie oparta na) , bazując na powyższym przykładzie:Pamiętaj, że ta konwersja pozwala odpowiednio dopasować obce znaki :
Aby przekonwertować wejściowy BACK na
ISO-8859-1
przetworzony, po prostu potokuj wynik do innegoiconv
polecenia:źródło
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'
odciskised: RE error: illegal byte sequence
dla mnie na Sierra.echo $LC_ALL
wyjściaen_US.UTF-8
FWIW.LC_ALL
zastępuje wszystkie inneLC_*
zmienne, w tymLC_CTYPE
, jak wyjaśniono w odpowiedzi.Dodaj następujące wiersze do pliku
~/.bash_profile
lub~/.zshrc
plików.źródło
LC_CTYPE
doC
przyczyn każdy bajt w ciągów się własnym charakterem bez stosowania żadnych zasad kodowania. Ponieważ naruszenie zasad kodowania (UTF-8) spowodowało pierwotny problem, problem ten zniknął. Jednak płacona cena jest taka, że powłoka i narzędzia rozpoznają wtedy tylko podstawowe litery angielskie (te w 7-bitowym zakresie ASCII) jako litery. Zobacz moją odpowiedź, aby uzyskać więcej.LC_CTYPE=C sed …
, tj. Tylko na polecenie sed.Moje obejście polegało na użyciu Perla:
źródło
Odpowiedź mklement0 jest świetna, ale mam kilka drobnych poprawek.
Wydaje się, że dobrym pomysłem jest jawne określenie
bash
kodowania podczas używaniaiconv
. Powinniśmy również wstawić znak kolejności bajtów ( nawet jeśli standard Unicode tego nie zaleca ), ponieważ mogą istnieć uzasadnione pomyłki między UTF-8 a ASCII bez znaku kolejności bajtów . Niestetyiconv
nie wstawia znaku kolejności bajtów, gdy jawnie określasz endianness (UTF-16BE
lubUTF-16LE
), więc musimy go użyćUTF-16
, który wykorzystuje endianness specyficzny dla platformy, a następnie użyć,file --mime-encoding
aby odkryć prawdziwąiconv
zastosowaną endianness .(Wszystkie moje kodowania wielkimi literami, ponieważ kiedy wyświetlasz listę wszystkich
iconv
obsługiwanych kodowańiconv -l
, wszystkie są wielkie).źródło
file -b --mime-encoding
do odkrywania i zgłaszania kodowania pliku. Jest jednak kilka aspektów, na które warto zwrócić uwagę, które omówię w osobnych komentarzach.LC_CTYPE
wartość domyślna to zazwyczaj<lang_region>.UTF-8
, więc każdy plik bez BOM (znak kolejności bajtów) jest interpretowany jako plik UTF-8. Tylko w świecie Windows jest używany pseudo-BOM0xef 0xbb 0xff
; z definicji UTF-8 nie potrzebuje BOM i nie jest zalecany (jak twierdzisz); poza światem Windows ten pseudo-BOM powoduje uszkodzenie .Unfortunately, iconv doesn't prepend a byte-order mark when you explicitly specify an endianness (UTF-16BE or UTF-16LE)
: to jest z założenia: jeśli wyraźnie określisz endianizm , nie ma potrzeby odzwierciedlania go również za pomocą BOM, więc żadne nie jest dodawane.LC_*
/LANG
zmienne:bash
,ksh
, orazzsh
(ewentualnie innych, ale niedash
) zrobić respektować kodowanie znaków; zweryfikuj w powłokach podobnych do POSIX z ustawieniami regionalnymi opartymi na UTF-8 za pomocąv='ä'; echo "${#v}"
: powłoka rozpoznająca UTF-8 powinna zgłosić1
; tzn. powinien rozpoznać sekwencję wielobajtowąä
(0xc3 0xa4
) jako pojedynczy znak. Być może nawet ważniejsze, jednak: to standardowe narzędzia (sed
,awk
,cut
, ...) również muszą być locale / kodowania świadomy, a jednocześnie większość z nich na nowoczesny Uniksopodobny platformy są, istnieją wyjątki, takie jakawk
na OSX, icut
na Linuksie.file
rozpoznanie pseudo-BOM UTF-8, ale problem polega na tym, że większość narzędzi uniksowych przetwarzających plik nie , i zwykle psuje się lub przynajmniej źle zachowuje, gdy ma do czynienia z jednym. Bez BOMfile
poprawnie identyfikuje plik 7-bitowych bajtów jako ASCII, a taki, który ma prawidłowe znaki wielobajtowe UTF-8 jako UTF-8. Zaletą UTF-8 jest to, że jest nadzbiorem ASCII: każdy prawidłowy plik ASCII jest z definicji prawidłowym plikiem UTF-8 (ale nie odwrotnie); traktowanie pliku ASCII jako UTF-8 jest całkowicie bezpieczne (co technicznie jest, po prostu nie zawiera znaków wielobajtowych.)Musisz po prostu przesłać polecenie iconv przed poleceniem sed . Ex z wejściem file.txt:
Opcja -f jest zestawem kodowym „od”, a opcja -t to konwersja zestawu kodowego „na”.
Dbaj o wielkość liter, strony internetowe zwykle wyświetlają małe litery, takie jak <charset = iso-8859-1 "/>, a iconv używa wielkich liter. Masz listę obsługiwanych zestawów kodów iconv w systemie za pomocą polecenia iconv -l
UTF8-MAC jest nowoczesnym zestawem kodowym Mac OS do konwersji.
źródło
Pomogłem odpowiedzieć na powyższe pytanie, używając tylko tr .
Mam plik .csv, który jest wyciągiem z karty kredytowej i próbuję go zaimportować do Gnucash. Mieszkam w Szwajcarii, więc mam do czynienia z takimi słowami jak Zurych. Podejrzewając, że Gnucash nie lubi „” w polach numerycznych, postanawiam po prostu zastąpić wszystko
z
Tutaj idzie:
Użyłem od, aby rzucić trochę światła: zwróć uwagę na 374 w połowie tego wyjścia od-c
Pomyślałem wtedy, że mogę spróbować przekonać tr do podstawienia 374 na dowolny poprawny kod bajtowy. Najpierw więc wypróbowałem coś prostego, co nie działało, ale efektem ubocznym było pokazanie mi, gdzie był problem:
Możesz zobaczyć tr bails na 374 znakach.
Używanie perla wydaje się unikać tego problemu
źródło
Moje obejście polegało na użyciu GNU
sed
. Działa dobrze dla moich celów.źródło
sed
jest opcją, jeśli chcesz zignorować nieprawidłowe bajty w strumieniu wejściowym (nie ma potrzebyLC_ALL=C sed ...
obejścia tego problemu), ponieważ GNUsed
po prostu przekazuje nieprawidłowe bajty zamiast zgłaszania błędu, ale pamiętaj, że jeśli chcesz poprawnie rozpoznać i przetworzyć wszystkie znaków w ciągu wejściowym, nie ma możliwości, aby najpierw zmienić kodowanie wejścia (zwykle za pomocąiconv
).