實例解析:PHP程序開發中的中文編碼問題

2020-12-17 站長之家

PHP程序設計中中文編碼問題曾經困擾很多人,導致這個問題的原因其實很簡單,每個國家(或區域)都規定了計算機信息交換用的字符編碼集,如美國的擴展 ASCII 碼, 中國的 GB2312-80,日本的 JIS 等。作為該國家/區域內信息處理的基礎,字符編碼集起著統一編碼的重要作用。字符編碼集按長度分為 SBCS(單字節字符集),DBCS(雙字節字符集)兩大類。早期的軟體(尤其是作業系統),為了解決本地字符信息的計算機處理,出現了各種本地化版本(L10N),為了區分,引進了 LANG, Codepage 等概念。但是由於各個本地字符集代碼範圍重疊,相互間信息交換困難;軟體各個本地化版本獨立維護成本較高。因此有必要將本地化工作中的共性抽取出來,作一致處理,將特別的本地化處理內容降低到最少。這也就是所謂的國際化(118N)。各種語言信息被進一步規範為 Locale 信息。處理的底層字符集變成了幾乎包含了所有字形的 Unicode。

現在大部分具有國際化特徵的軟體核心字符處理都是以 Unicode 為基礎的,在軟體運行時根據當時的ocale/Lang/Codepage 設置確定相應的本地字符編碼設置,並依此處理本地字符。在處理過程中需要實現 Unicode 和本地字符集的相互轉換,甚或以 Unicode 為中間的兩個不同本地字符集的相互轉換。這種方式在網絡環境下被進一步延伸,任何網絡兩端的字符信息也需要根據字符集的設置轉換成可接受的內容。

資料庫中的字符集編碼問題

流行的關係資料庫系統都支持資料庫字符集編碼,也就是說在創建資料庫時可以指定它自己的字符集設置,資料庫的數據以指定的編碼形式存儲。當應用程式訪問數據時,在入口和出口處都會有字符集編碼的轉換。對於中文數據,資料庫字符編碼的設置應當保證數據的完整性。GB2312、GBK、UTF-8 等都是可選的資料庫字符集編碼;當然我們也可以選擇 ISO8859-1 (8-bit),只是我們得在應

用程序寫數據之前先將 16Bit 的一個漢字或 Unicode 拆分成兩個 8-bit 的字符,讀數據之後也需要將兩個字節合併起來,同時還要判別其中的 SBCS 字符,因此我們並不推薦採用 ISO8859-1 作為資料庫字符集編碼。這樣不但沒有充分利用資料庫自身的字符集編碼支持,而且同時也增加了編程的複雜度。編程時,可以先用資料庫管理系統提供的管理功能檢查其中的中文數據是否正確。

PHP 程序在查詢資料庫之前,首先執行 mysql_query("SET NAMES xxxx"); 其中 xxxx 是你網頁的編碼(charset=xxxx),如果網頁中 charset=utf8,則 xxxx=utf8,如果網頁中 charset=gb2312,則xxxx=gb2312,幾乎所有 WEB 程序,都有一段連接資料庫的公共代碼,放在一個文件裡,在這文件裡,加入 mysql_query("SET NAMES xxxx") 就可以了。


SET NAMES 顯示客戶端發送的 SQL 語句中使用什麼字符集。因此,SET NAMES 'utf-8' 語句告訴伺服器「將來從這個客戶端傳來的信息採用字符集 utf-8」。它還為伺服器發送回客戶端的結果指定了字符集(例如,如果你使用一個 SELECT 語句,它表示列值使用了什麼字符集)。


定位問題時常用的技巧


定位中文編碼問題通常採用最笨的也是最有效的辦法―在你認為有嫌疑的程序處理後列印字符串的內碼。通過列印字符串的內碼,你可以發現什麼時候中文字符被轉換成 Unicode,什麼時候Unicode 被轉回中文內碼,什麼時候一個中文字成了兩個 Unicode 字符,什麼時候中文字符串被轉成了一串問號,什麼時候中文字符串的高位被截掉了……


