欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

關(guān)于你不想知道的所有Python3 unicode特性

 更新時(shí)間:2014年11月28日 15:23:11   投稿:mdxy-dxy  
我的讀者知道我是一個(gè)喜歡痛罵Python3 unicode的人。這次也不例外。我將會(huì)告訴你用unicode有多痛苦和為什么我不能閉嘴。我花了兩周時(shí)間研究Python3,我需要發(fā)泄我的失望。在這些責(zé)罵中,仍然有有用的信息,因?yàn)樗涛覀內(nèi)绾蝸?lái)處理Python3。如果沒(méi)有被我煩到,就讀一讀吧

我的讀者知道我是一個(gè)喜歡痛罵Python3 unicode的人。這次也不例外。我將會(huì)告訴你用unicode有多痛苦和為什么我不能閉嘴。我花了兩周時(shí)間研究Python3,我需要發(fā)泄我的失望。在這些責(zé)罵中,仍然有有用的信息,因?yàn)樗涛覀內(nèi)绾蝸?lái)處理Python3。如果沒(méi)有被我煩到,就讀一讀吧。

這次吐槽的內(nèi)容會(huì)不一樣。不會(huì)關(guān)聯(lián)到WSGI或者HTTP及與其相關(guān)的東西。通常,我被告知我應(yīng)該停止抱怨Python3 Unicode系統(tǒng),因?yàn)槲也粚?xiě)別人經(jīng)常寫(xiě)的代碼(HTTP庫(kù)之類的東西),所以我這次準(zhǔn)備寫(xiě)點(diǎn)別的東西:一個(gè)命令行應(yīng)用程序。我寫(xiě)了一個(gè)很方便的庫(kù)叫click來(lái)讓編寫(xiě)它更加簡(jiǎn)單。

注意,我做的是每一個(gè)新手Python程序員做的事情:寫(xiě)一個(gè)命令行應(yīng)用程序。Hello World程序。但是不同以往,我想要確保應(yīng)用程序是穩(wěn)定的并且對(duì)于Python2和Python3的Unicode都是支持的,還能夠進(jìn)行單元測(cè)試。所以接下來(lái)的就是如何來(lái)實(shí)現(xiàn)它。

我們想做什么

在Python3我們作為開(kāi)發(fā)者需要好好使用Unicode。顯然,我覺(jué)得這意味著所有的文本數(shù)據(jù)都是Unicode,所有非文本數(shù)據(jù)都是字節(jié)。在這么美妙的世界里所有的東西只有黑與白,Hello World的例子非常直截了當(dāng)。所以讓我們來(lái)寫(xiě)一些shell工具吧。

這是用Python2形式實(shí)現(xiàn)的應(yīng)用程序:

import sys
import shutil
 
for filename in sys.argv[1:]:
  f = sys.stdin
  if filename != '-':
    try:
      f = open(filename, 'rb')
    except IOError as err:
      print >> sys.stderr, 'cat.py: %s: %s' % (filename, err)
      continue
  with f:
    shutil.copyfileobj(f, sys.stdout)

顯然,命令在處理任何命令行選項(xiàng)的時(shí)候也不是特別好,不過(guò)至少能夠用。所以我們開(kāi)始碼代碼吧。

UNIX里的UNICODE

上面的代碼在Python2是不行的,因?yàn)槟惆抵刑幚碜止?jié)。命令行參數(shù)是字節(jié),文件名是字節(jié),文件內(nèi)容也是字節(jié)。語(yǔ)言衛(wèi)道士會(huì)指出這是不對(duì)的,這樣會(huì)引發(fā)問(wèn)題,但如果你開(kāi)始更多考慮它,你會(huì)發(fā)現(xiàn)這是個(gè)不固定的問(wèn)題。

UNIX是字節(jié),已經(jīng)被定義成了這樣,并且一直會(huì)是這樣。為了理解為什么你需要觀察數(shù)據(jù)傳輸?shù)牟煌瑘?chǎng)景。

  • 終端
  • 命令行參數(shù)
  • 操作系統(tǒng)輸入輸出層
  • 文件系統(tǒng)驅(qū)動(dòng)

順便提一下,這不是數(shù)據(jù)可能通過(guò)的唯一東西,但是我們來(lái)了解一下,在多少場(chǎng)景下我們能了解一個(gè)編碼。答案是一個(gè)也沒(méi)有。至少我們需要理解一個(gè)編碼是終端輸出區(qū)域信息。這個(gè)信息可以用來(lái)展現(xiàn)轉(zhuǎn)換,也能夠理解文本信息所擁有的編碼。

