Saya mencoba mengganti string di Makefile di Mac OS X untuk kompilasi silang ke iOS. String telah menyematkan tanda kutip ganda. Perintahnya adalah:
sed -i "" 's|"iphoneos-cross","llvm-gcc:-O3|"iphoneos-cross","clang:-Os|g' Configure
Dan kesalahannya adalah:
sed: RE error: illegal byte sequence
Saya telah mencoba melarikan diri dari tanda kutip ganda, koma, tanda hubung, dan titik dua tanpa sukacita. Sebagai contoh:
sed -i "" 's|\"iphoneos-cross\"\,\"llvm-gcc\:\-O3|\"iphoneos-cross\"\,\"clang\:\-Os|g' Configure
Saya mengalami banyak waktu men-debug masalah. Adakah yang tahu cara sed
mencetak posisi urutan byte ilegal? Atau apakah ada yang tahu apa urutan byte ilegal itu?
LC_CTYPE=C && LANG=C && sed command
LANG
masalahnya. Sigh ....sed
(seperti yang juga digunakan pada OS X) memerlukan-i ''
(argumen pilihan-string kosong) untuk pembaruan di tempat tanpa file cadangan; dengan GNUsed
, hanya-i
dengan sendirinya berfungsi - lihat stackoverflow.com/a/40777793/45375Jawaban:
Perintah sampel yang menunjukkan gejala:
sed 's/./@/' <<<$'\xfc'
gagal, karena byte0xfc
bukan karakter UTF-8 yang valid.Perhatikan bahwa, sebaliknya, GNU
sed
(Linux, tetapi juga dapat diinstal pada macOS) cukup melewati byte yang tidak valid, tanpa melaporkan kesalahan.Menggunakan jawaban yang sebelumnya diterima adalah pilihan jika Anda tidak keberatan kehilangan dukungan untuk lokal Anda yang sebenarnya (jika Anda menggunakan sistem AS dan Anda tidak perlu berurusan dengan karakter asing, itu mungkin baik-baik saja.)
Namun, efek yang sama dapat memiliki ad-hoc untuk perintah tunggal hanya :
Catatan: Yang penting adalah efektif
LC_CTYPE
pengaturanC
, sehinggaLC_CTYPE=C sed ...
akan normal juga bekerja, tetapi jikaLC_ALL
terjadi menjadi set (untuk sesuatu yang lain dariC
), itu akan menimpa individuLC_*
variabel -Kategori sepertiLC_CTYPE
. Dengan demikian, pendekatan yang paling kuat adalah menetapkanLC_ALL
.Namun, pengaturan (efektif)
LC_CTYPE
untukC
memperlakukan string seolah-olah setiap byte adalah karakternya sendiri ( tidak ada interpretasi berdasarkan aturan pengkodean dilakukan), tanpa memperhatikan - multibyte-on-demand - pengkodean UTF-8 yang OS X mempekerjakan secara default , di mana karakter asing memiliki penyandian multibyte .Singkatnya: pengaturan
LC_CTYPE
untukC
penyebab shell dan utilitas hanya mengenal huruf dasar bahasa Inggris sebagai huruf (yang di kisaran ASCII 7-bit), sehingga karakter asing. tidak akan diperlakukan sebagai huruf , menyebabkan, misalnya, konversi huruf besar / kecil gagal.Sekali lagi, ini mungkin baik-baik saja jika Anda tidak perlu mencocokkan karakter multibyte-encoded seperti
é
, dan hanya ingin melewati karakter tersebut .Jika ini tidak cukup dan / atau Anda ingin memahami penyebab kesalahan asli (termasuk menentukan byte input apa yang menyebabkan masalah) dan melakukan pengkodean konversi sesuai permintaan, baca terus di bawah ini.
Masalahnya adalah bahwa pengkodean file input tidak cocok dengan shell.
Lebih khusus lagi, file input berisi karakter yang dikodekan dengan cara yang tidak valid di UTF-8 (seperti yang dinyatakan oleh @Klas Lindbäck dalam komentar) - itulah yang ingin disampaikan oleh
sed
pesan kesalahaninvalid byte sequence
.Kemungkinan besar, file input Anda menggunakan pengodean 8-bit single-byte seperti
ISO-8859-1
, yang sering digunakan untuk menyandikan bahasa "Eropa Barat".Contoh:
Huruf beraksen
à
memiliki Unicode codepoint0xE0
(224) - sama seperti padaISO-8859-1
. Namun, karena sifat dari UTF-8 encoding, codepoint tunggal ini diwakili sebagai 2 byte -0xC3 0xA0
, sedangkan mencoba untuk melewati byte tunggal0xE0
adalah tidak valid di bawah UTF-8.Berikut ini demonstrasi masalah menggunakan string yang
voilà
disandikan sebagaiISO-8859-1
, dengan yangà
direpresentasikan sebagai satu byte (melalui string bash yang dikutip ANSI-C$'...'
) yang menggunakan\x{e0}
untuk membuat byte):Perhatikan bahwa
sed
perintah ini efektif no-op yang hanya melewati input, tetapi kita perlu memprovokasi kesalahan:Untuk mengabaikan masalah ,
LCTYPE=C
pendekatan di atas dapat digunakan:Jika Anda ingin menentukan bagian input mana yang menyebabkan masalah , coba yang berikut ini:
Output akan menampilkan semua byte yang memiliki set bit tinggi (byte yang melebihi kisaran ASCII 7-bit) dalam bentuk heksadesimal. (Namun, perlu diketahui bahwa itu juga mencakup urutan multibyte UTF-8 yang dikodekan dengan benar - diperlukan pendekatan yang lebih canggih untuk secara spesifik mengidentifikasi byte yang tidak valid dalam UTF-8.)
Melakukan konversi pengkodean sesuai permintaan :
Utilitas standar
iconv
dapat digunakan untuk mengkonversi ke (-t
) dan / atau dari (-f
) pengkodean;iconv -l
daftar semua yang didukung.Contoh:
Konversi FROM
ISO-8859-1
ke pengkodean yang berlaku di shell (berdasarkanLC_CTYPE
, yangUTF-8
-berdasarkan secara default), membangun contoh di atas:Perhatikan bahwa konversi ini memungkinkan Anda untuk mencocokkan karakter asing dengan benar :
Untuk mengonversi input BACK ke
ISO-8859-1
setelah diproses, cukup pipa hasilnya keiconv
perintah lain :sumber
LC_CTYPE=C sed 's/.*/&/' <<<$'voil\x{e0}'
cetakansed: RE error: illegal byte sequence
untuk saya di Sierra.echo $LC_ALL
outputen_US.UTF-8
FWIW.LC_ALL
mengabaikan semuaLC_*
variabel lain , termasukLC_CTYPE
, seperti yang dijelaskan dalam jawaban.Tambahkan baris berikut ke
~/.bash_profile
atau~/.zshrc
file (s).sumber
LC_CTYPE
untukC
penyebab setiap byte dalam string menjadi karakter tersendiri tanpa menerapkan aturan pengkodean. Karena pelanggaran aturan pengkodean (UTF-8) menyebabkan masalah asli, ini membuat masalah hilang. Namun, harga yang Anda bayar adalah bahwa shell dan utilitas kemudian hanya mengenali huruf Inggris dasar (yang dalam kisaran ASCII 7-bit) sebagai huruf. Lihat jawaban saya untuk lebih lanjut.LC_CTYPE=C sed …
, yaitu hanya pada perintah sed.Solusi saya telah menggunakan Perl:
sumber
Jawaban mklement0 bagus, tapi saya punya sedikit penyesuaian.
Sepertinya ide yang bagus untuk secara spesifik menentukan
bash
penyandian saat menggunakaniconv
. Selain itu, kita harus menambahkan tanda byte-order ( meskipun standar unicode tidak merekomendasikannya ) karena mungkin ada kebingungan yang sah antara UTF-8 dan ASCII tanpa tanda byte-order . Sayangnya,iconv
tidak menambahkan tanda byte-order ketika Anda secara eksplisit menentukan endianness (UTF-16BE
atauUTF-16LE
), jadi kita perlu menggunakanUTF-16
, yang menggunakan endianness platform-spesifik, dan kemudian gunakanfile --mime-encoding
untuk menemukan endianness sebenarnya yangiconv
digunakan.(Saya huruf besar semua pengkodean saya karena ketika Anda daftar semua
iconv
pengkodean yang didukung denganiconv -l
mereka semua huruf besar.)sumber
file -b --mime-encoding
untuk menemukan dan melaporkan penyandian file. Ada beberapa aspek yang perlu diperhatikan, yang akan saya lakukan dalam komentar terpisah.LC_CTYPE
biasanya<lang_region>.UTF-8
, jadi setiap file tanpa BOM (byte-order mark) karenanya ditafsirkan sebagai file UTF-8. Hanya di dunia Windows bahwa BOM semu0xef 0xbb 0xff
digunakan; menurut definisi, UTF-8 tidak memerlukan BOM dan tidak direkomendasikan (seperti yang Anda nyatakan); di luar dunia Windows, BOM semu ini menyebabkan banyak hal rusak .Unfortunately, iconv doesn't prepend a byte-order mark when you explicitly specify an endianness (UTF-16BE or UTF-16LE)
: itu dengan desain: jika Anda menentukan endianness secara eksplisit , tidak perlu juga mencerminkannya melalui BOM, jadi tidak ada yang ditambahkan.LC_*
/LANG
variabel:bash
,,ksh
danzsh
(mungkin yang lain, tetapi tidakdash
) menghormati pengkodean karakter; verifikasi dalam kerang mirip POSIX dengan lokal berbasis UTF-8 denganv='ä'; echo "${#v}"
: shell sadar UTF-8 harus melaporkan1
; yaitu, ia harus mengenali urutan multi-byteä
(0xc3 0xa4
), sebagai karakter tunggal . Mungkin bahkan lebih penting, namun: utilitas standar (sed
,awk
,cut
, ...) juga harus lokal / encoding-sadar, dan sementara sebagian besar dari mereka pada yang modern Unix-seperti platform yang, ada pengecualian, sepertiawk
di OSX, dancut
di Linux.file
mengenali pseudo-BOM UTF-8, tetapi masalahnya adalah sebagian besar utilitas Unix yang memproses file tidak , dan biasanya rusak atau setidaknya bertingkah salah ketika dihadapkan dengan satu. Tanpa BOM,file
mengidentifikasi dengan benar file byte semua-7-bit sebagai ASCII, dan yang memiliki karakter multi-byte UTF-8 yang valid sebagai UTF-8. Keindahan dari UTF-8 adalah bahwa ia adalah superset dari ASCII: setiap file ASCII yang valid secara definisi merupakan file UTF-8 yang valid (tetapi tidak sebaliknya); itu sangat aman untuk memperlakukan file ASCII sebagai UTF-8 (yang secara teknis, tidak mengandung karakter multi-byte.)Anda hanya perlu menyalurkan perintah ikonv sebelum perintah sed . Mis dengan input file.txt:
-f opsi adalah 'dari' codeset dan -t opsi adalah konversi 'ke' codeset.
Jaga kasus, halaman web biasanya menunjukkan huruf kecil seperti itu <charset = iso-8859-1" /> dan iconv menggunakan huruf besar. Anda harus daftar iconv didukung codesets di Anda sistem dengan perintah iconv -l
UTF8-MAC adalah kode OS Mac modern untuk konversi.
sumber
Saya mendapat bagian dari cara untuk menjawab pertanyaan di atas hanya dengan menggunakan tr .
Saya memiliki file .csv yang merupakan pernyataan kartu kredit dan saya mencoba mengimpornya ke Gnucash. Saya berbasis di Swiss jadi saya harus berurusan dengan kata-kata seperti Zürich. Mencurigai Gnucash tidak suka "" di bidang angka, saya memutuskan untuk mengganti semuanya
dengan
Ini dia:
Saya menggunakan od untuk menjelaskan: Perhatikan 374 di tengah-tengah output od -c ini
Lalu saya pikir saya mungkin mencoba membujuk tr untuk mengganti 374 untuk kode byte yang benar. Jadi pertama-tama saya mencoba sesuatu yang sederhana, yang tidak berhasil, tetapi memiliki efek samping dengan menunjukkan kepada saya di mana byte merepotkan itu:
Anda dapat melihat tr bails di karakter 374.
Menggunakan perl tampaknya menghindari masalah ini
sumber
Solusi saya telah menggunakan gnu
sed
. Bekerja dengan baik untuk tujuan saya.sumber
sed
adalah opsi jika Anda ingin mengabaikan byte yang tidak valid dalam aliran input (tidak perlu untukLC_ALL=C sed ...
penyelesaian), karena GNUsed
hanya melewati byte yang tidak valid melalui bukannya melaporkan kesalahan, tetapi perhatikan bahwa jika Anda ingin mengenali dan memproses semua karakter dalam string input, tidak ada jalan lain untuk mengubah pengkodean input terlebih dahulu (biasanya, denganiconv
).