學(xué)習(xí)PHP中的iconv擴(kuò)展相關(guān)函數(shù)

想必 iconv 這個(gè)擴(kuò)展的相關(guān)函數(shù)大家多少都接觸過,做為 PHP 的默認(rèn)擴(kuò)展它已經(jīng)存在了很久,也是我們在操作字符編碼時(shí)經(jīng)常會使用的函數(shù)。不過除了 iconv() 這個(gè)函數(shù)外,你還知道它的其它函數(shù)嗎?今天,我們就來學(xué)習(xí)一下 iconv 擴(kuò)展中的各種好玩的函數(shù)。

iconv 設(shè)置及獲取信息

首先,就是我們可以設(shè)置 iconv 擴(kuò)展中默認(rèn)定義的輸出和輸出字符編碼格式。

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() 接收兩個(gè)參數(shù),一個(gè)是設(shè)置的屬性類型,一個(gè)是設(shè)置的編碼格式。屬性類型包括 internal_encoding 、 input_encoding 和 output_encoding ,分別代表內(nèi)部的、輸入的、輸出的編碼格式。在這段測試代碼中,我們將 internal_encoding 設(shè)置為 UTF8 ,將 output_encoding 設(shè)置為 ISO-8859-1 ,然后使用 iconv_get_encoding() 打印出當(dāng)前環(huán)境中相關(guān)的 iconv 屬性設(shè)置信息,可以看到,在默認(rèn)情況下當(dāng)前環(huán)境中的 input_encoding 也是 UTF8 格式。

不過需要說明的是,iconv_set_encoding() 已經(jīng)是不推薦使用的函數(shù)了,或者說不推薦使用這個(gè)函數(shù)來設(shè)置上面的三種屬性類型,它們會報(bào)出過時(shí)警告信息?,F(xiàn)在更推薦直接使用 php.ini 中的 default_charset 來進(jìn)行設(shè)置。

iconv 根據(jù)編碼獲取字符長度、指定位置及截取字符串

在面對中文字符串的操作時(shí),我們使用默認(rèn)的 strlen() 之類的函數(shù)返回的中文字符長度是不正確的,這就牽涉到編碼的問題。一般情況下,UTF8 是占三個(gè)字節(jié),而 GBK 是占兩個(gè)字節(jié),所以說一個(gè)漢字對于 strlen() 來說如果是在 UTF8 環(huán)境中會返回 3 。當(dāng)然,現(xiàn)在大多數(shù)情況下我們會使用 MB 庫擴(kuò)展的相關(guān)函數(shù)來處理這種問題,不過 iconv 也為我們提供了幾個(gè)用于字符串操作的函數(shù)。

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() 就是獲取字符串長度的,如果不給第二個(gè)參數(shù)就按默認(rèn)的字符集編碼來獲取字符串長度。在測試代碼中可以看出,同樣八個(gè)中文字的內(nèi)容,使用不同的編碼返回的數(shù)量是不相同的。在這里,我們發(fā)現(xiàn) iconv 中對于 GBK 的中文是 1.5 個(gè)字節(jié),也就是 8 個(gè)中文字占用了 12 個(gè)字節(jié)的長度。

iconv_strpos() 和 iconv_strrpos() 和 strpos() 的作用一樣,返回某個(gè)字符第一次出現(xiàn)的位置,一個(gè)是從前往后(從左往右),另一個(gè)是從后往前(從右往左)。它們的第三個(gè)參數(shù)是偏移量,也就是查找到指定字符后再偏移幾個(gè)單位。從這里我們可以看出,對于 GBK 編碼的操作是有問題的,因?yàn)樵?iconv 中,GBK 是 1.5 個(gè)字節(jié),這樣會帶來單個(gè)字符無法定位的問題。

iconv_substr() 很明顯地就是截取字符串的函數(shù)了,同樣我們要根據(jù)編碼格式來指定它的截取位置。

iconv 轉(zhuǎn)換字符編碼

接下來就是本尊 iconv() 函數(shù)的使用的了,其實(shí)它反而沒什么可講的,將指定的編碼轉(zhuǎn)換成另外一種編碼而已,相信這個(gè)函數(shù)大家都不陌生。

$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:''
// }

我們找到的這個(gè)淘寶用于查找手機(jī)號相關(guān)信息的開放接口,返回的正好是 GBK 類型的數(shù)據(jù)。當(dāng)我們直接打印結(jié)果時(shí),在 UTF8 環(huán)境下它就會輸出亂碼信息。這時(shí),我們通過 iconv() 函數(shù)就能夠輕松地將編碼轉(zhuǎn)換成 UTF8 格式,并正確打印出了結(jié)果。第三個(gè)測試中,我們在要轉(zhuǎn)換到的字符集編碼類型后面加上了 //IGNORE ,目的就是忽略無法轉(zhuǎn)換的內(nèi)容,所以可以看出在最后我們轉(zhuǎn)換到錯(cuò)誤的 ISO-8859-1 時(shí),中文信息就全都沒有了,因?yàn)樗鼈儫o法轉(zhuǎn)換就被忽略掉了。

mime 郵件頭操作

最后我們再看一個(gè)非常不常用的內(nèi)容,那就是 iconv 還可以直接轉(zhuǎn)換 mime 頭中的編碼內(nèi)容信息。這個(gè) mime 頭信息其實(shí)就是標(biāo)示當(dāng)前文件或者內(nèi)容的 mime 類型。平常我們會根據(jù)它來判斷上傳的文件是否正確,除些之外,在郵件發(fā)送中,這個(gè) mime 頭的使用也非常廣泛。如果做過郵件發(fā)送接收相關(guān)的開發(fā)并且抓過包的同學(xué)一定見過下面的內(nèi)容。

