Saya mengalami masalah dengan menghapus karakter non-utf8 dari string, yang tidak ditampilkan dengan benar. Karakternya seperti ini 0x97 0x61 0x6C 0x6F (representasi hex)
Apa cara terbaik untuk menghapusnya? Ekspresi reguler atau yang lainnya?
Solusi yang tercantum di sini tidak berhasil untuk saya, jadi saya menemukan jawaban saya di sini di bagian "Validasi karakter": webcollab.sourceforge.net/unicode.html
bobef
Terkait dengan ini , tetapi belum tentu duplikat, lebih seperti sepupu dekat :)
Ini mencari urutan UTF-8, dan menangkapnya ke dalam grup 1. Ini juga cocok dengan byte tunggal yang tidak dapat diidentifikasi sebagai bagian dari urutan UTF-8, tetapi tidak menangkapnya. Penggantian adalah apa pun yang ditangkap ke dalam grup 1. Ini secara efektif menghapus semua byte yang tidak valid.
Hal ini dimungkinkan untuk memperbaiki string, dengan mengkodekan byte yang tidak valid sebagai karakter UTF-8. Tetapi jika kesalahannya acak, ini dapat meninggalkan beberapa simbol aneh.
$regex =<<<'END'/((?:[\x00-\x7F]# single-byte sequences 0xxxxxxx|[\xC0-\xDF][\x80-\xBF]# double-byte sequences 110xxxxx 10xxxxxx|[\xE0-\xEF][\x80-\xBF]{2}# triple-byte sequences 1110xxxx 10xxxxxx * 2|[\xF0-\xF7][\x80-\xBF]{3}# quadruple-byte sequence 11110xxx 10xxxxxx * 3 ){1,100}# ...one or more times)|([\x80-\xBF])# invalid byte in range 10000000 - 10111111|([\xC0-\xFF])# invalid byte in range 11000000 - 11111111/x
END;function utf8replacer($captures){if($captures[1]!=""){// Valid byte sequence. Return unmodified.return $captures[1];}
elseif ($captures[2]!=""){// Invalid byte of the form 10xxxxxx.// Encode as 11000010 10xxxxxx.return"\xC2".$captures[2];}else{// Invalid byte of the form 11xxxxxx.// Encode as 11000011 10xxxxxx.return"\xC3".chr(ord($captures[3])-64);}}
preg_replace_callback($regex,"utf8replacer", $text);
EDIT:
!empty(x)akan cocok dengan nilai yang tidak kosong ( "0"dianggap kosong).
x != ""akan cocok dengan nilai yang tidak kosong, termasuk "0".
x !== ""akan cocok dengan apapun kecuali "".
x != "" tampaknya yang terbaik untuk digunakan dalam kasus ini.
Saya juga telah sedikit mempercepat pertandingan. Alih-alih mencocokkan setiap karakter secara terpisah, ini mencocokkan urutan karakter UTF-8 yang valid.
apa yang harus digunakan $regex = <<<'END'untuk PHP <5.3.x?
serhio
Anda dapat mengonversinya ke format heredoc sebagai gantinya, dengan sedikit penalti untuk keterbacaan. Kemungkinan lain adalah menggunakan string kutipan tunggal, tetapi kemudian Anda harus menghapus komentar tersebut.
Markus Jarderot
Ada kesalahan ketik kecil di baris ini elseif (!empty($captures([2])) {dan Anda harus menggunakan !== ""sebagai pengganti kosong karena "0"dianggap kosong. Juga fungsi ini sangat lambat, dapatkah ini dilakukan lebih cepat?
Kendall Hopkins
2
Ekspresi ini memiliki masalah memori utama, lihat di sini .
Ja͢ck
1
@ MarkusJarderot, Regex ....... hmm, apakah fungsi ini siap produksi? Apakah ada kasus uji untuk fungsi ini?
Pacerier
132
Jika Anda menerapkan utf8_encode()ke string yang sudah UTF8, itu akan mengembalikan keluaran UTF8 yang kacau.
Saya membuat fungsi yang menangani semua masalah ini. Ini disebut Encoding::toUTF8().
Anda tidak perlu tahu apa pengkodean string Anda. Bisa Latin1 (ISO8859-1), Windows-1252 atau UTF8, atau string dapat memiliki campuran keduanya. Encoding::toUTF8()akan mengubah semuanya menjadi UTF8.
Saya melakukannya karena layanan memberi saya umpan data yang semuanya kacau, mencampur pengkodean tersebut dalam string yang sama.
Saya telah menyertakan fungsi lain, Encoding :: fixUTF8 (), yang akan memperbaiki setiap string UTF8 yang terlihat kacau karena telah dikodekan ke UTF8 beberapa kali.
@Alliswell yang mana? Bisakah Anda memberikan contoh?
Frosty Z
tentu,<0x1a>
Alliswell
1
@ Alliswell Jika saya tidak salah <0x1a>, meskipun bukan karakter yang dapat dicetak, adalah urutan UTF-8 yang benar-benar valid. Anda mungkin memiliki masalah dengan karakter yang tidak dapat dicetak? Periksa ini: stackoverflow.com/questions/1176904/…
Frosty Z
ya, itu masalahnya. Terima kasih, sobat!
Alliswell
Sebelum memanggil mb convert, saya harus mengatur karakter pengganti mbstring menjadi tidak ada ini_set('mbstring.substitute_character', 'none');jika tidak saya mendapatkan tanda tanya pada hasilnya.
cby016
21
Fungsi ini menghapus semua karakter NON ASCII, ini berguna tetapi tidak menyelesaikan pertanyaan:
Ini adalah fungsi saya yang selalu berfungsi, terlepas dari pengkodeannya:
itu adalah ASCII dan bahkan tidak mendekati apa yang diinginkan pertanyaan tersebut.
misaxi
1
Yang ini berhasil. Saya menghadapi masalah ketika Google Maps API melaporkan kesalahan karena 'karakter non-UTF-8' di URL permintaan API. Pelakunya adalah íkarakter di bidang alamat yang merupakan karakter UTF-8 yang valid lihat tabel . Moral: jangan percaya pesan kesalahan API :)
tidak berhasil untuk saya. Saya berharap saya bisa melampirkan baris yang diuji, tetapi sayangnya itu memiliki karakter yang tidak valid.
Nir O.
3
Maaf, setelah beberapa pengujian saya menyadari ini tidak benar-benar melakukan apa yang saya pikirkan. Saya sekarang menggunakan stackoverflow.com/a/8215387/138023
Znarkus
14
coba ini:
$string = iconv("UTF-8","UTF-8//IGNORE",$string);
Menurut manual iconv , fungsi tersebut akan mengambil parameter pertama sebagai rangkaian karakter input, parameter kedua sebagai rangkaian karakter keluaran, dan yang ketiga sebagai string input aktual.
Jika Anda menyetel rangkaian rangkaian karakter masukan dan keluaran ke UTF-8 , dan menambahkan //IGNOREtanda ke rangkaian rangkaian keluaran, fungsi akan menghapus (menghapus) semua karakter dalam string masukan yang tidak dapat diwakili oleh rangkaian rangkaian keluaran. Jadi, pemfilteran string input berlaku.
Jelaskan apa yang dilakukan jawaban Anda daripada membuang cuplikan kode.
Tomasz Kowalczyk
3
Saya telah mencoba ini, dan //IGNOREtampaknya tidak menekan pemberitahuan bahwa ada UTF-8 yang tidak valid (yang, tentu saja, saya ketahui, dan ingin saya perbaiki). Sebuah komentar berperingkat tinggi di manual tampaknya menganggapnya sebagai bug selama beberapa tahun.
halfer
Selalu lebih baik untuk digunakan iconv. @halfer Mungkin data masukan Anda bukan dari utf-8. Pilihan lainnya adalah melakukan konversi ulang ke ascii lalu kembali ke utf-8 lagi. Dalam kasus saya, saya pernah menggunakan iconvseperti$output = iconv("UTF-8//", "ISO-8859-1//IGNORE", $input );
m3nda
@ erm3nda: Saya sama sekali tidak ingat kasus penggunaan saya untuk ini - mungkin telah mengurai situs web UTF-8 yang dideklarasikan dengan charset yang salah. Terima kasih atas catatannya, saya yakin itu akan berguna untuk pembaca yang akan datang.
halfer
Ya, jika Anda tidak tahu sesuatu, coba saja dan akhirnya Anda akan menekan kuncinya ;-)
m3nda
9
Teks mungkin berisi karakter non-utf8 . Coba lakukan dulu:
UConverter dapat digunakan sejak PHP 5.5. UConverter adalah pilihan yang lebih baik jika Anda menggunakan ekstensi intl dan tidak menggunakan mbstring.
function replace_invalid_byte_sequence($str){returnUConverter::transcode($str,'UTF-8','UTF-8');}function replace_invalid_byte_sequence2($str){return(newUConverter('UTF-8','UTF-8'))->convert($str);}
htmlspecialchars dapat digunakan untuk menghapus urutan byte yang tidak valid sejak PHP 5.4. Htmlspecialchars lebih baik daripada preg_match untuk menangani ukuran byte yang besar dan akurasi. Banyak implementasi yang salah dengan menggunakan ekspresi reguler dapat dilihat.
function replace_invalid_byte_sequence3($str){return htmlspecialchars_decode(htmlspecialchars($str, ENT_SUBSTITUTE,'UTF-8'));}
Anda memiliki tiga solusi bagus, tetapi tidak jelas bagaimana pengguna akan memilih di antara mereka.
Bob Ray
6
Saya telah membuat fungsi yang menghapus karakter UTF-8 yang tidak valid dari sebuah string. Saya menggunakannya untuk menghapus deskripsi 27000 produk sebelum menghasilkan file ekspor XML.
Dari semua jawaban kompleks di atas, yang satu ini berhasil untuk saya! Terima kasih.
Emin Özlem
Saya bingung dengan fungsi ini. ord()mengembalikan hasil dalam kisaran 0-255. Raksasa ifdalam fungsi ini menguji rentang unicode yang ord()tidak akan pernah kembali. Jika ada yang ingin menjelaskan mengapa fungsi ini bekerja seperti itu, saya akan menghargai wawasannya.
i336_
4
Selamat datang di 2019 dan /u pengubah dalam regex yang akan menangani karakter multibyte UTF-8 untuk Anda
Jika Anda hanya menggunakan mb_convert_encoding($value, 'UTF-8', 'UTF-8') Anda masih akan mendapatkan karakter yang tidak dapat dicetak dalam string Anda
Metode ini akan:
Hapus semua karakter multibyte UTF-8 yang tidak valid dengan mb_convert_encoding
Hapus semua karakter yang tidak dapat dicetak seperti \r, \x00(NULL-byte) dan karakter kontrol lainnya denganpreg_replace
metode:
function utf8_filter(string $value):string{return preg_replace('/[^[:print:]\n]/u','', mb_convert_encoding($value,'UTF-8','UTF-8'));}
[:print:] cocokkan semua karakter yang dapat dicetak dan \n baris baru dan hapus yang lainnya
Anda dapat melihat tabel ASCII di bawah ini .. Karakter yang dapat dicetak berkisar dari 32 hingga 127, tetapi baris baru \nadalah bagian dari karakter kontrol yang berkisar dari 0 hingga 31 sehingga kita harus menambahkan baris baru ke regex/[^[:print:]\n]/u
Anda dapat mencoba mengirim string melalui regex dengan karakter di luar rentang yang dapat dicetak seperti \x7F(DEL), \x1B(Esc) dll. Dan lihat bagaimana mereka dilucuti
function utf8_filter(string $value):string{return preg_replace('/[^[:print:]\n]/u','', mb_convert_encoding($value,'UTF-8','UTF-8'));}
$arr =['Danish chars'=>'Hello from Denmark with æøå','Non-printable chars'=>"\x7FHello with invalid chars\r \x00"];foreach($arr as $k => $v){
echo "$k:\n---------\n";
$len = strlen($v);
echo "$v\n(".$len.")\n";
$strip = utf8_decode(utf8_filter(utf8_encode($v)));
$strip_len = strlen($strip);
echo $strip."\n(".$strip_len.")\n\n";
echo "Chars removed: ".($len - $strip_len)."\n\n\n";}
Jika Anda khawatir ya itu mempertahankan spasi sebagai karakter yang valid.
Melakukan apa yang saya butuhkan. Ini menghapus karakter emoji yang tersebar luas saat ini yang tidak sesuai dengan kumpulan karakter 'utf8' MySQL dan yang memberi saya kesalahan seperti "SQLSTATE [HY000]: Kesalahan umum: 1366 Nilai string salah".
Setelah mencoba ratusan solusi, satu-satunya solusi yang berhasil adalah milik Anda.
Haritsinh Gohil
1
Jadi aturannya adalah bahwa oktlet UTF-8 pertama memiliki set bit tinggi sebagai penanda, dan kemudian 1 hingga 4 bit untuk menunjukkan berapa banyak tambahan oktlet; kemudian masing-masing oktlet tambahan harus memiliki dua bit tinggi yang disetel ke 10.
Pseudo-python adalah:
newstring =''
cont =0for each ch instring:if cont:if(ch >>6)!=2:# high 2 bits are 10# do whatever, e.g. skip it, or skip whole point, or?else:# acceptable continuation of multi-octlet char
newstring += ch
cont -=1else:if(ch >>7):# high bit set?
c =(ch <<1)# strip the high bit markerwhile(c &1):# while the high bit indicates another octlet
c <<=1
cont +=1if cont >4:# more than 4 octels not allowed; cope with errorif!cont:# illegal, do something sensible
newstring += ch # or whateverif cont:# last utf-8 was not terminated, cope
Logika yang sama ini harus dapat diterjemahkan ke php. Namun, tidak jelas jenis pengupasan apa yang harus dilakukan setelah Anda mendapatkan karakter yang cacat.
Belum pernah menggunakannya di dalam PHP itu sendiri tetapi selalu berfungsi dengan baik untuk saya di baris perintah. Anda bisa mendapatkannya untuk menggantikan karakter yang tidak valid.
Jawaban:
Menggunakan pendekatan regex:
Ini mencari urutan UTF-8, dan menangkapnya ke dalam grup 1. Ini juga cocok dengan byte tunggal yang tidak dapat diidentifikasi sebagai bagian dari urutan UTF-8, tetapi tidak menangkapnya. Penggantian adalah apa pun yang ditangkap ke dalam grup 1. Ini secara efektif menghapus semua byte yang tidak valid.
Hal ini dimungkinkan untuk memperbaiki string, dengan mengkodekan byte yang tidak valid sebagai karakter UTF-8. Tetapi jika kesalahannya acak, ini dapat meninggalkan beberapa simbol aneh.
EDIT:
!empty(x)
akan cocok dengan nilai yang tidak kosong ("0"
dianggap kosong).x != ""
akan cocok dengan nilai yang tidak kosong, termasuk"0"
.x !== ""
akan cocok dengan apapun kecuali""
.x != ""
tampaknya yang terbaik untuk digunakan dalam kasus ini.Saya juga telah sedikit mempercepat pertandingan. Alih-alih mencocokkan setiap karakter secara terpisah, ini mencocokkan urutan karakter UTF-8 yang valid.
sumber
$regex = <<<'END'
untuk PHP <5.3.x?elseif (!empty($captures([2])) {
dan Anda harus menggunakan!== ""
sebagai pengganti kosong karena"0"
dianggap kosong. Juga fungsi ini sangat lambat, dapatkah ini dilakukan lebih cepat?Jika Anda menerapkan
utf8_encode()
ke string yang sudah UTF8, itu akan mengembalikan keluaran UTF8 yang kacau.Saya membuat fungsi yang menangani semua masalah ini. Ini disebut
Encoding::toUTF8()
.Anda tidak perlu tahu apa pengkodean string Anda. Bisa Latin1 (ISO8859-1), Windows-1252 atau UTF8, atau string dapat memiliki campuran keduanya.
Encoding::toUTF8()
akan mengubah semuanya menjadi UTF8.Saya melakukannya karena layanan memberi saya umpan data yang semuanya kacau, mencampur pengkodean tersebut dalam string yang sama.
Pemakaian:
Saya telah menyertakan fungsi lain, Encoding :: fixUTF8 (), yang akan memperbaiki setiap string UTF8 yang terlihat kacau karena telah dikodekan ke UTF8 beberapa kali.
Pemakaian:
Contoh:
akan mengeluarkan:
Unduh:
https://github.com/neitanod/forceutf8
sumber
Anda dapat menggunakan mbstring:
... akan menghapus karakter yang tidak valid.
Lihat: Mengganti karakter UTF-8 yang tidak valid dengan tanda tanya, mbstring.substitute_character tampaknya diabaikan
sumber
<0x1a>
<0x1a>
, meskipun bukan karakter yang dapat dicetak, adalah urutan UTF-8 yang benar-benar valid. Anda mungkin memiliki masalah dengan karakter yang tidak dapat dicetak? Periksa ini: stackoverflow.com/questions/1176904/…ini_set('mbstring.substitute_character', 'none');
jika tidak saya mendapatkan tanda tanya pada hasilnya.Fungsi ini menghapus semua karakter NON ASCII, ini berguna tetapi tidak menyelesaikan pertanyaan:
Ini adalah fungsi saya yang selalu berfungsi, terlepas dari pengkodeannya:
Bagaimana itu bekerja:
sumber
í
karakter di bidang alamat yang merupakan karakter UTF-8 yang valid lihat tabel . Moral: jangan percaya pesan kesalahan API :)Inilah yang saya gunakan. Sepertinya bekerja dengan cukup baik. Diambil dari http://planetozh.com/blog/2005/01/remove-invalid-characters-in-utf-8/
sumber
coba ini:
Menurut manual iconv , fungsi tersebut akan mengambil parameter pertama sebagai rangkaian karakter input, parameter kedua sebagai rangkaian karakter keluaran, dan yang ketiga sebagai string input aktual.
Jika Anda menyetel rangkaian rangkaian karakter masukan dan keluaran ke UTF-8 , dan menambahkan
//IGNORE
tanda ke rangkaian rangkaian keluaran, fungsi akan menghapus (menghapus) semua karakter dalam string masukan yang tidak dapat diwakili oleh rangkaian rangkaian keluaran. Jadi, pemfilteran string input berlaku.sumber
//IGNORE
tampaknya tidak menekan pemberitahuan bahwa ada UTF-8 yang tidak valid (yang, tentu saja, saya ketahui, dan ingin saya perbaiki). Sebuah komentar berperingkat tinggi di manual tampaknya menganggapnya sebagai bug selama beberapa tahun.iconv
. @halfer Mungkin data masukan Anda bukan dari utf-8. Pilihan lainnya adalah melakukan konversi ulang ke ascii lalu kembali ke utf-8 lagi. Dalam kasus saya, saya pernah menggunakaniconv
seperti$output = iconv("UTF-8//", "ISO-8859-1//IGNORE", $input );
Teks mungkin berisi karakter non-utf8 . Coba lakukan dulu:
Anda dapat membaca lebih lanjut di sini: http://php.net/manual/en/function.mb-convert-encoding.php news
sumber
UConverter dapat digunakan sejak PHP 5.5. UConverter adalah pilihan yang lebih baik jika Anda menggunakan ekstensi intl dan tidak menggunakan mbstring.
htmlspecialchars dapat digunakan untuk menghapus urutan byte yang tidak valid sejak PHP 5.4. Htmlspecialchars lebih baik daripada preg_match untuk menangani ukuran byte yang besar dan akurasi. Banyak implementasi yang salah dengan menggunakan ekspresi reguler dapat dilihat.
sumber
Saya telah membuat fungsi yang menghapus karakter UTF-8 yang tidak valid dari sebuah string. Saya menggunakannya untuk menghapus deskripsi 27000 produk sebelum menghasilkan file ekspor XML.
sumber
ord()
mengembalikan hasil dalam kisaran 0-255. Raksasaif
dalam fungsi ini menguji rentang unicode yangord()
tidak akan pernah kembali. Jika ada yang ingin menjelaskan mengapa fungsi ini bekerja seperti itu, saya akan menghargai wawasannya.Selamat datang di 2019 dan
/u
pengubah dalam regex yang akan menangani karakter multibyte UTF-8 untuk AndaJika Anda hanya menggunakan
mb_convert_encoding($value, 'UTF-8', 'UTF-8')
Anda masih akan mendapatkan karakter yang tidak dapat dicetak dalam string AndaMetode ini akan:
mb_convert_encoding
\r
,\x00
(NULL-byte) dan karakter kontrol lainnya denganpreg_replace
metode:
[:print:]
cocokkan semua karakter yang dapat dicetak dan\n
baris baru dan hapus yang lainnyaAnda dapat melihat tabel ASCII di bawah ini .. Karakter yang dapat dicetak berkisar dari 32 hingga 127, tetapi baris baru
\n
adalah bagian dari karakter kontrol yang berkisar dari 0 hingga 31 sehingga kita harus menambahkan baris baru ke regex/[^[:print:]\n]/u
Anda dapat mencoba mengirim string melalui regex dengan karakter di luar rentang yang dapat dicetak seperti
\x7F
(DEL),\x1B
(Esc) dll. Dan lihat bagaimana mereka dilucutihttps://www.tehplayground.com/q5sJ3FOddhv1atpR
sumber
php-mbstring
tidak dikemas dalam php secara default.sumber
Dari patch terbaru ke modul parser JSON Feed Drupal:
Jika Anda khawatir ya itu mempertahankan spasi sebagai karakter yang valid.
Melakukan apa yang saya butuhkan. Ini menghapus karakter emoji yang tersebar luas saat ini yang tidak sesuai dengan kumpulan karakter 'utf8' MySQL dan yang memberi saya kesalahan seperti "SQLSTATE [HY000]: Kesalahan umum: 1366 Nilai string salah".
Untuk detailnya, lihat https://www.drupal.org/node/1824506#comment-6881382
sumber
iconv
jauh lebih baik daripada yang berbasis regexp kunopreg_replace
, yang sudah usang saat ini.ereg_replace()
, maaf.Mungkin bukan solusi yang paling tepat, tetapi ini menyelesaikan pekerjaan dengan satu baris kode:
utf8_decode
akan mengubah karakter menjadi tanda tanya;str_replace
akan menghapus tanda tanya.sumber
Jadi aturannya adalah bahwa oktlet UTF-8 pertama memiliki set bit tinggi sebagai penanda, dan kemudian 1 hingga 4 bit untuk menunjukkan berapa banyak tambahan oktlet; kemudian masing-masing oktlet tambahan harus memiliki dua bit tinggi yang disetel ke 10.
Pseudo-python adalah:
Logika yang sama ini harus dapat diterjemahkan ke php. Namun, tidak jelas jenis pengupasan apa yang harus dilakukan setelah Anda mendapatkan karakter yang cacat.
sumber
c = (ch << 1)
akan membuat(c & 1)
nol untuk pertama kalinya, melewati loop. Tesnya mungkin(c & 128)
Untuk menghapus semua karakter Unicode di luar bidang bahasa dasar Unicode:
sumber
Sedikit berbeda dengan pertanyaannya, tetapi yang saya lakukan adalah menggunakan HtmlEncode (string),
kode semu di sini
masukan dan keluaran
Saya tahu ini tidak sempurna, tetapi berhasil untuk saya.
sumber
itu bekerja pada layanan kami
sumber
Bagaimana dengan iconv:
http://php.net/manual/en/function.iconv.php
Belum pernah menggunakannya di dalam PHP itu sendiri tetapi selalu berfungsi dengan baik untuk saya di baris perintah. Anda bisa mendapatkannya untuk menggantikan karakter yang tidak valid.
sumber