python --- 字符編碼學習小結(一)

       上半年的KPI,是用python作一個測試樁系統,如今系統框架基本也差很少定下來了。裏面有用到新學的工廠設計模式以及以及經常使用的大牛寫框架的業務邏輯和python小技巧。發現以前本身寫的代碼仍是面向過程思想的多,基本沒有面向對象的思想,近半年看的代碼給了很大的觸動,我須要升級個人技能了,因而也花了挺多時間在這個KPI學習上,如今先總結下在作這個系統時我所面臨到的python的字符編碼問題。html

  字符編碼問題,若是處理有問題,可能直接就報錯了;若是處理不得當,中文就會顯示亂碼。這是最初接觸字符編碼遇到問題最簡潔的表達。通常都會遇到如下幾個問題(逐漸升級的頭疼問題):python

  1. 使用編輯器或者Python IDE直接打印中文,報錯或者亂碼;mysql

  2. 前臺傳輸過來包含中文的字符串在後臺打印,報錯或者亂碼;web

  3. DB交互,從DB查詢或者insert的中文,操做時報錯或者亂碼;sql

  4. 文件操做,從文件中讀取或者寫文件時,報錯或者亂碼;數據庫

  除了第四個問題,暫時我沒遇到過,前面3個問題,我遇到好幾回了;遇到字符編碼問題,若是想着就解決當前的問題,隨便在百度上,狂搜各類方法亂試,可能還真能解決這個問題,可是耗時太長了,下次再遇到同樣會頭炸開,我以爲學習解決這個問題須要通過如下幾個過程:設計模式

  1. 簡單的瞭解計算機字符編碼的發展史,ASCII編碼是啥? EASCII是啥?GBK是啥?unicode是啥?UTF-8是啥?UTF-16是啥?若是這些最初級的基本概念不瞭解,後面學習會很困難。服務器

  2. 理解python的字符串的數據類型,str和unicode,二者之間是如何轉換的?網絡

  3. mysql支持哪些字符?mysql的環境變量跟字符集相關的有七,八個,都是神馬意思?最簡單的要怎麼使用?app

      第一:字符編碼的前世此生

      1989年,荷蘭人Guido van Rossum發明python語言,第一個公開發行版發行於1991年,當時在那個時代,是不關心編碼問題的並且英文字符個數自己也是有限的,26個字母,10個數字,標點符號,鍵盤上加起來能輸入的字符就一百多個,用一個字節來存儲已經夠了,8個比特位能存256個字符。因而美國人制定了一套字符編碼標準ASCII。最開始的ASCII只定義了128個字符,包括96個字符和32個控制符,所以 ASCII 只使用了一個字節的後7位,最高位都爲0。

  隨着時代的進步,計算機開始普及到千家萬戶,計算機進入中國面臨的一個問題就是字符編碼,中國的漢字是人類使用頻率最多的文字,常見的漢字就有成千上萬,大大超出了 ASCII 編碼所能表示的字符範圍了,因而中國人本身弄了一套編碼叫 GB2312,GB2312 編碼共收錄了6763個漢字,同時他還兼容 ASCII,GB 2312的出現,基本知足了漢字的計算機處理須要,它所收錄的漢字已經覆蓋中國大陸99.75%的使用頻率,不過 GB2312 仍是不能100%知足中國漢字的需求,對一些罕見的字和繁體字 GB2312 無法處理,後來就在GB2312的基礎上建立了一種叫 GBK 的編碼,GBK 不只收錄了27484個漢字,同時還收錄了藏文、蒙文、維吾爾文等主要的少數民族文字。一樣 GBK 也是兼容 ASCII 編碼的,對於英文字符用1個字節來表示,漢字用兩個字節來標識。

       世界語言種類有多少,計算機的字符編碼相應就會增長多少。因而統一聯盟國際組織提出了Unicode編碼,Unicode的學名是」Universal Multiple-Octet Coded Character Set」,簡稱爲UCS。Unicode有兩種格式:UCS-2和UCS-4。UCS-2就是用兩個字節編碼,一共16個比特位,這樣理論上最多能夠表示65536個字符,不過要表示全世界全部的字符顯示65536個數字還遠遠不過,由於光漢字就有近10萬個,所以Unicode4.0規範定義了一組附加的字符編碼,UCS-4就是用4個字節(實際上只用了31位,最高位必須爲0)。世界上任何一個字符均可以用一個Unicode編碼來表示,一旦字符的Unicode編碼肯定下來後,就不會再改變了。可是Unicode有必定的侷限性,一個Unicode字符在網絡上傳輸或者最終存儲起來的時候,並不見得每一個字符都須要兩個字節,好比一字符「A「,用一個字節就能夠表示的字符,卻使用兩個字節,太浪費空間了。UTF-8(8-bit Unicode Transformation Format)就出現了,UTF-8是一種針對Unicode的可變長度字符編碼,又稱萬國碼。UTF-8用1到6個字節編碼Unicode字符。

 

    第二:python 字符串類型

  python2.X 系統的默認編碼是ASCII,3.X系統就是unicode,因此在2.X系列遇到的編碼問題會更多。