舉個(gè)例子,如果LC_CTYPE的值為en_US.utf-8告訴應(yīng)用程序系統(tǒng)使用US English,并且大部分文本數(shù)據(jù)是utf-8編碼。實(shí)際上還有很多別的變量,不過(guò)我們假定這是我們唯一需要看的。注意LC_CTYPE并不代表所有的數(shù)據(jù)都是utf-8編碼的。它代替通知應(yīng)用程序如何分類文本特性并且什么時(shí)候需要應(yīng)用轉(zhuǎn)換。

這很重要,原因是因?yàn)閏 locale。c locale是POSIX唯一指定的現(xiàn)場(chǎng),它說(shuō)所有ASCII編碼和來(lái)自命令行工具的回復(fù)會(huì)按照POSIX spec里定義的來(lái)對(duì)待。

在我們上面的cat工具里,如果它是比特,沒(méi)有別的方法來(lái)對(duì)待這些數(shù)據(jù)。原因是shell里沒(méi)有指定這數(shù)據(jù)是什么。例如你調(diào)用cat hello.txt,終端會(huì)在對(duì)應(yīng)用程序編碼的時(shí)候?qū)ello.txt進(jìn)行編碼。

但是現(xiàn)在想想這個(gè)例子echo *。Shell會(huì)把目前目錄的所有文件名傳遞給你的應(yīng)用程序。那它們是什么編碼?文件名沒(méi)有編碼!

UNICODE瘋狂

現(xiàn)在一個(gè)用Windows的人看到這里會(huì)說(shuō):弄UNIX的人在搞什么呢。但這還不算悲慘。產(chǎn)生這些工作的原因是一些聰明的人設(shè)計(jì)得這個(gè)系統(tǒng)能夠向后兼容。不像Windows把每個(gè)API都定義兩次,在POSIX上,最好的處理方法是為了顯示的目的將其假定為字節(jié),用默認(rèn)的編碼方式來(lái)編碼。

用上面的cat命令來(lái)舉例。比如有一個(gè)關(guān)于文件無(wú)法打開(kāi)的錯(cuò)誤信息,原始是因?yàn)樗鼈儾淮嬖诨蛘咚鼈兪鞘鼙Wo(hù)的,或者其他任何的原因。我們假定文件是用latin1編碼的,因?yàn)樗莵?lái)自1995年外部驅(qū)動(dòng)。終端會(huì)獲取標(biāo)準(zhǔn)輸出,它將會(huì)試著把它用utf-8編碼,因?yàn)檫@是它認(rèn)為的編碼。因?yàn)樽址莑atin1編碼的,因?yàn)樗鼰o(wú)法順利得解碼。但是不怕,不會(huì)有什么崩潰,因?yàn)槟愕慕K端在無(wú)法處理它的時(shí)候會(huì)無(wú)視它。

它在圖形界面上怎樣?每種有兩個(gè)版本。在一個(gè)像Nautilus 這樣的圖形界面上列出所有的文件。它把文件名和圖標(biāo)關(guān)聯(lián)起來(lái),能夠雙擊并且試著使文件名能夠顯示出來(lái),因而把它解碼。例如它會(huì)嘗試用utf-8解碼,錯(cuò)誤的地方用問(wèn)題記號(hào)來(lái)替代。你的文件名可能不是完全可讀的但那是你仍能打開(kāi)文件。

UNIX上的unicode只在你強(qiáng)制所有東西用它的時(shí)候會(huì)很瘋狂。但那不是unicode在UNIX上工作的方式。UNIX沒(méi)有區(qū)別unicode和字節(jié)的API。它們是相同的,使其更容易處理。

C Locale

C Locale在這里出現(xiàn)的次數(shù)非常多。C Locale是避免POSIX的規(guī)格被強(qiáng)行應(yīng)用到任何地方的一種手段。POSIX服從操作系統(tǒng)需要支持設(shè)置LC_CTYPE,來(lái)讓一切使用ASCII編碼。

這個(gè)locale是在不同的情況下挑選的。你主要發(fā)現(xiàn)這個(gè)locale為所有從cron啟動(dòng)的程序,你的初始化程序和子進(jìn)程提供一個(gè)空的環(huán)境。C Locale在環(huán)境里復(fù)原了一個(gè)健全的ASCII地帶,否則你無(wú)法信任任何東西。

但是ASCII這個(gè)詞指出它是7bit編碼。這不是問(wèn)題,因?yàn)椴僮飨到y(tǒng)是能處理字節(jié)的!任何基于8bit的內(nèi)容能正常處理,但你與操作系統(tǒng)遵循約定,那么字符處理會(huì)限制在前7bit。任何你的工具生成的信息它會(huì)用ASCII編碼并且使用英語(yǔ)。