取用合適的樣本字符串也有助於區分問題的類型。如:"aa啊 aa?@aa" 等中英相間,GB、GBK特徵字符均有的字符串。一般來說,英文字符無論怎麼轉換或處理,都不會失真(如果遇到了,可以嘗試著增加連續的英文字母長度)。

解決各種應用的亂碼問題

1) 使用 標籤設置頁面編碼

這個標籤的作用是聲明客戶端的瀏覽器用什麼字符集編碼顯示該頁面,xxx 可以為 GB2312、GBK、UTF-8(和 MySQL 不同,MySQL 是 UTF8)等等。因此,大部分頁面可以採用這種方式來告訴瀏覽器顯示這個頁面的時候採用什麼編碼,這樣才不會造成編碼錯誤而產生亂碼。但是有的時候我們會發現有了這句還是不行,不管 xxx 是哪一種,瀏覽器採用的始終都是一種編碼,這個情況我後面會談到。

請注意, 是屬於 HTML 信息的,僅僅是一個聲明,僅表明伺服器已經把 HTML 信息傳到了瀏覽器。

2) header("content-type:text/html; charset=xxx");

這個函數 header() 的作用是把括號裡面的信息發到 http 標頭。如果括號裡面的內容為文中所說那樣,那作用和 標籤基本相同,大家對照第一個看發現字符都差不多的。但是不同的是如果有這段函數,瀏覽器就會永遠採用你所要求的 xxx 編碼,絕對不會不聽話,因此這個函數是很有用的。為什麼會這樣呢?那就得說說 http 標頭和 HTML信息的差別了:

http 標頭是伺服器以 http 協議傳送 HTML 信息到瀏覽器前所送出的字串。而 標籤是屬於 HTML 信息的,所以 header() 發送的內容先到達瀏覽器,通俗點就是 header() 的優先級高於 (不知道可不可以這樣講)。假如一個 php 頁面既有header("content-type:text/html;charset=xxx"),又有,瀏覽器就只認前者 http 標頭而不認 meta 了。當然這個函數只能在 php 頁面內使用。

同樣也留有一個問題,為什麼前者就絕對起作用,而後者有時候就不行呢?這就是接下來要談的Apache 的原因了。

3) AddDefaultCharset

Apache 根目錄的 conf 文件夾裡,有整個 Apache 的配置文檔 httpd.conf。

用文本編輯器打開 httpd.conf,第 708 行(不同版本可能不同)有 AddDefaultCharset xxx,xxx為編碼名稱。這行代碼的意思:設置整個伺服器內的網頁文件 http 標頭裡的字符集為你默認的 xxx字符集。有這行,就相當於給每個文件都加了一行 header("content-type:text/html; charset=xxx")。這下就明白為什麼明明 設置了是 utf-8,可瀏覽器始終採用 gb2312 的原因。

如果網頁裡有 header("content-type:text/html; charset=xxx"),就把默認的字符集改為你設置的字符集,所以這個函數永遠有用。如果把 AddDefaultCharset xxx 前面加個"#",注釋掉這句,而且頁面裡不含 header("content-type…"),那這個時候就輪到 meta 標籤起作用了。

下面列出以上的優先順序:

.. header("content-type:text/html; charset=xxx")

.. AddDefaultCharset xxx

..

如果你是 web 程式設計師,建議給你的每個頁面都加個header("content-type:text/html;charset=xxx"),這樣就可以保證它在任何伺服器都能正確顯示,可移植性也比較強。

4) php.ini 中的 default_charset 配置:

php.ini 中的 default_charset = "gb2312" 定義了 php 的默認語言字符集。一般推薦注釋掉此行,讓瀏覽器根據網頁頭中的 charset 來自動選擇語言而非做一個強制性的規定,這樣就可以在同臺伺服器上提供多種語言的網頁服務。

結束語

其實 php 開發中的中文編碼並沒有想像的那麼複雜,雖然定位和解決問題沒有定規,各種運行環境也各不盡然,但後面的原理是一樣的。了解字符集的知識是解決字符問題的基礎。不過,隨著中文字符集的變化,不僅僅是 php 編程,中文信息處理中的問題還是會存在一段時間的。

