簡體   English   中英

導致 PHP 在轉換為 UTF-8 之前無法檢測到正確的字符編碼導致數據丟失的已知麻煩字符列表

[英]List of known troublesome characters that causes PHP to fail to detect the proper character encoding before converting to UTF-8 resulting in lost data

PHP 並不總是正確的,我寫的內容必須始終正確。 在這種情況下,帶有主題的 email 包含短划線字符 該線程是關於檢測 PHP 錯誤地檢測到單獨的奇怪字符(比如說,在其他純 ASCII 文本中)。 我已經確定了一個 static 示例,盡管我的目標是創建一個明確的線程,其中包含盡可能接近我們可以創建的插入代碼版本。

這是我從 email 的主題 header 開始的字符串:

<?php
//This is AFTER exploding the : of the header and using trim on $p[1]:
$s = '=?ISO-8859-1?Q?orkut=20=96=20convite=20enviado=20por=20Lais=20Piccirillo?=';
//orkut – convite enviado por Lais Piccirillo
?>

通常,下一步是執行以下操作:

$s = imap_mime_header_decode($s);//orkut � convite enviado por Lais Piccirillo

通常超過這一點,我會做以下事情:

$s = mb_convert_encoding($subject, 'UTF-8', mb_detect_encoding($s));//en dash missing!

現在,我收到了 static 對較早 static 問題的回答 最終,我能夠將這組工作代碼放在一起:

<?php
$s1 = '=?ISO-8859-1?Q?orkut=20=96=20convite=20enviado=20por=20Lais=20Piccirillo?=';

//Attempt to determine the character set:
$en = mb_detect_encoding($s1);//ASCII; wrong!!!
$p = explode('?', $s1, 3)[1];//ISO-8859-1; wrong!!!

//Necessary to decode the q-encoded header text any way FIRST:
$s2 = imap_mime_header_decode($s1);

//Now scan for character exceptions in the original text to compensate for PHP:
if (strpos($s1, '=96') !== false) {$s2 = mb_convert_encoding($s2[0]->text, 'UTF-8', 'CP1252');}
else {$s2 = mb_convert_encoding($s2[0]->text, 'UTF-8');}

//String is finally ready for client output:
echo '<pre>'.print_r($s2,1).'</pre>';//orkut – convite enviado por Lais Piccirillo
?>

現在要么我仍然編程不正確,並且在 PHP 中一些東西我丟失了(嘗試了html_entity_decodeiconvmb_convert_encodingutf8_encode的許多組合),或者,至少在 PHP 的那一刻,我們將被強制檢測特定字符並手動覆蓋編碼,就像我在第 12 行所做的那樣。在后一種情況下,需要創建錯誤報告,或者如果已經存在特定於該問題的錯誤報告,則可能更新錯誤報告。

所以從技術上講,問題是:

我們如何正確檢測所有字符編碼以防止在將字符串轉換為 UTF-8 的過程中丟失任何字符?

如果不存在這樣的正確答案,則有效答案包括在其他情況下純 ASCII 文本導致 PHP 未能正確檢測到正確的字符編碼從而導致不正確的 UTF-8 編碼字符串的字符。 假設這個問題在未來得到解決,並且可以針對所有其他相關答案中列出的所有奇數字符進行驗證,那么可以接受正確的答案。

您將 PHP 無法解決的問題歸咎於 PHP:

  • $s1一個 ASCII 字符串; 就像字符串“笑臉表情符號”是 ASCII 一樣,盡管它描述了字符串“”。
  • $s2根據您發送的信息進行解碼。 實際上,它被解碼為原始字節序列,以及輸入中提供的 label。

您的實際問題是您發送的信息是錯誤的 - 發送給您的系統犯了一個常見錯誤,即錯誤地將 Windows-1252 標記為 ISO-8859-1。

兩種編碼之間的區別在於,從 0x80 到 0x9F 的字節是 ISO 8859 中的控制字符,並且(大部分)分配給 Windows-1252 中的可打印字符。 請注意,任何系統都無法自動告訴您打算使用哪種解釋 - 無論哪種方式,memory 中都只有一個包含 0x96 的字節。 然而,任何此類字節更有可能是 Windows- 1252字符,而不是 ISO 8859 中很少使用的額外控制字符,因此一個常見的解決方案是簡單地假設任何標記為 ISO-8859-1 的數據是實際上是 Windows-1252

這使得解決方案非常簡單:

// $input is the ASCII string you've received
$input = '=?ISO-8859-1?Q?orkut=20=96=20convite=20enviado=20por=20Lais=20Piccirillo?=';

// Decode the string into its labelled encoding, and string of bytes
$mime_decoded = imap_mime_header_decode($input);
$input_encoding = $mime_decode[0]->charset;
$raw_bytes = $mime_decode[0]->text;

// If it claims to be ISO-8859-1, assume it's lying
if ( $input_encoding === 'ISO-8859-1' ) {
    $input_encoding = 'Windows-1252';
}

// Now convert from a known encoding to UTF-8 for the use of your application
$utf8_string = mb_convert_encoding($raw_bytes, 'UTF-8', $input_encoding);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM