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?
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?
Jawaban:
Menggunakan pendekatan regex:
$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
)
| . # anything else
/x
END;
preg_replace($regex, '$1', $text);
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.
$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:
require_once('Encoding.php');
use \ForceUTF8\Encoding; // It's namespaced now.
$utf8_string = Encoding::toUTF8($mixed_string);
$latin1_string = Encoding::toLatin1($mixed_string);
Saya telah menyertakan fungsi lain, Encoding :: fixUTF8 (), yang akan memperbaiki setiap string UTF8 yang terlihat kacau karena telah dikodekan ke UTF8 beberapa kali.
Pemakaian:
require_once('Encoding.php');
use \ForceUTF8\Encoding; // It's namespaced now.
$utf8_string = Encoding::fixUTF8($garbled_utf8_string);
Contoh:
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("FÃÂédÃÂération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
akan mengeluarkan:
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Unduh:
Anda dapat menggunakan mbstring:
$text = mb_convert_encoding($text, 'UTF-8', 'UTF-8');
... akan menghapus karakter yang tidak valid.
<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:
function remove_bs($Str) {
$StrArr = str_split($Str); $NewStr = '';
foreach ($StrArr as $Char) {
$CharNo = ord($Char);
if ($CharNo == 163) { $NewStr .= $Char; continue; } // keep £
if ($CharNo > 31 && $CharNo < 127) {
$NewStr .= $Char;
}
}
return $NewStr;
}
Bagaimana itu bekerja:
echo remove_bs('Hello õhowå åare youÆ?'); // Hello how are you?
í
karakter di bidang alamat yang merupakan karakter UTF-8 yang valid lihat tabel . Moral: jangan percaya pesan kesalahan API :)
$text = iconv("UTF-8", "UTF-8//IGNORE", $text);
Inilah yang saya gunakan. Sepertinya bekerja dengan cukup baik. Diambil dari http://planetozh.com/blog/2005/01/remove-invalid-characters-in-utf-8/
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 //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.
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 iconv
seperti$output = iconv("UTF-8//", "ISO-8859-1//IGNORE", $input );
Teks mungkin berisi karakter non-utf8 . Coba lakukan dulu:
$nonutf8 = mb_convert_encoding($nonutf8 , 'UTF-8', 'UTF-8');
Anda dapat membaca lebih lanjut di sini: http://php.net/manual/en/function.mb-convert-encoding.php news
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)
{
return UConverter::transcode($str, 'UTF-8', 'UTF-8');
}
function replace_invalid_byte_sequence2($str)
{
return (new UConverter('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'));
}
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.
public function stripInvalidXml($value) {
$ret = "";
$current;
if (empty($value)) {
return $ret;
}
$length = strlen($value);
for ($i=0; $i < $length; $i++) {
$current = ord($value{$i});
if (($current == 0x9) || ($current == 0xA) || ($current == 0xD) || (($current >= 0x20) && ($current <= 0xD7FF)) || (($current >= 0xE000) && ($current <= 0xFFFD)) || (($current >= 0x10000) && ($current <= 0x10FFFF))) {
$ret .= chr($current);
}
else {
$ret .= "";
}
}
return $ret;
}
ord()
mengembalikan hasil dalam kisaran 0-255. Raksasa if
dalam 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.
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:
mb_convert_encoding
\r
, \x00
(NULL-byte) dan karakter kontrol lainnya denganpreg_replace
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 \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 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";
}
php-mbstring
tidak dikemas dalam php secara default.
Dari patch terbaru ke modul parser JSON Feed Drupal:
//remove everything except valid letters (from any language)
$raw = preg_replace('/(?:\\\\u[\pL\p{Zs}])+/', '', $raw);
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
iconv
jauh lebih baik daripada yang berbasis regexp kuno preg_replace
, yang sudah usang saat ini.
ereg_replace()
, maaf.
Mungkin bukan solusi yang paling tepat, tetapi ini menyelesaikan pekerjaan dengan satu baris kode:
echo str_replace("?","",(utf8_decode($str)));
utf8_decode
akan mengubah karakter menjadi tanda tanya;
str_replace
akan menghapus tanda tanya.
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 = 0
for each ch in string:
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 -= 1
else:
if (ch >> 7): # high bit set?
c = (ch << 1) # strip the high bit marker
while (c & 1): # while the high bit indicates another octlet
c <<= 1
cont += 1
if cont > 4:
# more than 4 octels not allowed; cope with error
if !cont:
# illegal, do something sensible
newstring += ch # or whatever
if 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.
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:
$str = preg_replace("/[^\\x00-\\xFFFF]/", "", $str);
Sedikit berbeda dengan pertanyaannya, tetapi yang saya lakukan adalah menggunakan HtmlEncode (string),
kode semu di sini
var encoded = HtmlEncode(string);
encoded = Regex.Replace(encoded, "&#\d+?;", "");
var result = HtmlDecode(encoded);
masukan dan keluaran
"Headlight\x007E Bracket, { Cafe Racer<> Style, Stainless Steel 中文呢?"
"Headlight~ Bracket, { Cafe Racer<> Style, Stainless Steel 中文呢?"
Saya tahu ini tidak sempurna, tetapi berhasil untuk saya.
static $preg = <<<'END'
%(
[\x09\x0A\x0D\x20-\x7E]
| [\xC2-\xDF][\x80-\xBF]
| \xE0[\xA0-\xBF][\x80-\xBF]
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}
| \xED[\x80-\x9F][\x80-\xBF]
| \xF0[\x90-\xBF][\x80-\xBF]{2}
| [\xF1-\xF3][\x80-\xBF]{3}
| \xF4[\x80-\x8F][\x80-\xBF]{2}
)%xs
END;
if (preg_match_all($preg, $string, $match)) {
$string = implode('', $match[0]);
} else {
$string = '';
}
itu bekerja pada layanan kami
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.