注意POSIX規(guī)范沒(méi)有說(shuō)你的應(yīng)用程序應(yīng)當(dāng)死于火焰。

Python3死于火焰

Python3在unicode上選擇了與UNIX不同的立場(chǎng)。Python3說(shuō):任何東西是Unicode(默認(rèn)情況下,除非是在某些情況下,除非我們發(fā)送重復(fù)編碼的數(shù)據(jù),可即使如此,有時(shí)候它仍然是Unicode,雖然是錯(cuò)誤的Unicode)。文件名是Unicode,終端是Unicode,stdin和stdout是Unicode,有如此多的Unicode。因?yàn)閁NIX不是Unicode,Python3現(xiàn)在的立場(chǎng)是它是對(duì)的UNIX是錯(cuò)的,人們也應(yīng)該修改POSIX的定義來(lái)添加Unicode。那么這樣的話,文件名就是Unicode了,終端也是Unicode了,這樣也就不會(huì)看到一些由于字節(jié)導(dǎo)致的錯(cuò)誤了。

不是僅僅我這樣說(shuō)。這些是Python關(guān)于Unicode的腦殘想法導(dǎo)致的bug:

如果你Google一下,你就能發(fā)現(xiàn)如此多的吐槽??纯从卸嗌偃税惭bpip模塊失敗,原因是changelog里的一些字符,或者是因?yàn)閔ome文件夾的原因又,或者是因?yàn)镾SH session是用ASCII的,或者是因?yàn)樗麄兪鞘褂肞utty連接的。

Python3 cat

現(xiàn)在開(kāi)始為Python3修復(fù)cat。我們?nèi)绾巫??首先,我們需要處理字?jié),因?yàn)橛行〇|西可能會(huì)顯示一些不符合shell編碼的東西。所以無(wú)論如何,文件內(nèi)容需要是字節(jié)。但我們也需要打開(kāi)基本輸出來(lái)讓它支持字節(jié),而它默認(rèn)是不支持的。我們也需要分別處理一些情況比如Unicode API失敗,因?yàn)榫幋a是C。那么這就是,Python3特性的cat。

import sys
import shutil
 
def _is_binary_reader(stream, default=False):
  try:
    return isinstance(stream.read(0), bytes)
  except Exception:
    return default
 
def _is_binary_writer(stream, default=False):
  try:
    stream.write(b'')
  except Exception:
    try:
      stream.write('')
      return False
    except Exception:
      pass
    return default
  return True
 
def get_binary_stdin():
  # sys.stdin might or might not be binary in some extra cases. By
  # default it's obviously non binary which is the core of the
  # problem but the docs recomend changing it to binary for such
  # cases so we need to deal with it. Also someone might put
  # StringIO there for testing.
  is_binary = _is_binary_reader(sys.stdin, False)
  if is_binary:
    return sys.stdin
  buf = getattr(sys.stdin, 'buffer', None)
  if buf is not None and _is_binary_reader(buf, True):
    return buf
  raise RuntimeError('Did not manage to get binary stdin')
 
def get_binary_stdout():
  if _is_binary_writer(sys.stdout, False):
    return sys.stdout
  buf = getattr(sys.stdout, 'buffer', None)
  if buf is not None and _is_binary_writer(buf, True):
    return buf
  raise RuntimeError('Did not manage to get binary stdout')
 
def filename_to_ui(value):
  # The bytes branch is unecessary for *this* script but otherwise
  # necessary as python 3 still supports addressing files by bytes
  # through separate APIs.
  if isinstance(value, bytes):
    value = value.decode(sys.getfilesystemencoding(), 'replace')
  else:
    value = value.encode('utf-8', 'surrogateescape') \
      .decode('utf-8', 'replace')
  return value
 
binary_stdout = get_binary_stdout()
for filename in sys.argv[1:]:
  if filename != '-':
    try:
      f = open(filename, 'rb')
    except IOError as err:
      print('cat.py: %s: %s' % (
        filename_to_ui(filename),
        err
      ), file=sys.stderr)
      continue
  else:
    f = get_binary_stdin()
 
  with f:
    shutil.copyfileobj(f, binary_stdout)

