學習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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章