學習PHP中的iconv擴充套件相關函式
想必 iconv 這個擴充套件的相關函式大家多少都接觸過,做為 PHP 的預設擴充套件它已經存在了很久,也是我們在操作字元編碼時經常會使用的函式。不過除了 iconv() 這個函式外,你還知道它的其它函式嗎?今天,我們就來學習一下 iconv 擴充套件中的各種好玩的函式。
iconv 設定及獲取資訊
首先,就是我們可以設定 iconv 擴充套件中預設定義的輸出和輸出字元編碼格式。
iconv_set_encoding("internal_encoding", "UTF-8"); // Deprecated: iconv_set_encoding(): Use of iconv.internal_encoding is deprecated iconv_set_encoding("output_encoding", "ISO-8859-1"); // Deprecated: iconv_set_encoding(): Use of iconv.output_encoding is deprecated var_dump(iconv_get_encoding()); // array(3) { // ["input_encoding"]=> // string(5) "UTF-8" // ["output_encoding"]=> // string(10) "ISO-8859-1" // ["internal_encoding"]=> // string(5) "UTF-8" // }
iconv_set_encoding() 接收兩個引數,一個是設定的屬性型別,一個是設定的編碼格式。屬性型別包括 internal_encoding 、 input_encoding 和 output_encoding ,分別代表內部的、輸入的、輸出的編碼格式。在這段測試程式碼中,我們將 internal_encoding 設定為 UTF8 ,將 output_encoding 設定為 ISO-8859-1 ,然後使用 iconv_get_encoding() 打印出當前環境中相關的 iconv 屬性設定資訊,可以看到,在預設情況下當前環境中的 input_encoding 也是 UTF8 格式。
不過需要說明的是,iconv_set_encoding() 已經是不推薦使用的函數了,或者說不推薦使用這個函式來設定上面的三種屬性型別,它們會報出過時警告資訊。現在更推薦直接使用 php.ini 中的 default_charset 來進行設定。
iconv 根據編碼獲取字元長度、指定位置及擷取字串
在面對中文字串的操作時,我們使用預設的 strlen() 之類的函式返回的中文字元長度是不正確的,這就牽涉到編碼的問題。一般情況下,UTF8 是佔三個位元組,而 GBK 是佔兩個位元組,所以說一個漢字對於 strlen() 來說如果是在 UTF8 環境中會返回 3 。當然,現在大多數情況下我們會使用 MB 庫擴充套件的相關函式來處理這種問題,不過 iconv 也為我們提供了幾個用於字串操作的函式。
echo iconv_strlen("測試長度測試長度"), PHP_EOL; // 8
echo iconv_strlen("測試長度測試長度", 'ISO-8859-1'), PHP_EOL; // 24
echo iconv_strlen("測試長度測試長度", 'GBK'), PHP_EOL; // 12
echo '======', PHP_EOL;
echo iconv_strpos("測試長度測試長度", "長"), PHP_EOL; // 2
echo iconv_strpos("測試長度測試長度", "長", 0, 'ISO-8859-1'), PHP_EOL; // 6
echo iconv_strpos("測試長度測試長度", "長", 0, 'GBK'), PHP_EOL; //
echo '======', PHP_EOL;
echo iconv_strrpos("測試長度測試長度", "長"), PHP_EOL; // 6
echo iconv_strrpos("測試長度測試長度", "長", 'ISO-8859-1'), PHP_EOL; // 18
echo '======', PHP_EOL;
echo iconv_substr("測試長度測試長度", 2, 4), PHP_EOL; // 長度測試
echo iconv_substr("測試長度測試長度", 6, 12, 'ISO-8859-1'), PHP_EOL; // 長度測試
echo iconv_substr("測試長度測試長度", 3, 6, 'GBK'), PHP_EOL; // 長度測試
iconv_strlen() 就是獲取字串長度的,如果不給第二個引數就按預設的字符集編碼來獲取字串長度。在測試程式碼中可以看出,同樣八個中文字的內容,使用不同的編碼返回的數量是不相同的。在這裡,我們發現 iconv 中對於 GBK 的中文是 1.5 個位元組,也就是 8 箇中文字佔用了 12 個位元組的長度。
iconv_strpos() 和 iconv_strrpos() 和 strpos() 的作用一樣,返回某個字元第一次出現的位置,一個是從前往後(從左往右),另一個是從後往前(從右往左)。它們的第三個引數是偏移量,也就是查詢到指定字元後再偏移幾個單位。從這裡我們可以看出,對於 GBK 編碼的操作是有問題的,因為在 iconv 中,GBK 是 1.5 個位元組,這樣會帶來單個字元無法定位的問題。
iconv_substr() 很明顯地就是擷取字串的函數了,同樣我們要根據編碼格式來指定它的擷取位置。
iconv 轉換字元編碼
接下來就是本尊 iconv() 函式的使用的了,其實它反而沒什麼可講的,將指定的編碼轉換成另外一種編碼而已,相信這個函式大家都不陌生。
$phone = file_get_contents('https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=13888888888');
print_r($phone);
// __GetZoneResult_ = {
// mts:'1388888',
// province:'����',
// catName:'�й��ƶ�',
// telString:'13888888888',
// areaVid:'30515',
// ispVid:'3236139',
// carrier:'�����ƶ�'
// }
print_r(iconv('GBK', 'UTF-8', $phone));
// __GetZoneResult_ = {
// mts:'1388888',
// province:'雲南',
// catName:'中國移動',
// telString:'13888888888',
// areaVid:'30515',
// ispVid:'3236139',
// carrier:'雲南移動'
// }
print_r(iconv('GBK', 'ISO-8859-1//IGNORE', $phone));
// __GetZoneResult_ = {
// mts:'1388888',
// province:'',
// catName:'',
// telString:'13888888888',
// areaVid:'30515',
// ispVid:'3236139',
// carrier:''
// }
我們找到的這個淘寶用於查詢手機號相關資訊的開放介面,返回的正好是 GBK 型別的資料。當我們直接列印結果時,在 UTF8 環境下它就會輸出亂碼資訊。這時,我們通過 iconv() 函式就能夠輕鬆地將編碼轉換成 UTF8 格式,並正確打印出了結果。第三個測試中,我們在要轉換到的字符集編碼型別後面加上了 //IGNORE ,目的就是忽略無法轉換的內容,所以可以看出在最後我們轉換到錯誤的 ISO-8859-1 時,中文資訊就全都沒有了,因為它們無法轉換就被忽略掉了。
mime 郵件頭操作
最後我們再看一個非常不常用的內容,那就是 iconv 還可以直接轉換 mime 頭中的編碼內容資訊。這個 mime 頭資訊其實就是標示當前檔案或者內容的 mime 型別。平常我們會根據它來判斷上傳的檔案是否正確,除些之外,在郵件傳送中,這個 mime 頭的使用也非常廣泛。如果做過郵件傳送接收相關的開發並且抓過包的同學一定見過下面的內容。
headers_string = <<<EOF
Subject: =?UTF-8?B?UHLDvGZ1bmcgUHLDvGZ1bmc=?=
To: [email protected]
Date: Thu, 1 Jan 1970 00:00:00 +0000
Message-Id: <[email protected]>
Received: from localhost (localhost [127.0.0.1]) by localhost
with SMTP id example for <[email protected]>;
Thu, 1 Jan 1970 00:00:00 +0000 (UTC)
(envelope-from [email protected])
Received: (qmail 0 invoked by uid 65534); 1 Thu 2003 00:00:00 +0000
EOF;
Subject 字元就是郵件的標題,To 就是傳送人的郵件地址。在這裡我們主要看一下 Subject 的內容,它的開頭就有一段描述這個欄位使用的編碼資訊的內容,?UTF-8 ,然後後面是一堆看不懂的東西。其實我們簡單地能看出來這是一個 base64 編碼的內容,如果將它解碼在對應的編碼內容下就能看到原文資訊。不過,這個時候我們也可以使用 iconv 來直接轉換它的編碼。
$headers = iconv_mime_decode_headers($headers_string, 0, "ISO-8859-1");
var_dump($headers);
// array(5) {
// ["Subject"]=>
// string(15) "Pr�fung Pr�fung"
// ["To"]=>
// string(19) "[email protected]"
// ["Date"]=>
// string(30) "Thu, 1 Jan 1970 00:00:00 +0000"
// ["Message-Id"]=>
// string(21) "<[email protected]>"
// ["Received"]=>
// array(2) {
// [0]=>
// string(204) "from localhost (localhost [127.0.0.1]) by localhost with SMTP id example for <[email protected]>; Thu, 1 Jan 1970 00:00:00 +0000 (UTC) (envelope-from [email protected])"
// [1]=>
// string(57) "(qmail 0 invoked by uid 65534); 1 Thu 2003 00:00:00 +0000"
// }
// }
看到了麼?不僅直接轉了編碼,而且還將 mime 頭格式轉換成了 PHP 中的陣列格式。當然,我們這裡測試的程式碼是將正常的內容轉換到 ISO-8859-1 了,反而是出現了亂碼。下面我們再拿一箇中文郵件的例子來看下。
$headers_string = <<<EOF
Return-Path: <[email protected]>
Delivered-To: [email protected]
Received: (qmail 75513 invoked by alias); 20 May 2002 02:19:53 -0000
Received: from unknown (HELO bluesky) (61.155.118.135)
by 202.106.187.143 with SMTP; 20 May 2002 02:19:53 -0000
Message-ID: <007f01c3111c$742fec00$0100007f@bluesky>
From: "=?gb2312?B?wLbAtrXEzOwNCg==?=" <[email protected]>
To: "bhw98" <[email protected]>
Cc: <[email protected]>
Subject: =?gb2312?B?ztK1xLbgtK6/2rPM0PI=?=
Date: Sat, 20 May 2002 10:03:36 +0800
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="----=_NextPart_000_007A_01C3115F.80DFC5E0"
EOF;
$headers = iconv_mime_decode_headers($headers_string, 0, "UTF-8");
var_dump($headers);
// array(11) {
// ["Return-Path"]=>
// string(21) "<[email protected]>"
// ["Delivered-To"]=>
// string(14) "[email protected]"
// ["Received"]=>
// array(2) {
// [0]=>
// string(58) "(qmail 75513 invoked by alias); 20 May 2002 02:19:53 -0000"
// [1]=>
// string(101) "from unknown (HELO bluesky) (61.155.118.135) by 202.106.187.143 with SMTP; 20 May 2002 02:19:53 -0000"
// }
// ["Message-ID"]=>
// string(40) "<007f01c3111c$742fec00$0100007f@bluesky>"
// ["From"]=>
// string(38) ""藍藍的天
// " <[email protected]>"
// ["To"]=>
// string(24) ""bhw98" <[email protected]>"
// ["Cc"]=>
// string(21) "<[email protected]>"
// ["Subject"]=>
// string(21) "我的多串列埠程式"
// ["Date"]=>
// string(31) "Sat, 20 May 2002 10:03:36 +0800"
// ["MIME-Version"]=>
// string(3) "1.0"
// ["Content-Type"]=>
// string(16) "multipart/mixed;"
// }
這個中文郵件 mime 頭的 Subject 指定的是 GB2312 。通過 iconv_mime_decode_headers() 函式我們將整個頭資訊中的內容都轉換成了 UTF8 ,這時就可以正常顯示所有的內容資訊了。當然,我們也可以對單個的 mime 欄位進行轉碼。
echo iconv_mime_decode("Subject: =?gb2312?B?ztK1xLbgtK6/2rPM0PI=?=", 0, 'UTF-8'), PHP_EOL; // Subject: 我的多串列埠程式
除了對於接收的資訊進行編碼轉換之外,我們還可以自己編碼相關的內容進行傳送使用。
$preferences = array(
"input-charset" => "UTF-8",
"output-charset" => "GBK",
"line-length" => 76,
"line-break-chars" => "\n"
);
$preferences["scheme"] = "Q";
echo iconv_mime_encode("Subject", "測試頭", $preferences), PHP_EOL;
// Subject: =?GBK?Q?=B2=E2=CA=D4=CD=B7?=
$preferences["scheme"] = "B";
echo iconv_mime_encode("Subject", "測試頭", $preferences), PHP_EOL;
// Subject: =?GBK?B?suLK1M23?=
iconv_mime_encode() 函式就是用於進行 mime 頭編碼的函式。第一個引數是 mime 欄位名,第二個引數是欄位值,第三個函式就是我們進行編碼的引數了。編碼引數的內容通過欄位名就可以看出來,從什麼編碼轉換成什麼編碼,行的長度多少,換行符是什麼。另外它還有一個 scheme 欄位,就是用於指定編碼結果的型別,如果設定的是 B ,那麼編碼結果就會再加一層 base64 操作。
總結
是不是感覺奇怪的小姿勢又增加了呀?沒錯,在沒刷文件之前我也只知道一個 iconv 而已。甚至在學習了這些內容之後我才發現了郵件資訊原來是這樣編碼的,自己都感覺自己一下子高大上了。好了,不說廢話了,自己動手試試吧!
測試程式碼:
https://github.com/zhangyue0503/dev-blog/blob/master/php/202011/source/2.學習PHP中的iconv擴充套件相關函式.php
參考文件:
https://www.cnblogs.com/onelikeone/p/7865596.html
https://www.php.net/manual/zh/book.iconv.php
關注公眾號:【硬核專案經理】獲取最新文章
新增微信/QQ好友:【xiaoyuezigonggong/149844827】免費得PHP、專案管理學習資料
知乎、公眾號、抖音、頭條搜尋【硬核專案經理】
B站ID:482780532