1
2
3
4
5
>>> import sys
>>> sys.getdefaultencoding()
'ascii'
 
 

  如今會遇到第一類問題,在python源代碼文件中若是不顯示地指定編碼的話,將出現語法錯誤:

    這個提示很明顯,非ASCII碼在源代碼中出現了。單純的出現這類問題,能夠採用如下方法解決:

   方法一:在 文件前指定編碼格式

#!/usr/bin/env python
#coding:UTF-8

 方法二:設置整個系統的字符編碼才能解決問題:(結合方法一一塊兒使用)

default_encoding = 'utf-8'
if sys.getdefaultencoding() != default_encoding: reload(sys) sys.setdefaultencoding(default_encoding)

   方法一和方法二都嘗試了還有問題,可能就是你的編輯器的顯示問題了,我使用的是pycharm,在file-setting裏能夠這樣的設置:

   調試IDE編碼和project編碼後,終於能打印中文了:

以前,剛學習python的時候,總結的一段:

  1. 不設置源文件編碼格式,輸入中文,後直接打印,會提示存在‘non-ascii’,編譯不經過

  2. 設置源文件編碼格式爲gbk,輸入中文後,打印亂碼

  3. 設置源文件編碼格式爲gbk,輸入中文s1 = u'測試'後,打印正常

  4. 設置源文件編碼格式爲gbk,輸入中文後,先將字符串解碼decode或者unicode方法,後打印正常

 5. 設置源文件編碼格式爲utf-8,輸入中文後直接輸出正常

  6. 設置工具和工程的默認編碼爲gbk,輸入中文後,打印正常。

     從python2.0開始,就有一種新的數據類型 Unicode Strings,可是在python3的到來,這個概念已經被弱化了。python2.*的默認編碼格式是ASCII碼,而python3.*的默認編碼格式已經換成了Unicode。在python2中和字符串相關的數據類型,分別是strunicode兩種,他們都是basestring的子類,可見str與unicode是兩種不一樣類型的字符串對象。區分一個

變量是字符仍是unicode,可使用type方法:

>>> a=''
>>> type(a) <type 'str'>
>>> a '\xe5\xa5\xbd'
 
>>> b=u''
>>> type(b) <type 'unicode'>
>>> b u'\u597d'

Python中str和unicode之間是如何轉換的呢?這兩種類型的字符串類型之間的轉換就是靠這兩個方法decodeencode

 

      這2個函數的具體使用,就不舉例了。網上這類文章挺多的。這時就有可能遇到第二個問題,前臺傳入的中文在後臺亂碼,沒法處理。通常出現這類問題,都是先後臺編碼格式不一致致使的;

     方法一:統一先後臺編碼格式;

     方法二:若是沒法統一,那取數據的時候就須要進行轉碼處理,以前我遇到一個問題,前臺傳入的是GBK格式的中文,我是作後臺處理的,後臺全系統都是用utf8編碼的,接收到GBK的http請求後,顯示的中文是亂碼的,致使解析那段GBK的xml都報異常。後面作了調整,再接收到前臺的GBK字符串後,首先decode(gbk)再encode(utf8)就成功了。

  第三:操做DB,須要瞭解的mysql字符集。

      使用python對DB的操做,隨時都有可能出現亂碼。網上搜如下2個方法偶爾也能解決問題:

    方法一:conn = MySQLdb.connect(self.host,self.username,self.password,self.database,charset='gbk')

    方法二:

      dbSqlCursor.execute('SET NAMES gbk;')        
      dbSqlCursor.execute('SET CHARACTER SET gbk;')        
      dbSqlCursor.execute('SET character_set_connection=gbk;')
    有時候就不能了,若是不瞭解緣由,估計想撞牆了。因而我鼓足了勇氣,查閱了下mysql官方文檔的這部分的小內容:https://dev.mysql.com/doc/refman/5.7/en/charset-connection.html;看完我知道本身下一部深刻學習的方向了。這裏總結下我粗學的一些皮毛:
       字符集和校驗規則變量涉及到客戶端與服務器的交互。
       數據從客戶端到服務器端的解析入DB的時候,數據字符集是怎麼變化的呢?客戶端和服務器創建鏈接後,客戶端以什麼字符編碼發送數據?服務器收到客戶端發送的數據後,會將語句翻譯成什麼樣的字符集呢?服務器處理後,在將結果集或錯誤消息返回給客戶端以前,服務器應該翻譯什麼字符集?千萬不要自覺得一個mysql環境變量就能完成這些事情,沒那麼簡單。

