python 中文編碼亂碼問題的解決

前言:

中文編碼問題一直是程序員頭疼的問題,而Python2中的字符編碼足矣令新手抓狂。本文將盡量用通俗的語言帶大傢徹底的瞭解字符編碼以及Python2和3中的各種編碼問題。

一、什麼是字符編碼。

要徹底解決字符編碼的問題就不能不去瞭解到底什麼是字符編碼。計算機從本質上來說隻認識二進制中的0和1,可以說任何數據在計算機中實際的物理表現形式也就是0和1,如果你將硬盤拆開,你是看不到所謂的數字0和1的,你能看到的隻是一塊光滑閃亮的磁盤,如果你用足夠大的放大鏡你就能看到磁盤的表面有著無數的凹凸不平的元件,**凹下去的代表0,突出的代表1,**這就是計算機用來表現二進制的方式。

1.ASCII

現在我們面臨瞭第一個問題:如何讓人類語言,比如英文被計算機理解?我們以英文為例,英文中有英文字母(大小寫)、標點符號、特殊符號。如果我們將這些字母與符號給予固定的編號,然後將這些編號轉變為二進制,那麼計算機明顯就能夠正確讀取這些符號,同時通過這些編號,計算機也能夠將二進制轉化為編號對應的字符再顯示給人類去閱讀。由此產生瞭我們最熟知的ASCII碼。ASCII 碼使用指定的7 位或8 位二進制數組合來表示128 或256 種可能的字符。這樣在大部分情況下,英文與二進制的轉換就變得容易多瞭。

2.GB2312

然而,雖然計算機是美國人發明的,但是全世界的人都在使用計算機。現在出現瞭另一個問題:如何讓中文被計算機理解?這下麻煩瞭,中文不像拉丁語系是由固定的字母排列組成的。ASCII 碼顯然沒辦法解決這個問題,為瞭解決這個問題中國國傢標準總局1980年發佈《信息交換用漢字編碼字符集》提出瞭GB2312編碼,用於解決漢字處理的問題。1995年又頒佈瞭《漢字編碼擴展規范》(GBK)。GBK與GB 2312—1980國傢標準所對應的內碼標準兼容,同時在字匯一級支持ISO/IEC10646—1和GB 13000—1的全部中、日、韓(CJK)漢字,共計20902字。這樣我們就解決瞭計算機處理漢字的問題瞭。

3.Unicode

現在英文和中文問題被解決瞭,但新的問題又出現瞭。全球有那麼多的國傢不僅有英文、中文還有阿拉伯語、西班牙語、日語、韓語等等。難不成每種語言都做一種編碼?基於這種情況一種新的編碼誕生瞭:Unicode。Unicode又被稱為統一碼、萬國碼;它為每種語言中的每個字符設定瞭統一並且唯一的二進制編碼,以滿足跨語言、跨平臺進行文本轉換、處理的要求。 Unicode支持歐洲、非洲、中東、亞洲(包括統一標準的東亞象形漢字和韓國表音文字)。這樣不管你使用的是英文或者中文,日語或者韓語,在Unicode編碼中都有收錄,且對應唯一的二進制編碼。這樣大傢都開心瞭,隻要大傢都用Unicode編碼,那就不存在這些轉碼的問題瞭,什麼樣的字符都能夠解析瞭。

4.UTF-8

但是,由於Unicode收錄瞭更多的字符,可想而知它的解析效率相比ASCII碼和GB2312的速度要大大降低,而且由於Unicode通過增加一個高字節對ISO Latin-1字符集進行擴展,當這些高字節位為0時,低字節就是ISO Latin-1字符。對可以用ASCII表示的字符使用Unicode並不高效,因為Unicode比ASCII占用大一倍的空間,而對ASCII來說高字節的0對他毫無用處。為瞭解決這個問題,就出現瞭一些中間格式的字符集,他們被稱為 通用轉換格式,即UTF(Unicode Transformation Format)。而我們最常用的UTF-8就是這些轉換格式中的一種。在這裡我們不去研究UTF-8到底是如何提高效率的,你隻需要知道他們之間的關系即可。

總結:

1. 為瞭處理英文字符,產生瞭ASCII碼。
2. 為瞭處理中文字符,產生瞭GB2312。
3. 為瞭處理各國字符,產生瞭Unicode。
4. 為瞭提高Unicode存儲和傳輸性能,產生瞭UTF-8,它是Unicode的一種實現形式。

二、Python2中的字符編碼

1. Python2中默認的字符編碼是ASCII碼,也就是說Python在處理數據時,隻要數據沒有指定它的編碼類型,Python默認將其當做ASCII碼來進行處理。這個問題最直接的表現在當我們編寫的python文件中包含有中文字符時,在運行時會提示出錯。如圖:

在這裡插入圖片描述


在這裡插入圖片描述

這個問題出現的原因是:Python2會將整個python腳本中的內容當做ASCII碼去處理,當腳本中出現瞭中文字符,比如這裡的“小明”,我們知道ASCII碼是不能夠處理中文字符的,所以出現瞭這個錯誤。 解決的辦法是:在文件頭部加入一行編碼聲明 如圖:

# -*- coding: utf-8 -*-

在這裡插入圖片描述

這樣,Python在處理這個腳本時,會用UTF-8的編碼去處理整個腳本,就能夠正確的解析中文字符瞭。

2. Python2中字符串有str和unicode兩種類型

在這裡插入圖片描述

上圖中展現出瞭Python2中字符串的兩種類型:

name變量被賦予瞭一個字符串“小明”;

unicode_name是name變量的unicode格式,這裡我們使用瞭decode()方法,我們會在後面的內容中詳細講解;