這不是最差的版本。不是因?yàn)槲蚁胱屖虑楦訌?fù)雜,它現(xiàn)在就是有這么復(fù)雜。例如在例子里沒(méi)有做的是在讀取一個(gè)二進(jìn)制的東西是強(qiáng)制清理文本stdout。在這個(gè)例子里沒(méi)有必要,是因?yàn)檫@里的print調(diào)用去了stderr而不是stdout,但如果你想打印一些stdout,你就必須清理。為什么?因?yàn)閟tdout是別的緩沖區(qū)之上的緩沖區(qū),如果你不強(qiáng)制清理它,你的輸出順序可能會(huì)出錯(cuò)。

不僅僅是我,例如看: ,會(huì)發(fā)現(xiàn)相同的麻煩。

跳起編碼舞蹈

為了理解shell里的命令行參數(shù),順便說(shuō)一些Python3里最糟糕的情況:

  1. shell把文件名以字節(jié)傳給腳本
  2. 字節(jié)在命中你的代碼前被Python以預(yù)期的解碼方式解碼。因?yàn)檫@是有損好的過(guò)程,Python3使用一個(gè)特別的錯(cuò)誤處理器來(lái)處理解碼錯(cuò)誤。
  3. Python代碼處理一個(gè)沒(méi)有錯(cuò)誤的文件,并且需要格式化一個(gè)錯(cuò)誤信息。因?yàn)槲覀儗?xiě)文本流的時(shí)候如果它不是非法的unicode,是不會(huì)寫(xiě)替代的。
  4. 將包含替代的unicode串編碼為utf-8,然后告訴它處理替代轉(zhuǎn)義。
  5. 然后我們從utf-8解碼并告訴他忽略錯(cuò)誤
  6. 結(jié)果字符串回到只有文本的流里
  7. 之后終端會(huì)解碼我們的字符串來(lái)進(jìn)行顯示

以下是Python2里的情況:

  1. shell把文件名作為字節(jié)傳給腳本
  2. shell解碼字符串來(lái)進(jìn)行顯示

因?yàn)镻ython2版本里的字符串處理只是在出錯(cuò)的時(shí)候進(jìn)行糾正,因?yàn)閟hell在顯示文件名時(shí)能做得更好。

注意這沒(méi)有讓腳本更不對(duì)。如果你需要對(duì)輸入數(shù)據(jù)進(jìn)行實(shí)際的字符串處理,你就要在2.x和3.x里面切換到unicode處理。但在那種情況,你也想讓你的腳本支持一個(gè)—charset參數(shù),那么在2.x和3.x上做的工作差不多。只是在3.x上會(huì)更加糟糕,你需要構(gòu)建在2.x上不需要的二進(jìn)制標(biāo)準(zhǔn)輸出。

但你是錯(cuò)誤的

很顯然我錯(cuò)了,我被人告訴這些:

  • 我感到痛苦是因?yàn)槲也幌癯鯇W(xué)者那樣思考,新的unicode系統(tǒng)會(huì)對(duì)初學(xué)者更友好
  • 我不考慮windows用戶和新的文本模型對(duì)windows用戶是多么大的改進(jìn)
  • 問(wèn)題不在于Python,問(wèn)題在POSIX規(guī)范
  • Linux發(fā)行版需要開(kāi)始支持C.UTF-8,因?yàn)樗鼈儽贿^(guò)去一直阻礙著
  • 問(wèn)題是SSH發(fā)送了錯(cuò)誤的編碼。SSH需要修復(fù)這個(gè)問(wèn)題。
  • Python3里一大堆unicode錯(cuò)誤的真正問(wèn)題是人們不傳遞明確的編碼而假設(shè)Python3作出了正確的決定。
  • 我與分解代碼工作,顯然這在Python3里會(huì)更難。
  • 我應(yīng)該去改進(jìn)Python3而不是在twitter和博客上抱怨
  • 你在沒(méi)有問(wèn)題的地方制造問(wèn)題。讓每個(gè)人修復(fù)他們的環(huán)境和對(duì)任何東西進(jìn)行編碼就很好。這是用戶的問(wèn)題。
  • Java有這個(gè)問(wèn)題好多年了,這對(duì)開(kāi)發(fā)者來(lái)說(shuō)沒(méi)問(wèn)題。

你知道嗎?我在做HTTP方面的工作的時(shí)候就停止了抱怨,因?yàn)槲医邮芰诉@個(gè)主意,就是HTTP/WSGI的一大堆問(wèn)題對(duì)人們來(lái)說(shuō)很平常。但你知道什么?在Hello World這樣的情況下也有相同的問(wèn)題??赡芪覒?yīng)該放棄獲得一個(gè)高質(zhì)量的unicode支持的庫(kù),就這么將就了。