一、 客戶端和服務器創建鏈接後,客戶端以什麼字符編碼發送數據?

   答:服務器以character_set_client系統變量被設置, conn = MySQLdb.connect(self.host,self.username,self.password,self.database,charset='gbk') 只是設置了客戶端發生數據的字符編碼格式

 

二、服務器在收到語句後將其翻譯成什麼字符集?

   答:服務器使用character_set_connection和collation_connection系統變量。它將收到的字符集從character_set_client轉到character_set_connection,而後再進行處理。

 

三、服務器處理完後,將結果集或錯誤消息返回給客戶端以前,服務器應該翻譯什麼字符集?

    答:character_set_results系統變量指定的服務器返回查詢結果給客戶端的字符。這包括結果數據,如列值,以及結果元數據,如列名和錯誤消息。

    其中,character_set_results這些都是mysql的系統環境變量,要想弄清楚mysql的字符集的系統變量,能夠查看官網:https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_collation_connection;裏面有詳細介紹這幾種mysql字符集系統變量的信息,部分截圖以下:
 
其中: character_set_client系統變量的詳細介紹:
     這些在這個過程當中,有一些mysql的變量比較重要:
一、服務器字符集和整理的character_set_server和collation_server,character_set_connection系統變量的值。
二、默認的數據庫的字符集和整理的character_set_database和collation_database系統變量的值。
三、數據庫返回的字符集,character_set_results。
   基本操做DB時,注意這幾個變量就能解決很問題,官網還有介紹:
   Two statements affect the connection-related character set    variables as a group:
  • SET NAMES 'charset_name'   [COLLATE  'collation_name']

     A SET NAMES 'charset_name'      statement is equivalent to these three statements:         

    SET character_set_client = charset_name; SET character_set_results = charset_name; SET character_set_connection = charset_name;
  • SET CHARACTER SET   'charset_name'         

     A    SET  CHARACTER SET   charset_name statement     is equivalent to these three statements:         

    SET character_set_client = charset_name; SET character_set_results = charset_name; SET collation_connection = @@collation_database;

     這時看看百度常常給的解決方法2,嘿嘿,後面2個操做多餘了吧。

      在實際操做的過程當中,遇到過亂碼的問題,瞭解原理後,明白是由於客戶端在發送數據給DB時,SET character_set_connection=gbk;實際上個人DB的編碼格式是Latin1的;在鏈接的時候,修改charset='latin1',就能夠了。

     還有遇到DB返回SQL Error: 1366: Incorrect string value: "\xE8\xAF\xA6\xE7\xBB\x86…" for column "address" at row 1 問題

     這時,須要肯定數據的字符集,表的字符集,列的字符集;若是列的字符集是指定的,就會直接使用列的字符集,這個優先級最高。後面我修改了列的字符集成utf8,就解決了。

     以上提到的這些問題,其實在mysql的官網這些原理都有詳細的說明。這塊我就沒仔細看了;後面還真能夠好好自學下,部分有些問題遇到後,沒來得及截圖,致使如今寫總結沒啥實例,以後會注意下這個問題。學習真的不是一蹴而就的事情,是一件須要持續不停的事情,鵝廠呆了半年,終於差很少能夠把氣喘勻了,又能夠繼續個人學習之路了。

相關文章
相關標籤/搜索