headers_string = <<<EOF
Subject: =?UTF-8?B?UHLDvGZ1bmcgUHLDvGZ1bmc=?=
To: example@example.com
Date: Thu, 1 Jan 1970 00:00:00 +0000
Message-Id: <example@example.com>
Received: from localhost (localhost [127.0.0.1]) by localhost
    with SMTP id example for <example@example.com>;
    Thu, 1 Jan 1970 00:00:00 +0000 (UTC)
    (envelope-from example-return-0000-example=example.com@example.com)
Received: (qmail 0 invoked by uid 65534); 1 Thu 2003 00:00:00 +0000
EOF;

Subject 字符就是郵件的標(biāo)題,To 就是發(fā)送人的郵件地址。在這里我們主要看一下 Subject 的內(nèi)容,它的開頭就有一段描述這個(gè)字段使用的編碼信息的內(nèi)容,?UTF-8 ,然后后面是一堆看不懂的東西。其實(shí)我們簡單地能看出來這是一個(gè) base64 編碼的內(nèi)容,如果將它解碼在對應(yīng)的編碼內(nèi)容下就能看到原文信息。不過,這個(gè)時(shí)候我們也可以使用 iconv 來直接轉(zhuǎn)換它的編碼。

$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) "example@example.com"
//     ["Date"]=>
//     string(30) "Thu, 1 Jan 1970 00:00:00 +0000"
//     ["Message-Id"]=>
//     string(21) "<example@example.com>"
//     ["Received"]=>
//     array(2) {
//       [0]=>
//       string(204) "from localhost (localhost [127.0.0.1]) by localhost with SMTP id example for <example@example.com>; Thu, 1 Jan 1970 00:00:00 +0000 (UTC) (envelope-from example-return-0000-example=example.com@example.com)"
//       [1]=>
//       string(57) "(qmail 0 invoked by uid 65534); 1 Thu 2003 00:00:00 +0000"
//     }
//   }

看到了么?不僅直接轉(zhuǎn)了編碼,而且還將 mime 頭格式轉(zhuǎn)換成了 PHP 中的數(shù)組格式。當(dāng)然,我們這里測試的代碼是將正常的內(nèi)容轉(zhuǎn)換到 ISO-8859-1 了,反而是出現(xiàn)了亂碼。下面我們再拿一個(gè)中文郵件的例子來看下。

$headers_string = <<<EOF
Return-Path: <bluesky7810@163.com>
Delivered-To: bhw98@sina.com
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==?=" <bluesky7810@163.com>
To: "bhw98" <bhw98@sina.com>
Cc: <bhwang@jlonline.com>
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) "<bluesky7810@163.com>"
//     ["Delivered-To"]=>
//     string(14) "bhw98@sina.com"
//     ["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) ""藍(lán)藍(lán)的天
//   " <bluesky7810@163.com>"
//     ["To"]=>
//     string(24) ""bhw98" <bhw98@sina.com>"
//     ["Cc"]=>
//     string(21) "<bhwang@jlonline.com>"
//     ["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;"
//   }

這個(gè)中文郵件 mime 頭的 Subject 指定的是 GB2312 。通過 iconv_mime_decode_headers() 函數(shù)我們將整個(gè)頭信息中的內(nèi)容都轉(zhuǎn)換成了 UTF8 ,這時(shí)就可以正常顯示所有的內(nèi)容信息了。當(dāng)然,我們也可以對單個(gè)的 mime 字段進(jìn)行轉(zhuǎn)碼。

echo iconv_mime_decode("Subject: =?gb2312?B?ztK1xLbgtK6/2rPM0PI=?=", 0, 'UTF-8'), PHP_EOL; // Subject: 我的多串口程序

除了對于接收的信息進(jìn)行編碼轉(zhuǎn)換之外,我們還可以自己編碼相關(guān)的內(nèi)容進(jìn)行發(fā)送使用。

$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() 函數(shù)就是用于進(jìn)行 mime 頭編碼的函數(shù)。第一個(gè)參數(shù)是 mime 字段名,第二個(gè)參數(shù)是字段值,第三個(gè)函數(shù)就是我們進(jìn)行編碼的參數(shù)了。編碼參數(shù)的內(nèi)容通過字段名就可以看出來,從什么編碼轉(zhuǎn)換成什么編碼,行的長度多少,換行符是什么。另外它還有一個(gè) scheme 字段,就是用于指定編碼結(jié)果的類型,如果設(shè)置的是 B ,那么編碼結(jié)果就會再加一層 base64 操作。

總結(jié)

是不是感覺奇怪的小姿勢又增加了呀?沒錯(cuò),在沒刷文檔之前我也只知道一個(gè) iconv 而已。甚至在學(xué)習(xí)了這些內(nèi)容之后我才發(fā)現(xiàn)了郵件信息原來是這樣編碼的,自己都感覺自己一下子高大上了。好了,不說廢話了,自己動手試試吧!

測試代碼:

https://github.com/zhangyue0503/dev-blog/blob/master/php/202011/source/2.學(xué)習(xí)PHP中的iconv擴(kuò)展相關(guān)函數(shù).php

參考文檔:

https://www.cnblogs.com/onelikeone/p/7865596.html

https://www.php.net/manual/zh/book.iconv.php

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容