我可以對(duì)以上觀點(diǎn)進(jìn)行反駁,但最終也沒(méi)關(guān)系了。如果Python3是我唯一使用的Python語(yǔ)言,我會(huì)解決所有的問(wèn)題并且使用它開(kāi)發(fā)。有一個(gè)完美的另一個(gè)語(yǔ)言叫Python2,它有更大的用戶基礎(chǔ),并且用戶基礎(chǔ)是很牢固的。這時(shí)我是非常沮喪的。

Python3可能足夠強(qiáng)大,會(huì)開(kāi)始讓UNIX走Windows走過(guò)的路:在很多地方使用unicode,但我很懷疑這樣的做法。

更可能的事情是人們?nèi)耘f使用Python2,并且用Python3做一些很爛的東西?;蛘咚麄儠?huì)用Go。這門語(yǔ)言使用了與Python2很相似的模型:任何東西都是字節(jié)串。并且假設(shè)其編碼是UTF-8。到此結(jié)束。

相關(guān)文章

  • 在cmd中運(yùn)行.py文件: python的操作步驟

    在cmd中運(yùn)行.py文件: python的操作步驟

    今天小編就為大家分享一篇在cmd中運(yùn)行.py文件: python的操作步驟,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • Java爬蟲(chóng)技術(shù)框架之Heritrix框架詳解

    Java爬蟲(chóng)技術(shù)框架之Heritrix框架詳解

    這篇文章主要介紹了爬蟲(chóng)技術(shù)框架之Heritrix框架詳解,文中通過(guò)示例介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • python多個(gè)字符串條件分割split方式

    python多個(gè)字符串條件分割split方式

    這篇文章主要介紹了python多個(gè)字符串條件分割split方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • python opencv畫(huà)局部放大圖實(shí)例教程

    python opencv畫(huà)局部放大圖實(shí)例教程

    這篇文章主要給大家介紹了關(guān)于python opencv畫(huà)局部放大圖的相關(guān)資料,獲取鼠標(biāo)的單擊相應(yīng)以及鼠標(biāo)的移動(dòng)信息,進(jìn)行放大功能的實(shí)現(xiàn),需要的朋友可以參考下
    2021-10-10
  • Python命令行運(yùn)行文件的實(shí)例方法

    Python命令行運(yùn)行文件的實(shí)例方法

    在本篇文章里小編給大家整理的是一篇關(guān)于Python命令行運(yùn)行文件的實(shí)例方法,有興趣的朋友們可以學(xué)習(xí)參考下。
    2021-03-03
  • Python面向?qū)ο缶幊讨械念惡蛯?duì)象學(xué)習(xí)教程

    Python面向?qū)ο缶幊讨械念惡蛯?duì)象學(xué)習(xí)教程

    這篇文章主要介紹了Python面向?qū)ο缶幊讨械念惡蛯?duì)象學(xué)習(xí)教程,面向?qū)ο笫荘ython的基礎(chǔ)特性,其中的類與對(duì)象的特性和使用方法是Python學(xué)習(xí)當(dāng)中的基本功,需要的朋友可以參考下
    2015-03-03
  • django authenticate用戶身份認(rèn)證的項(xiàng)目實(shí)踐

    django authenticate用戶身份認(rèn)證的項(xiàng)目實(shí)踐

    Django的contrib.auth模塊中的authenticate()函數(shù)用于對(duì)用戶的憑據(jù)進(jìn)行身份驗(yàn)證,本文就來(lái)介紹一下django authenticate用戶身份認(rèn)證的使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-08-08
  • pandas ix &iloc &loc的區(qū)別

    pandas ix &iloc &loc的區(qū)別

    這篇文章主要介紹了pandas ix &iloc &loc的區(qū)別,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-01-01
  • Python3中在Anaconda環(huán)境下安裝basemap包

    Python3中在Anaconda環(huán)境下安裝basemap包

    今天小編就為大家分享一篇關(guān)于Python3中在Anaconda環(huán)境下安裝basemap包的文章,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2018-10-10
  • Python使用QQ郵箱發(fā)送Email的方法實(shí)例

    Python使用QQ郵箱發(fā)送Email的方法實(shí)例

    實(shí)際開(kāi)發(fā)過(guò)程中使用到郵箱的概率很高,那么如何借助python使用qq郵箱發(fā)送郵件呢?正好最近工作遇到這個(gè)需求,所以想著把方法分享出來(lái)方便大家,所以這篇文章主要介紹了Python使用QQ郵箱發(fā)送Email的實(shí)現(xiàn)方法,需要的朋友可以參考。
    2017-02-02

最新評(píng)論