about 3 years ago

By Vince Lombardi:

The joy is in creating, not maintaining

 
over 3 years ago

最近需要使用Django开发个Web App. Django官方推荐使用Postgresql作为数据库,所以这里记录一下在Mac OSX上安装的步骤和碰到的问题。

  1. 最简单的方式是安装Postgres.App. 这个应用里自带了最新版本的PostgreSQL而且不需要学习数据库服务器启动和关闭的命令。程序安好后(别忘了拖拽到Application的文件夹里),会自动在端口5432开启一个PostgreSQL的服务器。在程序界面里还有一个很贴心的按钮 'Open psql',可以自动为你在命令行里打开一个客户端与服务器进行连接。而且它会使用你当前的Mac用户名在服务器上为你注册成为一个superuser,让你立刻就可以在数据库进行任何的修改和操作。

  2. 通过Python与PostgreSQL建立连接和操作需要安装psycopg2这个库。可以通过一下指令来安装:

$ pip install psycopg2

这个时候会出现一个错误:

Error: pg_config executable not found.

Please add the directory containing pg_config to the PATH

再次求助StackOverflow找到了答案,psycopg2在安装的时候需要pg_config这个程序。这个程序其实已经随着Postgres.app安装到了硬盘上,但是还没有被添加到系统的PATH里。以下是添加方式:

$ cd ~
$ nano .bash_profile

然后在bash_profile里添加, 你可能需要修改版本号码(9.4):

export PATH=$PATH:/Applications/Postgres.app/Contents/Versions/9.4/bin

保存文件后重新加载bash_profile,注意两个点之间的空格:

$ . .bash_profile

确认pg_config确实可以被系统找到:

$ which pg_config

/Applications/Postgres.app/Contents/Versions/9.4/bin/pg_config

然后再次安装psycopg2就好了

$ pip install psycopg2

以上。

 
over 3 years ago

用Python 2.x会经常碰到一个错误:

UnicodeEncodeError: 'ascii' codec can't encode character ... : ordinal not in range(128)

搞清这个问题之前要先理解三个知识点:

  1. UTF-8 vs Unicode
  2. Encoding vs Decoding
  3. Python 2.7里的 str 和 unicode

1. UTF-8 vs Unicode

这一点已经在[之前的博文]里解释过了(http://cheng.logdown.com/posts/2015/01/14/utf-8-vs-unicode:),这里我来总结一下

  • Unicode 只是一个文字与数字之间的映射。比如,'汉' 这个字在Unicode里的代码是 ‘6C49’,想对应的数字就是 27721。地球上每种语言里的每一个文字都有这样一个相对应的数字标识。这个文字与数字的映射表就是 Unicode。
  • 当我们把这个映射后的数字存储在计算机上时,需要把它转换成 0 和 1. 我们可以简单的把 27721 转换成二进制代码 ’01101100 01001001‘ 来存储。但问题是计算机怎么能够知道这个两个字节是代表一个文字而不是两个文字? 这个时候就需要再有一种编码形式来告诉计算机将这个字节视为一个文字。这个编码就是UTF-8 (当然,UTF-8只是众多编码中的一种)

可以用这个顺序来理解:

屏幕上看到的文字     Unicode代码     根据UTF-8规范存在计算机内存或者硬盘里的模样
       汉      ->     6C49     ->       11100110 10110001 10001001

2. Encoding vs Decoding

在Python中把一个Unicode类转化为 0 和 1 的过程叫做Encoding。 把 0 和 1 反转为Unicode类的过程叫做Decoding。

在Python 2.7版本里,ASCII是默认的Encoding和Decoding的方法。

3. Python 2.7里的 str 和 unicode

当你把一个字符(不管该字符是英文字母还是ASCII里不能包含的字符)赋值给一个变量时:

han = '汉'

这个变量的类型都会是str:

In [113]:han = '汉'
         type(han)
Out[113]:str

但这里有很重要的一点需要理解

In [124]:han = '汉'
         bin_han = '\xe6\xb1\x89'
         han == bin_han # 虽然在界面里我们看到的是'汉'这个字,但其实它是一堆字符,并不是我们看到的文字

Out[124]:True

str这个类的本质其实就是原始字符数据(raw byte data)。它并不是我们所看到的'汉'!

那么Unicode类也是这样吗?

In [114]:uni_han= u'汉'
         type(uni_han)
Out[114]:unicode

In [131]:uni_han= u'汉'
         u_han = u'\u6c49'
         uni_han == u_han # 在Unicode中存储的是u'\u6c49'而不是你所看到的u'汉'

Out[131]:True

理解了以上三个知识点,我们就可以很容易的解释 'ascii' codec can't encode character 这个错误的缘由了。

用示例来解释 'ascii' codec can't encode character

In [117]:han = '汉'
         print type(han)
         print len(han)
         str(han) 
         
Out[117]:<type 'str'>
         3              <- '汉'的长度是3,明明是一个字,为什么长度是3?
         '\xe6\xb1\x89' <- 答案在这里

当'汉'这个字被存储在内存中时,它会被转为三个字符'\xe6\xb1\x89'。所以len()给出的长度是3,而不是1. 那么为了让'汉'变成一个真正的字,我们就需要对它进行Decoding。(参看2. 把 0 和 1 转换为Unicode的过程叫Decoding)

In [125]:str.decode(han)
---------------------------------------------------------------------------
UnicodeDecodeError                        Traceback (most recent call last)
<ipython-input-125-3ff96a3a19da> in <module>()
----> 1 str.encode(han)

UnicodeDecodeError: 'ascii' codec can't decode byte 0xe6 in position 0: ordinal not in range(128)

这里Python抛出了异常。因为默认的ASCII编码无法Decode这个文字。因为这个文字的数值已经超过了0 - 127这个范围。所以我们需要使用UTF-8编码来Decode:

In [127]:str.decode(han, 'utf8')
Out[127]:u'\u6c49'

这里han这个变量被成功Decode为 u'\u6c49

In [142]:uni_han = u'\u6c49'
         len(uni_han)
Out[142]:1              <- 长度变为了正确的1

再来个示例作为结尾

猜猜这段代码的输出是什么:

uni_han = u'\u6c49'
print '\'{0}\'的长度是{1}'.format(uni_han, len(uni_han))

结果是:

---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)
<ipython-input-143-9bec6fa25583> in <module>()
      1 uni_han = u'\u6c49'
