Saya memiliki file teks dalam penyandian yang tidak dikenal atau campuran. Saya ingin melihat baris yang berisi urutan byte yang tidak valid UTF-8 (dengan memipipkan file teks ke beberapa program). Setara, saya ingin menyaring baris yang valid UTF-8. Dengan kata lain, saya sedang mencari .grep [notutf8]
Solusi ideal adalah portable, pendek dan dapat digeneralisasi untuk pengkodean lainnya, tetapi jika Anda merasa cara terbaik adalah memanggang definisi UTF-8 , silakan.
command-line
text-processing
character-encoding
unicode
Gilles 'SANGAT berhenti menjadi jahat'
sumber
sumber
Jawaban:
Jika Anda ingin menggunakan
grep
, Anda dapat melakukan:di UTF-8 lokal untuk mendapatkan baris yang memiliki setidaknya urutan UTF-8 tidak valid (setidaknya ini bekerja dengan GNU Grep).
sumber
-a
, itu harus bekerja oleh POSIX. Namun GNUgrep
setidaknya gagal menemukan UTF-8 pengganti yang dikodekan UTF-16 bukan karakter atau codepoint di atas 0x10FFFF.-a
dibutuhkan oleh GNUgrep
(yang tidak sesuai dengan POSIX, saya kira). Mengenai, area pengganti dan codepoint di atas 0x10FFFF, ini bug (yang bisa menjelaskan itu ). Untuk ini, menambahkan-P
harus berfungsi dengan GNUgrep
2.21 (tetapi lambat); itu buggy setidaknya di Debian grep / 2.20-4 .grep
merupakan utilitas teks (hanya diharapkan bekerja pada input teks), jadi saya kira perilaku GNU grep sama validnya dengan yang ada di sini.grep
(yang tujuannya adalah untuk menganggap urutan tidak valid sebagai tidak cocok), dan kemungkinan bug.Saya pikir Anda mungkin ingin iconv . Ini untuk mengkonversi antar set kode dan mendukung sejumlah format yang tidak masuk akal. Misalnya, untuk menghapus apa pun yang tidak valid di UTF-8 Anda dapat menggunakan:
iconv -c -t UTF-8 < input.txt > output.txt
Tanpa opsi -c itu akan melaporkan masalah dalam mengkonversi ke stderr, jadi dengan arah proses Anda dapat menyimpan daftar ini. Cara lain adalah dengan melucuti barang-barang non-UTF8 dan kemudian
diff input.txt output.txt
untuk daftar tempat perubahan dilakukan.
sumber
iconv -c -t UTF-8 <input.txt | diff input.txt - | sed -ne 's/^< //p'
. Ini tidak akan berfungsi sebagai saluran pipa, karena Anda harus membaca input dua kali (tidak,tee
tidak akan melakukannya, mungkin memblokir tergantung pada berapa banyak bufferingiconv
dandiff
lakukan).diff <(iconv -c -t UTF-8 <input.txt) input.txt
Sunting: Saya telah memperbaiki kesalahan ketik di regex .. Dibutuhkan '\ x80` bukan \ 80 .
Regex untuk memfilter formulir UTF-8 yang tidak valid, untuk kepatuhan ketat terhadap UTF-8, adalah sebagai berikut
Output (dari baris kunci. Dari Uji 1 ):
Q. Bagaimana cara membuat data pengujian untuk menguji regex yang menyaring Unicode yang tidak valid?
A. Buat algoritma uji UTF-8 Anda sendiri, dan patahkan aturannya ...
Catch-22 .. Namun, bagaimana Anda menguji algoritma pengujian Anda?
Regex, di atas, telah diuji (menggunakan
iconv
sebagai referensi) untuk setiap nilai integer dari0x00000
hingga0x10FFFF
.. Nilai atas ini menjadi nilai integer maksimum dari Unicode CodepointMenurut halaman wikipedia UTF-8 ini ,.
Numeber ini (1.112.064) setara dengan rentang
0x000000
untuk0x10F7FF
, yang merupakan 0x0800 malu maksimum integer-nilai aktual untuk tertinggi Unicode codepoint:0x10FFFF
Ini blok bilangan bulat yang hilang dari spektrum Unicode codepoints, karena kebutuhan untuk UTF-16 encoding untuk langkah di luar maksud desain aslinya melalui sistem yang disebut pasangan pengganti . Satu blok
0x0800
bilangan bulat telah dicadangkan untuk digunakan oleh UTF-16 .. Blok ini mencakup rentang0x00D800
hingga0x00DFFF
. Tidak satu pun dari intimeter ini yang merupakan nilai Unicode legal, dan karenanya nilai UTF-8 tidak valid.Dalam Uji 1 ,
regex
telah diuji terhadap setiap angka dalam kisaran Unicode Codepoints, dan cocok dengan tepat hasiliconv
.. yaitu. Nilai valid 0x010F7FF , dan 0x000800 nilai tidak valid.Namun, masalah sekarang muncul dari, * Bagaimana regex menangani Nilai UTF-8 Out-Of-Range; di atas
0x010FFFF
(UTF-8 dapat meluas hingga 6 byte, dengan nilai integer maksimum 0x7FFFFFFF ?Untuk menghasilkan nilai byte UTF-8 non-unicode yang diperlukan , saya telah menggunakan perintah berikut:
Untuk menguji validitasnya (dalam beberapa cara), saya telah menggunakan
Gilles'
UTF-8 regex ...Output dari 'perl's print chr' cocok dengan penyaringan regex Gilles .. Satu memperkuat validitas yang lain .. Saya tidak dapat menggunakan
iconv
karena hanya menangani subset Standar Unicode yang valid dari UTF-8 (asli) yang lebih luas standar...Para biarawati yang terlibat agak besar, jadi saya telah menguji top-of-range, bottom-of-range, dan beberapa pemindaian melangkah dengan peningkatan seperti, 11111, 13579, 33333, 53441 ... Hasilnya semua cocok, jadi sekarang semua yang tersisa adalah untuk menguji regex terhadap nilai-nilai gaya UTF-8 out-of-range (tidak valid untuk Unicode, dan karena itu juga tidak valid untuk UTF-8 yang ketat itu sendiri) ..
Berikut adalah modul tes:
sumber
\300\200
(benar-benar buruk: itu kode 0 tidak dinyatakan dengan byte nol!). Saya pikir regexp Anda menolaknya dengan benar.Saya menemukan
uconv
(dalamicu-devtools
paket di Debian) berguna untuk memeriksa data UTF-8:(
\x
Bantuan menemukan karakter yang tidak valid (kecuali untuk false positive yang secara sukarela diperkenalkan dengan literal di\xE9
atas)).(banyak penggunaan bagus lainnya).
sumber
recode
dapat digunakan sama - kecuali bahwa saya pikir itu harus gagal jika diminta untuk menerjemahkan urutan multibyte yang tidak valid. Saya tidak yakin; itu tidak akan gagalprint...|recode u8..u8/x4
misalnya (yang hanya melakukan hexdump seperti yang Anda lakukan di atas) karena tidak melakukan apa-apa kecualiiconv data data
, tetapi gagal sepertirecode u8..u2..u8/x4
karena menerjemahkan lalu mencetak. Tapi saya tidak cukup tahu tentang hal itu untuk memastikan - dan ada banyak kemungkinan.test.txt
,. Bagaimana seharusnya saya menemukan karakter yang tidak valid menggunakan solusi Anda? Apa artinyaus
dalam kode Anda?us
berarti Amerika Serikat, itu adalah kependekan dari ASCII. Ini mengubah input menjadi ASCII di mana karakter non-ASCII dikonversi menjadi\uXXXX
notasi dan non-karakter menjadi\xXX
.Python telah memiliki built-in
unicode
fungsi sejak versi 2.0.Dalam Python 3,
unicode
telah dilipat menjadistr
. Itu harus dilewatkan objek byte-seperti , di sinibuffer
objek yang mendasari untuk deskriptor standar .sumber
python 2
gagal untuk menandai UTF-8 pengganti UTF-16 pengganti non-karakter (setidaknya dengan 2.7.6).Saya menemukan masalah serupa (detail di bagian "Konteks") dan tiba dengan solusi ftfy_line_by_line.py berikut :
Menggunakan encode + replace + ftfy untuk memperbaiki secara otomatis Mojibake dan koreksi lainnya.
Konteks
Saya telah mengumpulkan> 10GiB CSV dari metadata filesystem dasar menggunakan skrip gen_basic_files_metadata.csv.sh berikut , menjalankan dasarnya:
The kesulitan saya punya adalah dengan pengkodean tidak konsisten dari nama file di file sistem, menyebabkan
UnicodeDecodeError
saat memproses lebih lanjut dengan aplikasi python ( csvsql untuk lebih spesifik).Oleh karena itu saya menerapkan skrip ftfy di atas, dan butuh
Harap dicatat ftfy sangat lambat, memproses yang> 10GiB ambil:
sedangkan sha256sum untuk perbandingan:
pada Intel (R) Core (TM) i7-3520M CPU @ 2.90GHz + 16GiB RAM (dan data pada drive eksternal)
sumber