ฉันมีปัญหากับการลบอักขระที่ไม่ใช่ utf8 ออกจากสตริงซึ่งแสดงไม่ถูกต้อง อักขระเป็นเช่นนี้ 0x97 0x61 0x6C 0x6F (การแทนค่าฐานสิบหก)
วิธีที่ดีที่สุดในการลบออกคืออะไร? นิพจน์ทั่วไปหรืออย่างอื่น?
ฉันมีปัญหากับการลบอักขระที่ไม่ใช่ utf8 ออกจากสตริงซึ่งแสดงไม่ถูกต้อง อักขระเป็นเช่นนี้ 0x97 0x61 0x6C 0x6F (การแทนค่าฐานสิบหก)
วิธีที่ดีที่สุดในการลบออกคืออะไร? นิพจน์ทั่วไปหรืออย่างอื่น?
คำตอบ:
ใช้วิธี 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);
มันค้นหาลำดับ UTF-8 และจับสิ่งเหล่านั้นเป็นกลุ่ม 1 นอกจากนี้ยังจับคู่ไบต์เดี่ยวที่ไม่สามารถระบุได้ว่าเป็นส่วนหนึ่งของลำดับ UTF-8 แต่ไม่ได้จับสิ่งเหล่านั้น การแทนที่คือสิ่งที่ถูกจับเป็นกลุ่ม 1 สิ่งนี้จะลบไบต์ที่ไม่ถูกต้องทั้งหมดได้อย่างมีประสิทธิภาพ
เป็นไปได้ที่จะซ่อมแซมสตริงโดยการเข้ารหัสไบต์ที่ไม่ถูกต้องเป็นอักขระ UTF-8 แต่หากข้อผิดพลาดเป็นแบบสุ่มสิ่งนี้อาจทำให้สัญลักษณ์แปลก ๆ
$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);
แก้ไข:
!empty(x)
จะจับคู่ค่าที่ไม่ว่างเปล่า ( "0"
ถือว่าว่างเปล่า)x != ""
"0"
จะตรงกับค่าที่ไม่ว่างเปล่ารวมทั้งx !== ""
""
จะตรงกับสิ่งใดนอกจากx != ""
ดูเหมือนจะดีที่สุดที่จะใช้ในกรณีนี้
ฉันยังเร่งการแข่งขันเล็กน้อย แทนที่จะจับคู่อักขระแต่ละตัวแยกกันจะจับคู่ลำดับของอักขระ UTF-8 ที่ถูกต้อง
$regex = <<<'END'
PHP <5.3.x?
elseif (!empty($captures([2])) {
และคุณควรใช้!== ""
แทนการเว้นว่างเนื่องจาก"0"
ถือว่าว่างเปล่า นอกจากนี้ฟังก์ชั่นนี้ช้ามากสามารถทำได้เร็วขึ้นหรือไม่?
หากคุณใช้utf8_encode()
กับสตริง UTF8 อยู่แล้วระบบจะส่งคืนเอาต์พุต UTF8 ที่อ่านไม่ออก
ฉันสร้างฟังก์ชันที่แก้ไขปัญหานี้ทั้งหมด It's Encoding::toUTF8()
เรียกว่า
คุณไม่จำเป็นต้องรู้ว่าการเข้ารหัสสตริงของคุณคืออะไร อาจเป็น Latin1 (ISO8859-1), Windows-1252 หรือ UTF8 หรือสตริงสามารถผสมกันได้ Encoding::toUTF8()
จะแปลงทุกอย่างเป็น UTF8
ฉันทำเพราะบริการให้ฟีดข้อมูลทั้งหมดทำให้ฉันสับสนผสมการเข้ารหัสเหล่านั้นในสตริงเดียวกัน
การใช้งาน:
require_once('Encoding.php');
use \ForceUTF8\Encoding; // It's namespaced now.
$utf8_string = Encoding::toUTF8($mixed_string);
$latin1_string = Encoding::toLatin1($mixed_string);
ฉันได้รวมฟังก์ชั่นอื่น Encoding :: fixUTF8 () ซึ่งจะแก้ไขทุกสตริง UTF8 ที่ดูผิดเพี้ยนจากการเข้ารหัสเป็น UTF8 หลายครั้ง
การใช้งาน:
require_once('Encoding.php');
use \ForceUTF8\Encoding; // It's namespaced now.
$utf8_string = Encoding::fixUTF8($garbled_utf8_string);
ตัวอย่าง:
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");
จะส่งออก:
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
ดาวน์โหลด:
คุณสามารถใช้ mbstring:
$text = mb_convert_encoding($text, 'UTF-8', 'UTF-8');
... จะลบอักขระที่ไม่ถูกต้อง
<0x1a>
<0x1a>
แม้ว่าจะไม่ใช่อักขระที่พิมพ์ได้ แต่ก็เป็นลำดับ UTF-8 ที่ถูกต้องอย่างสมบูรณ์ คุณอาจมีปัญหาเกี่ยวกับอักขระที่ไม่สามารถพิมพ์ได้? ตรวจสอบสิ่งนี้: stackoverflow.com/questions/1176904/…
ini_set('mbstring.substitute_character', 'none');
มิฉะนั้นฉันจะได้รับเครื่องหมายคำถามในผลลัพธ์
ฟังก์ชันนี้จะลบอักขระที่ไม่ใช่ ASCII ทั้งหมดซึ่งมีประโยชน์ แต่ไม่สามารถแก้ปัญหาได้:
นี่คือฟังก์ชันของฉันที่ใช้งานได้ตลอดเวลาโดยไม่คำนึงถึงการเข้ารหัส:
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;
}
มันทำงานอย่างไร:
echo remove_bs('Hello õhowå åare youÆ?'); // Hello how are you?
í
ตัวละครในช่องที่อยู่ซึ่งเป็น UTF-8 ตัวอักษรที่ถูกต้องตารางเห็น ขวัญกำลังใจ: อย่าไว้วางใจข้อความแสดงข้อผิดพลาดของ API :)
$text = iconv("UTF-8", "UTF-8//IGNORE", $text);
นี่คือสิ่งที่ฉันใช้ ดูเหมือนจะทำงานได้ดี นำมาจากhttp://planetozh.com/blog/2005/01/remove-invalid-characters-in-utf-8/
ลองสิ่งนี้:
$string = iconv("UTF-8","UTF-8//IGNORE",$string);
ตามคู่มือ iconvฟังก์ชันจะรับพารามิเตอร์ตัวแรกเป็นชุดอักขระอินพุตพารามิเตอร์ที่สองเป็นชุดอักขระของเอาต์พุตและตัวที่สามเป็นสตริงอินพุตจริง
หากคุณตั้งค่าชุดอักขระอินพุตและเอาต์พุตเป็นUTF-8และต่อท้าย//IGNORE
แฟล็กเข้ากับชุดอักขระเอาต์พุตฟังก์ชันจะดร็อป (แถบ) อักขระทั้งหมดในสตริงอินพุตที่ไม่สามารถแสดงโดยชุดอักขระเอาต์พุตได้ ดังนั้นการกรองสตริงอินพุตจึงมีผล
//IGNORE
ดูเหมือนจะไม่ระงับการแจ้งเตือนว่า UTF-8 ที่ไม่ถูกต้องมีอยู่ (ซึ่งแน่นอนฉันรู้และต้องการแก้ไข) ความคิดเห็นที่ได้รับคะแนนสูงในคู่มือนี้ดูเหมือนว่าจะเป็นข้อบกพร่องมาหลายปีแล้ว
iconv
ดีกว่าเสมอไปใช้ @halfer บางทีข้อมูลอินพุตของคุณไม่ได้มาจาก utf-8 อีกทางเลือกหนึ่งคือทำการแปลงใหม่เป็น ascii จากนั้นกลับไปที่ utf-8 อีกครั้ง ในกรณีของฉันฉันใช้iconv
เช่น$output = iconv("UTF-8//", "ISO-8859-1//IGNORE", $input );
ข้อความที่อาจจะมีตัวละครที่ไม่ใช่ utf8 ลองทำก่อน:
$nonutf8 = mb_convert_encoding($nonutf8 , 'UTF-8', 'UTF-8');
คุณสามารถอ่านเพิ่มเติมได้ที่นี่: http://php.net/manual/en/function.mb-convert-encoding.php news
UConverter สามารถใช้ได้ตั้งแต่ PHP 5.5 UConverter เป็นตัวเลือกที่ดีกว่าถ้าคุณใช้ส่วนขยาย intl และไม่ใช้ 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 สามารถใช้เพื่อลบลำดับไบต์ที่ไม่ถูกต้องตั้งแต่ PHP 5.4 Htmlspecialchars ดีกว่า preg_match สำหรับการจัดการไบต์ขนาดใหญ่และความแม่นยำ สามารถมองเห็นการใช้งานที่ไม่ถูกต้องโดยใช้นิพจน์ทั่วไปได้
function replace_invalid_byte_sequence3($str)
{
return htmlspecialchars_decode(htmlspecialchars($str, ENT_SUBSTITUTE, 'UTF-8'));
}
ฉันได้สร้างฟังก์ชันที่ลบอักขระ UTF-8 ที่ไม่ถูกต้องออกจากสตริง ฉันใช้เพื่อล้างคำอธิบายของผลิตภัณฑ์ 27000 รายการก่อนที่จะสร้างไฟล์ส่งออก 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()
ส่งคืนผลลัพธ์ในช่วง 0-255 ยักษ์ใหญ่if
ในฟังก์ชั่นนี้ทดสอบช่วงยูนิโคดที่ord()
ไม่มีวันกลับมา หากใครต้องการชี้แจงว่าเหตุใดฟังก์ชันนี้จึงทำงานได้ดีฉันขอขอบคุณสำหรับข้อมูลเชิงลึก
ยินดีต้อนรับสู่ปี 2019 และ/u
ตัวปรับแต่งใน regex ซึ่งจะจัดการ UTF-8 multibyte chars ให้คุณ
หากคุณใช้เพียงอย่างเดียวmb_convert_encoding($value, 'UTF-8', 'UTF-8')
คุณจะยังคงมีอักขระที่ไม่สามารถพิมพ์ได้ในสตริงของคุณ
วิธีนี้จะ:
mb_convert_encoding
\r
, \x00
(NULL ไบต์) และตัวอักษรอื่น ๆ ที่มีการควบคุมpreg_replace
function utf8_filter(string $value): string{
return preg_replace('/[^[:print:]\n]/u', '', mb_convert_encoding($value, 'UTF-8', 'UTF-8'));
}
[:print:]
จับคู่ตัวอักษรและ\n
บรรทัดใหม่ที่พิมพ์ได้ทั้งหมดและตัดส่วนอื่น ๆ
คุณสามารถดูตาราง ASCII ด้านล่าง .. ตัวอักษรที่พิมพ์ได้มีตั้งแต่ 32 ถึง 127 แต่การขึ้นบรรทัดใหม่\n
เป็นส่วนหนึ่งของอักขระควบคุมซึ่งมีค่าตั้งแต่ 0 ถึง 31 ดังนั้นเราจึงต้องเพิ่มบรรทัดใหม่ใน regex/[^[:print:]\n]/u
คุณสามารถลองส่งสตริงผ่าน regex ด้วยตัวอักษรนอกช่วงที่พิมพ์ได้เช่น\x7F
(DEL), \x1B
(Esc) เป็นต้นและดูว่ามีการถอดอย่างไร
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
ไม่ได้บรรจุใน php ตามค่าเริ่มต้น
$string = preg_replace('~&([a-z]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);~i', '$1', htmlentities($string, ENT_COMPAT, 'UTF-8'));
จากแพตช์ล่าสุดไปจนถึงโมดูลตัวแยกวิเคราะห์ Feeds JSON ของ Drupal:
//remove everything except valid letters (from any language)
$raw = preg_replace('/(?:\\\\u[\pL\p{Zs}])+/', '', $raw);
หากคุณกังวลใช่มันจะคงเว้นวรรคเป็นอักขระที่ถูกต้อง
ทำในสิ่งที่ฉันต้องการ มันลบอักขระอิโมจิที่แพร่หลายในปัจจุบันซึ่งไม่พอดีกับชุดอักขระ 'utf8' ของ MySQL และทำให้ฉันมีข้อผิดพลาดเช่น "SQLSTATE [HY000]: ข้อผิดพลาดทั่วไป: 1366 ค่าสตริงไม่ถูกต้อง"
ดูรายละเอียดได้ที่https://www.drupal.org/node/1824506#comment-6881382
iconv
อยู่ไกลดีกว่า regexp เก่าล้าสมัยตามpreg_replace
, ชจะเลิกในปัจจุบัน
ereg_replace()
ขอโทษ
อาจไม่ใช่วิธีแก้ปัญหาที่แม่นยำที่สุด แต่ทำให้งานสำเร็จด้วยรหัสบรรทัดเดียว:
echo str_replace("?","",(utf8_decode($str)));
utf8_decode
จะแปลงอักขระเป็นเครื่องหมายคำถาม
str_replace
จะตัดเครื่องหมายคำถามออก
ดังนั้นกฎก็คือว่าอ็อกเล็ตUTF-8 ตัวแรกมีบิตสูงที่กำหนดเป็นเครื่องหมายจากนั้น 1 ถึง 4 บิตเพื่อระบุจำนวนอ็อกเล็ตเพิ่มเติม จากนั้นอ็อกเล็ตเพิ่มเติมแต่ละตัวจะต้องมีค่าสูงสองบิตที่กำหนดเป็น 10
pseudo-python จะเป็น:
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
ตรรกะเดียวกันนี้ควรแปลเป็น php ได้ อย่างไรก็ตามยังไม่ชัดเจนว่าจะทำการลอกแบบใดเมื่อคุณได้รับตัวละครที่ผิดรูปแบบ
c = (ch << 1)
จะทำให้(c & 1)
เป็นศูนย์ในครั้งแรกโดยข้ามลูป การทดสอบน่าจะเป็น(c & 128)
ในการลบอักขระ Unicode ทั้งหมดที่อยู่นอกระนาบภาษาพื้นฐานของ Unicode:
$str = preg_replace("/[^\\x00-\\xFFFF]/", "", $str);
แตกต่างจากคำถามเล็กน้อย แต่สิ่งที่ฉันกำลังทำคือใช้ HtmlEncode (สตริง)
รหัสหลอกที่นี่
var encoded = HtmlEncode(string);
encoded = Regex.Replace(encoded, "&#\d+?;", "");
var result = HtmlDecode(encoded);
อินพุตและเอาต์พุต
"Headlight\x007E Bracket, { Cafe Racer<> Style, Stainless Steel 中文呢?"
"Headlight~ Bracket, { Cafe Racer<> Style, Stainless Steel 中文呢?"
ฉันรู้ว่ามันไม่สมบูรณ์แบบ แต่ได้ผลสำหรับฉัน
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 = '';
}
มันทำงานกับบริการของเรา
iconv เป็นอย่างไร:
http://php.net/manual/en/function.iconv.php
ไม่ได้ใช้มันใน PHP เอง แต่มันทำงานได้ดีสำหรับฉันในบรรทัดคำสั่งเสมอ คุณสามารถใช้แทนอักขระที่ไม่ถูกต้องได้