----> 2 print '\'{0}\'的长度是{1}'.format(uni_han, len(uni_han))

UnicodeEncodeError: 'ascii' codec can't encode character u'\u6c49' in position 0: ordinal not in range(128)

好伤心啊,本以为再也不会碰到这个问题了。那么问题出在哪呢?这部分代码'\'{0}\'的长度是{1}'是str,也就是原始的字符数据。我们想把一个Unicode (uni_han)混到它们里一起打印。这时,Python信心满满的用了默认的ASCII编码来Encode uni_han。结果可想而知,又是再次超出0 - 127的范围,无法Encode。这时,我们就需要告诉Python放弃ASCII吧,请使用UTF-8:

In [145]:uni_han = u'\u6c49'
         print '\'{0}\'的长度是{1}'.format(unicode.encode(uni_han,'utf8'), len(uni_han))
         '汉'的长度是1

另一种方法是让前半部分的str变为Unicode:

In [150]:uni_han = u'\u6c49'
         print u'\'{0}\'的长度是{1}'.format(uni_han, len(uni_han))
         '汉'的长度是1

总结

在Python 2.x里str就是原始的010101, Unicode是Unicode,这两个东西不能混着用。当一个文字被写到硬盘上时,或者打印到屏幕上时,需要使用正确的Encoding编码。Python默认使用ASCII,但其实应该用UTF-8。这个问题以后还会经常碰到。关键是要理解ASCII,UTF8,Unicode, Encoding和Decoding的定义和关系。

 
over 3 years ago

平时很喜欢用iPython的Notebook功能来学习和实验python程序。今天在台新电脑上办公,所以需要重新安装一下。安装iPython有两种选择:

  1. 通过pip安装
  2. 安装Anaconda从而获得iPython (这个选择比较适合新手,推荐下载Anaconda安装包然后一键搞定)

这次主要介绍如何通过pip来安装。iPython是由很多模块组成的, 为了不漏装任何组件,我用了这个命令来安装所有组件:

$ pip install ipython[all]

安装成功后通过这命令来运行Notebook:

$ ipython notebook

之后有可能会弹出这个错误(你如果选择了用Anaconda的方式来安装也会碰到这个错误):

ValueError, 'unknown locale: %s' % localename

StackOverflow上已经有人提出了解决方案,在命令行里找到.bash_profile 然后添加下面两行代码:

export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8

然后别忘了重新载入.bash_profile让新添加的代码生效(注意两个点之间的空格):

$ . .bash_profile

之后再运行这个命令就可以在浏览器里看到iPython Notebook的界面了:

$ ipython notebook

iPython Notebook的工作原理是在本地启动一个服务器,你通过localhost:8888/tree 这个地址就可以连接到这个服务器上与之通信。从而实现在浏览器里写代码,传给本地服务器执行,然后本地服务器传回结果并在网页上呈现这个循环。

每次使用完Notebook,只关闭网页本身是不够的。需要在你启动Notebook的那个Terminal里输 Ctrl + c 然后确认,才能把服务器关闭。

 
over 3 years ago

Let me use an example to illustrate this topic:

A chinese character:      汉
it's unicode value:       U+6C49
convert 6C49 to binary:   01101100 01001001

Nothing magical so far, it's very simple. Now, let's say we decide to store this character on our hard drive. To do that, we need to store the character in binary format. We can simply store it as is '01101100 01001001'. Done!

But wait a minute, is '01101100 01001001' one character or two characters? You knew this is one character because I told you, but when a computer reads it, it has no idea. So we need some sort of "encoding" to tell the computer to treat it as one.

This is where the rules of 'UTF-8' comes in: http://www.fileformat.info/info/unicode/utf8.htm

Binary format of bytes in sequence

1st Byte    2nd Byte    3rd Byte    4th Byte    Number of Free Bits   Maximum Expressible Unicode Value
0xxxxxxx                                                7             007F hex (127)
110xxxxx    10xxxxxx                                (5+6)=11          07FF hex (2047)
1110xxxx    10xxxxxx    10xxxxxx                  (4+6+6)=16          FFFF hex (65535)
11110xxx    10xxxxxx    10xxxxxx    10xxxxxx    (3+6+6+6)=21          10FFFF hex (1,114,111)

According to the table above, if we want to store this character using the 'UTF-8' format, we need to prefix our character with some 'headers'. Our chinese character is 16 bits long (count the binary value yourself), so we will use the format on row 3 as it provides enough space:

Header  Place holder    Fill in our Binary   Result         
1110    xxxx            0110                 11100110
10      xxxxxx          110001               10110001
10      xxxxxx          001001               10001001

Writing out the result in one line:

11100110 10110001 10001001

This is the UTF-8 (binary) value of the chinese character! (confirm it yourself)

Summary

A chinese character:      汉
it's unicode value:       U+6C49
convert 6C49 to binary:   01101100 01001001
UTF-8 binary:             11100110 10110001 10001001