相關焦點

  • PHP 開發中的中文編碼問題
    首頁 > 語言 > 關鍵詞 > php最新資訊 > 正文 PHP 開發中的中文編碼問題
  • PHP網站開發遇到的中文編碼
    PHP程序設計中中文編碼問題曾經困擾很多人,導致這個問題的原因其實很簡單,每個國家(或區域)都規定了計算機信息交換用的字符編碼集,如美國的擴展 ASCII 碼, 中國的 GB2312-80,日本的 JIS 等。作為該國家/區域內信息處理的基礎,字符編碼集起著統一編碼的重要作用。
  • php中文亂碼問題的終極解決方案匯總
    閱讀文章前先來一套視頻教程,回復關鍵詞「320」領取很多新手朋友學習PHP的時候,發現程序中的中文在輸出的時候會出現亂碼的問題,那麼為什麼會出現這種亂碼的情況呢?一般來說,亂碼的出現有2種原因,一種是由於編碼(charset) 設置錯誤,導致瀏覽器以錯誤的編碼來解析,從而出現了滿屏亂七八糟的「天書」,第二種就是文件被以錯誤的編碼打開,然後保存,比如一個文本文件原先是GB2312編碼的,卻以UTF-8編碼打開再保存,就會出現亂碼的問題。本篇文章,就帶大家了解一下,怎麼解決php中亂碼的問題。
  • PHP開發編碼規範特麼要注意
    4.3.1 程序頭注釋塊4.3.2 類的注釋4.3.3 函數和方法的注釋4.3.4 變量或者語句注釋4.4 其他規範(建議)4.4.1 php代碼標記4.4.2 程序文件名、目錄名4.4.3 PHP項目通常的文件目錄結構4.4.4 PHP和HTML代碼的分離問題4.4.5 PHP項目開發中的程序邏輯結構5 特定環境下
  • PHPCMS開發文檔裡看到PHP編碼規範
    註:這是從PHPCMS開發文檔裡看到編碼規範,雖名為PHPCMS的開發規範,但我覺得所有的PHP編程都該如此。寫了那麼多PHP,很多編碼對照這規範都感覺欠缺很多,今後一定要對照糾正。Phpcms 編碼規範1. 引言…. 22. 適用範圍…. 23.
  • php框架開發:實現最簡單的MVC框架實例教程
    需要的朋友可以參考下本文以一個實例的形式講述了PHP實現MVC框架的過程,比較淺顯易懂。具體分析如下:首先,在學習一個框架之前,基本上我們都需要知道什麼是mvc,即model-view-control,說白了就是數據控制以及頁面的分離實現,mvc就是這樣應運而生的,mvc分為了三個層次,而且三個層次各司其職,互不幹擾,首先簡單介紹下,各個層次:view即是視圖,也就是web頁面,control即是控制器 向系統發出指令的工具,model 簡單說是從資料庫中取出數據進行處理
  • PHP中文字符串反轉編碼錯誤解決方式
    在使用PHP處理字符串反轉的時候,我們第一反應是使用PHP的內置函數strrev來處理,思維上是沒有問題的,但是我們需要知道一個問題,函數strrev是否可以處理中文漢字的字符串呢?帶著疑問我們一起來看一下。
  • php解析json格式的文本
    昨天,在我校的高級培訓群裡,有同學諮詢了如何用php解析json格式的文本問題,老師發現這個問題比較典型,在此將相關的知識點總結分享給同學們,希望對其他同學掌握該知識點也能起到一些幫助。細心的同學會發現該同學用的是js對象的表示方法,如果把json內容當成是一個大的對象,那麼裡面包含了cn和en兩個小對象,每個小對象當中又可以拆分為1,2兩個對象,典型的key-value的結構。那麼我們php裡面有一個函數叫get_object_vars()就是用來解析對象格式的數據。於是老師給出了帶有調試信息的代碼,幫助該同學理解php的解析過程(如下圖所示)。
  • php str-ireplace()函數替換字符串/數組中的一些字符
    str_ireplace()函數定義及用法在php中,str_ireplace()函數是使用來替換字符串或數組中的一些字符(不區分大小寫),返回替換後的新字符串或數組,原字符串或數組不變;str_ireplace()函數如果處理的數據是一個數組:該函數將對數組中的每個元素進行查找和替換
  • php字符串常用處理,運算符和幾個常用的字符串函數
    小編自學php大概花了3個多月,說不上精通,但是還算小有成就,獨立開發中小型網站還是不成問題的。小編大學是計算機系的,學過很多計算機語言(c語言、java、彙編、c#等),所以自學php就相當的快。為了讓更多的php學習者了解到php更多的知識,小編結合自己的理解和自學時所做的筆記,整理了一些知識點。藉助百家號這個大平臺分享給大家,希望對大家有所幫助!
  • PHP中JSON的應用
    XML的解析,恐怕已經不是什麼難題了,特別是PHP5,大量的XML解析器的湧現,如最輕量級的SimpleXML。不過對於AJAX來說,XML的解析更傾向於前臺Javascript的支持度。我想所有解析過XML的人,都會因樹和節點而頭大。不可否認,XML是很不錯的數據存儲方式,但是其靈活恰恰造成了其解析的困難。當然,這裡所指的困難,是相對於本文的主角--JSON而言。JSON為何物?
  • 字符編碼常識及常見問題解析
    常見的中文GB2312(國家簡體中文字符集)就是用兩個字節來表示一個漢字(注意是表示一個漢字,對於拉丁字母,GB2312還是是用一個字節來表示以兼容ASCII)。我們用下表來說明各中文編碼之間的規則和兼容性。
  • 如何正確解決Python中的中文編碼問題?
    這種異常在Python中很容易遇到,尤其是在Python2.x中,是一個很讓初學者費解頭疼的問題。不過,如果你理解了Python的Unicode,並在編碼中遵循一定的原則,這種編碼問題還是比較容易理解和解決的。
  • 全網最詳細的常見PHP漏洞全方面解析
    程序可以改變的環境變量的前綴,如:safe_mode_allowed_env_vars = PHP_ , 當這個選項的值為空時,那麼 php 可以改變任何環境變量safe_mode_protected_env_vars = string用來指定 php 程序不可改變的環境變量的前綴3、限制外部程序的執行safe_mode_exec_dir
  • PHP一些常見的漏洞梳理
    其中 :https://www.cnblogs.com/xiaoqiyue/p/10951836.htmlhttps://www.cnblogs.com/endust/p/11804767.html一、php文件包含漏洞在開發中,開發人員會將一些重複使用的代碼函數匯總放到單個文件中
  • 培養自己的php編碼規範,養成一個好習慣
    編碼規範包含兩大塊,代碼規範和注釋規範其實我們所寫的php腳本,其實也就是由兩大塊組成的,即對代碼的書寫、對代碼的注釋!不同的框架,甚至不同的公司,對這方面都會有所不同,這裡只是已將之言,僅僅是對自己的書寫規範進行一個總結!
  • PHP編寫基本的Socket程序
    我的目的因為在以後的學習中,我可能會用到網絡方面的內容,但同時很多寫 PHP 的 coder 都沒寫過 socket 程序,但是肯定聽說過它,也肯定聽說過網絡編程這個詞;所以為了今後的學習,我打算在這裡先簡單的講解下相關知識,本篇博文自帶實例程序,代碼託管在碼云:php-socket-base-code
  • PHP接口特性的實例講解
    PHP接口特性的實例講解 如果你對於PHP接口特性還有些模糊,那麼我們可以通過對學習JAVA接口的知識,來說明PHP接口的一些相關知識。javaeye博客|2009-11-23 20:16 我們在通過學習PHP語言的時候經常會遇到一些問題
  • python爬蟲小知識,中文在url中的編碼解碼
    有時候我們做爬蟲經常會遇到這種編碼格式,大概的樣式為 %xx%xx%xx,對於這部分編碼,python提供了一個quote的方法來編碼,對應的解碼為unquote方法。編碼、解碼直接上實例額。。。大家請忽略那些波浪線。。。因為我的pycharm很舊了,版本沒有更新,所以,用f-string會提示錯誤,但實際上能運行的。
  • PHP中__get()方法詳解
    PHP中把以兩個下劃線__開頭的方法稱為魔術方法(Magic methods),這些方法在PHP中充當了舉足輕重的作用。