兩者在終端中返回瞭不同的字節串,type返回瞭不同的數據類型,但print打印出瞭相同的輸出。

這裡我們註意到一個“字節串”的名稱,字節串是指該字符串在python中的標準形式,也就是說無論一個字符串是什麼樣的編碼,在python中都會有一串字節串來進行表示。字節串是沒有編碼的,對應的最終交給計算機處理的數據形式。

3. Python2中可以直接查看到unicode的字節串。

在上圖中,輸入unicode_name的返回值,是一個unicode字節串,我們能夠直接看到這個字節串。而在python3中,我們將不能直接看到unicode字節串,它會被顯示為中文的“小明”;因為python3默認使用unicode編碼,unicode字節串將被直接處理為中文顯示出來。

總結:

1. Python2中默認的字符編碼是ASCII碼。
2. Python2中字符串有str和unicode兩種類型。str有各種編碼的區別,unicode是沒有編碼的標準形式。
3. Python2中可以直接查看到unicode的字節串。

三、decode()與encode()方法

前面我們說瞭這麼多都是為瞭這一節做鋪墊,現在我們開始來處理Python2中的字符編碼問題。我們首先要學習Python為我們提供的兩個轉換編碼的方法decode()與encode()。

decode()方法將其他編碼字符轉化為Unicode編碼字符。
encode()方法將Unicode編碼字符轉化為其他編碼字符。

話不多說,直接上圖:

在這裡插入圖片描述
chardet模塊可以檢測字符串編碼,沒有該模塊的可以用pip install chardet安裝。

首先解釋一下為什麼name=”小明” 這裡的小明是一個utf-8編碼的字符。因為我使用的是Ubuntu14.04操作系統,系統默認的字符編碼就是UTF-8,所以當我在終端將一個中文輸入時,系統就會自動將這個中文字符以UTF-8的編碼傳遞給Python。所以如果你的系統是windows操作系統,而大多數情況下windows的系統編碼默認是gb2312,那麼在windows下做上圖的測試“小明”這個字符就是gb2312編碼。

上圖中我們將utf-8編碼的name通過decode()方法轉換為unicode_name,然後通過encode()方法將unicode_name轉換為gb2312_name。這時我們再用print去輸出gb2312編碼的字符時缺產生瞭一個奇怪的輸出。這是因為我的操作系統使用的是UTF-8編碼,對於gb2312編碼的字符自然不能夠正確解析,如果我們將該gb2312的字節串放在windows下輸出就能夠得到我們想要的中文,如圖:

在這裡插入圖片描述

所謂亂碼本質上是系統編碼與所提供字符的編碼不一致導致的,我們舉一個例子:

小明的電腦中存瞭一個utf-8的字母A,存儲在計算機中是1100001;

小紅的電腦中也存瞭一個gb2312的字母A,存儲在計算機中是11000010;

當小明與小紅交換信息時,各自的計算機就不會把對方傳遞過來的A識別為字母A,可能認為這是字母B。

所以當我們需要操作系統正確的輸出一個字符時,除瞭要知道該字符的字符編碼,也要知道自己系統所使用的字符編碼。如果系統使用的是UTF-8編碼,處理的卻是gb2312的字符就會出現所謂“亂碼”。

一個Tips:decode()方法與在字符串前加u的方法實現的效果相同比如u’小明’

在這裡插入圖片描述

總結:

  • Python2的對於字符編碼的轉換要以unicode作為“中間人”進行轉化。
  • 知道自己系統的字符編碼(Linux默認utf-8,Windows默認GB2312),對癥下藥。

四、一個字符編碼的例子

在Linux操作系統下使用python2下獲取網易首頁的title,並以正確的中文顯示出來

在這裡插入圖片描述

163的首頁使用的字符編碼是gb2312,而我們前面提到過Linux下的默認字符編碼為UTF-8,我們測試一下直接提取會不會出現亂碼問題。

在這裡插入圖片描述

我們發現確實提取到的title並不能正確顯示,因為網頁中已經聲明瞭它是一個gb2312的字符編碼,而我的系統中默認的字符編碼為UTF-8顯然,我必須要將title轉換為UTF-8的字符。

在這裡插入圖片描述

其實由於utf-8屬於unicode字符編碼,在Linux中我們可以直接打印出unicode編碼的字符。如:

在這裡插入圖片描述

現在我們在Windows用Python2來做另一個實驗,這次我們換成百度首頁的title:

在這裡插入圖片描述

這次我們發現網頁上的字符編碼為utf-8,那麼我在Windows下會不會出現亂碼:

在這裡插入圖片描述

所以我們再次強調:亂碼本質上是系統編碼與所提供字符的編碼不一致導致的

在Pyhon3中字符編碼有瞭很大改善最主要的有以下幾點:

  • Python 3的源碼.py文件 的默認編碼方式為UTF-8,所以在Python3中你可以不用在py腳本中寫coding聲明,並且系統傳遞給python的字符不再受系統默認編碼的影響,統一為unicode編碼。
  • 將字符串和字節序列做瞭區別,字符串str是字符串標準形式與2.x中unicode類似,bytes類似2.x中的str有各種編碼區別。bytes通過解碼轉化成str,str通過編碼轉化成bytes。

PS:有一個小問題被許多新手所困擾,我們來看一下圖片

在這裡插入圖片描述

我們看到當一個中文字符出現在一個list(或tuple、dict)中時,它並不會被顯示為一個中文而是字節串。但當該字符串從list中提取出來再print時就能夠正常顯示為中文。字節串是所有字符在python中的“本質”形態,所以你可以簡單的理解為list中呈現出的字節串是給計算機看的

到此這篇關於python 中文編碼亂碼問題的解決的文章就介紹到這瞭,更多相關python 中文編碼亂碼內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: