关于文字集与编码

写Python2的人,很多人都见过下面这行Error

1
2
3
4
5
6
Traceback (most recent call last):
File "/Users/Jack/Documents/ApricotForestDoc/2_product_rnd/redspiderlily/mailfetcher.py", line 35, in <module>
if is_reply_mail(subject):
File "/Users/Jack/Documents/ApricotForestDoc/2_product_rnd/redspiderlily/module/mailutil.py", line 46, in is_reply_mail
return ("回复" in subject_.lower()) or ("re:" in subject_.lower())
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)

上一次聊到Python3升级的一个重头就是unicode编码。所以这次想重点就聊两句字符编码。说实话就是在此之前自己也有些算不清楚。所以还是深入的跑到wiki上研究了一番,分享供参考。

ASCII,全称American Standard Code for Information Interchange,是一个字符编码标准。使用0-127(2^7)的数字代表不同的英文字符,这里包括大小写字母,空格,特殊字符等。这是一个大家都比较熟悉的字符集标准,可以看看下面图中的内容。这里不做赘述。

但是,ASCII因为标准内含有的有限,带有音标的字符‘é’就无法很好的表述,更别提汉字了。到了1980年代,计算机技术发展,那时候大家已经开始使用字节(byte)来作为计算机基本计数单位。1byte=8bit(2^8=256),所以可以表述的字符变多了。那个时候开始出现了实用128-255这些数字表示发音单词。所以直到今天,你去看word里面的字符表,Latin依然可以看到这个顺序关系,大体就是这个原因。

再之后,当世界各地的语言发展出了各自不同的字符集体系,比如中国简体(GBK,GB18030),中文繁体(Big5,以前有个特别扯的名字叫做大五码),法语(Latin1),日语等等。本来各种语言字符集各自写互不干扰,倒也相安无事。但是,世界大融合嘛,于是问题来了。有人需要在中文里写上一段日语,就像这样 ++“日本語にほんご”++ 。于是问题就来了,怎么才能在同一个文件里现实不同的字符集的字符内容呢?1980年,人来开始尝试解决这个问题,并制定了一个新的计算机工业标准用以规范的处理、表示和编码全世界主要文字。这个标准叫做Unicode(全称是The Unicode Standard)。目前,Unicode标准是8.0,已经包含了全世界已经有超过12万个字符,覆盖129种现代和历史上的语言种类。在这里面需要说明一个额外的概念叫做Universal Coded Character Set (UCS,也叫做ISO 10646)。按照Wiki对于Unicode和UCS的说法,目前两套字符集应该是完全相同的。同一个数字在两个字符集里所代表的字符应该是相同的。但是Unicode的外延要多一些。USC仅仅是一个字符集,而对应的Unicode同时还规范了校对、标准化、表示顺序的算法等。就如同本文提及对Unicode的定义一样,Unicode出来包括了字符集,还有表现和处理方法的部分。因此Unicode应该说是一个更加广泛含义上的标准。

理论上说,Unicode字符集或者UCS有110万字符点数可以被分配,目前时机分配成16个Plane,其中Plane0,被叫做BMP(Basic Multilingual Plane),一共65536个,其中绝大部分是中文(Chinese),日文(Japanese)和韩文(Korean),三者合称CJK。

这里需要特别提一下Chinese Simplified(GBK,GB18030),自从2000年以后,中国政府规定,所有在中国售卖的软件产品必须支持Chinese Simplified(GB18030)字符集。因此在我国,就出现了一个神奇的事情,就是Unicode和Chinese Simplified双字符集并行的问题。

有趣的是,笔者大概调研了几大中文网站的编码如下:

URI 字符编码
https://www.taobao.com/ Unicode(UTF-8)
http://www.jd.com/ Chinese Simplified (GBK)
http://bj.meituan.com/ Unicode(UTF-8)
https://www.baidu.com/ Unicode(UTF-8)
http://cn.bing.com/ Unicode(UTF-8)
http://www.sohu.com/ Chinese Simplified (GBK)
http://www.qq.com/ Chinese Simplified (GBK)
http://www.sina.com.cn/ Unicode(UTF-8)

感觉上各个大厂也是自说自话,不是不是很一致要遵守政府规定或者不遵守规定。这么说来,国家对这块儿在申请xxx备案的管理也不是很严格。

话有点扯远了,咱们再回来。目前Unicode字符集共设定16个Plane(数字从0x000000-0x10FFFF)对应(2^16+2^20)对应(1,114,112)。刚刚说的Plane0,基础语言集定义是从0x000000-0x00FFFF(2^16)。其他的大家可以查,Plane1和2用了一些,其他基本上用的很少。因此总共来说目前分配的字符大约是12万。

但是这样庞大的数字和计算机的比特(byte)之间并不统一,把一个Unicode字符串转换成Byte的过程,这里引入了一个叫做Encoding,编码的概念。1992年,为了兼容不同处理器和C语言,人们引入了一个编码标准,这就是大家广泛知道的UTF-8。截至2016年5月份,在WWW上的统计,UTF-8的使用率已经达到86.9%,对比GB2312(0.8%)。同时W3C也把UTF-8作为XML和HTML的推荐编码。

下面我们来阐述一下UTF-8的实现原理(同时可以结合下图来看):

如果是7位以内表述的字符表数字,就只占用一个自己,表现为(0xxxxxxx),这样刚好和ASCII码的描述相一致,这样就不会造成原有ASCII的识别错乱,特别是针对C语言的strlen()和strcopy()。从第8位,也就是十位数的256开始,采用两字节表述模式(110xxxxx 10xxxxxx),最多可以表示11个bit位,也就是2^8(256)到2^12-1(4095)。以此类推。是不是一种很有趣的编码模式 :)

下面看一个栗子。以杏树林的“杏”字为题。

所以总结一下,Unicode和ASCII是一个字符集的概念,他们是随着电信发展而产生的编码本。只不过Unicode有多包涵了表示和处理部分,范围会更广泛。为了让计算机能读懂编码,适应计算机的计算,我们有了诸如UTF-8类的编码方法。

值得参考的一系列词条出处
https://docs.python.org/3/howto/unicode.html
https://en.wikipedia.org/wiki/Character_encoding
https://en.wikipedia.org/wiki/Unicode
https://en.wikipedia.org/wiki/Universal_Coded_Character_Set
https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane
https://en.wikipedia.org/wiki/UTF-8

升级Python3几个小总结

最近,主要是想集中把这次的改造写完,早日上线。这次主要干了两件事情。第一是升级Python3,这里面顺带写一点学习总结。算是个misc文章吧。下一篇主要是想聊聊没有后台的系统到底是如何搭建以及为什么要做这个实验。

OK,先说这次Python3升级吧。想了很久了,受制于各种阻力,总是担心升级会出问题。但是Python3既然已经嚷嚷了这么多年。而且还是有越来越多的系统在往Python3上迁移,所以还是用用看。至少知道坑在哪里,以后会遇到哪些问题。

总结一下本次修改较多的几个部分:

  • Print语法:Python3最有名的一个表征就是print的括号问题。在Python2里面是没有的,在3里加上了()这个语法。所以导致从2向3升级的过程中print,成了修改最多的语法之一。

  • Pip2(Python2)和Pip3(Python3),一般来讲,如果之前装的是2的,升级到3时候Python和Pip两个命令依然表示的是2,如果要是使用3.x的话,需要加入pip3或者python3,来作为命令开头。为了简单期间,或者用env来定义。还有一种就是在bash_profile里面增加python的alias

    1
    2
    alias python='python3'
    alias pip='pip3'
  • MySQL-python包的使用。这个很讨厌,因为目前这个包还不支持Python3。不过也没什么着急的,换一个呗。PyMySQL这个是Python官方对于MySQL的支持包。对于基本的增删改查操作,用法基本上是一样的。只不过要特别注意的是连接时要加入“charset=”utf8””来实现对中文的支持。这个在MySQLdb里面好像是默认,至少我当时没有设置。

    1
    db = pymysql.connect(host=host, port=int(port), user=username, passwd=password, db=database, charset="utf8")
  • 关于unicode,这里说道大头了哈。因为绝大多数人对于Python2中文支持都是一个噩梦。文字,被在--之间转换。但在Python3里面,很重要的一点是对中文做了很好的支持。消除了unicode这一层环节。所以现在就可以比较明确的说“中文.encode(‘utf-8’)”就是解码环节,无需再顾忌其他。

  • 也正是因为这个unicode的转变,其实造成了Python2里面很多原始代码都要进行一系列的和字符集相关的变化。比如一个buildin的函数open,在以前2的时代,是不需要对字符集进行指定的(话说,其实也是因为那个时代就没有什么字符集)。现在要特别说明打开字符集类型才可以进行有效的读写操作。

    1
    open(filename, 'w', newline='', encoding='utf-8')
  • 另外很赞的一点是,Unittest对Python的支持非常好。我自己实际用了unittest2和mock两个库。基本上都没有出现什么相关的修改。非常赞!所以说测试还是要加哦,很不错滴。

这次升级,确实花了些时间,处理问题。但确实得到了不少有益的实践。总结下来是两点吧

  1. 单元测试很重要。整个过程中,升级后的第一件事情就是运行单元测试,其中大量的错误,但是不可怕,一个一个的改就好了。因为我的程序设计web、邮件、数据库等多方通讯,没有单元测试,效率会大打折扣,而且还有可能遇到因为网络问题而导致的联调失败
  2. Python2到3的升级并不可怕,主要问题恰恰是来自于unicode这块儿的改变。只要记好了字符集utf-8这件事情。你会发现大量问题,都和这个有直接关系。

回顾北极星之病毒增长

上周做了一次关于北极星的回顾,一直想写也忘记了。今天提起来,其中印象比较深的部分还是在讨论这次送iPhone活动的成效。

复盘的结果是这样的:总的来讲,本次活动的成效还是比较好的,收获了大量的新用户注册。但是,如果说病毒效应的话,这次活动并不算事一个很好的病毒效应传播。从数据上看,并未达到预期的增长效果。

总结复盘,看了一下这篇知乎上的文章,我认为说的非常棒。
http://www.zhihu.com/question/20901811

“在facebook出现之前,有sixdegrees, Friendster, Myspace之类的社交网站,有一本书上说:facebook之所以能打败Friendster之类是因为用到了病毒式传播。如下图, 病毒式传播开始一般是线性增长,到到达临界点(Critical mass),然后以指数方式增长,直到饱和点。如果没办法到达临界点,一段周期后,就废了。”

觉得这一段话比较准确的说明了病毒式增长的方法和意义。下面这张图是原文中的曲线表述。这个图形,和我们自己的活动图形还是颇为相似。但是如何界定什么是“Saturation”饱和点,什么事“Critical mass”这个问题上,我还是有些困惑。关于我们图形里的第二个波峰,是哪一个点,还不是很清楚。我的感觉是,还需要一、两个活动,进行持续观察,寻找图像之间的拟合系数。通过这种曲线拟合关系,也许可以寻找到医疗互联网的一些有意思的数据。也就是这大约280万医生的市场运作数据。

希望大家每一个人和我一样,每周对数据关注一点点。作为一家互联网公司,数据驱动,才是核心。身为精英文化,目前的我不完全认同“无脑强运营”的文化,更加倾向于精英就是努力寻找对的事情,用数据说话,把方向找准。都说互联网没有对错,只有试一试。我觉得这是对的,但应该在数据驱动前提下完成的。而不是试了,然后呢?再是下一个。中国的互联网大量资本注入,已经不允许我们万事浅尝辄止,应该可以精细剖析。这也是现在所谓精细化运营的重要体现。

代码也是一种诗

只是为了记住那些你曾经非常容易忽略的。这里也没有什么不一样,只是安静的土地,再来喧嚣

1
2
3
4
5
6
python manage.py syncdb
python manage.py test
python manage.py migrate
python manage.py runserver
python manage.py inspectdb
python manage.py startapp service

python_db_basic

有些东西就是放下来记录一下,关于python的。这两天病了,忽然觉得这个事情总是对着有一种箭在弦上不得不发的感觉,所以说什么也要把这几块儿技术上的东西完成。搞完这个,应该可以踏踏实实的看看公司下一步的内部管理要如何做了,现在开始有点疲态的感觉,可能是因为平台接二连三的事故。不过这个我倒是不担心,毕竟一直在运行,一点点小聪明还是可以搞定的,只是,眼看,需要一个系统行的计划了,新的一个半年计划应该要搞一下出来了。

这个title叫python db basic,其实名字不好,主要是记录一下几个主要的命令,如何继续完成我的这个小玩具。

1)Python DB,超简单的东西,Djangle框架和RoR框架一样,基本上对于简单应用就是记住几行命令

在Models里面建立好class

在命令行里面运行,并生成migration脚本

1
python manage.py makemigrations

在命令行里面运行,并生成数据库的内容

1
python manage.py syncdb

在view里面用class直接产生新的obj,然后obj.save()就可以了

2)一个微信的小东西
ngrok用来做本地调试,却是好用截个图,剩下的自己查吧

王哲,CTOin杏树林
紧张的时候,静下来写一写条目,来看看哪些在做,哪些要做,哪些没做

python_misc

研究python,发现最大的问题就是中英文的转换,实在是太郁闷了,研究了好久。最主要的还是出在python的encode和decode上面
http://www.jb51.net/article/17560.htm
这篇文章不错,基本上把需要的内容都解释的很清楚了。

在别人代码的基础上做了些修改,现在已经基本可以用了。

这个是该出来的加入的内容
python ./jira -s http://bug.xingshulin.com -u **** -p **** cat JCTP-9
python ./jira -s http://bug.xingshulin.com -u **** -p **** comment JCTP-9 “let do something fantastic”
python ./jira -s http://bug.xingshulin.com -u **** -p **** create -s “this is summary2” -d “nothing special” -p JCTP -t “运维事故” -a “wangzhe” -f “customfield_10200:20/12/14”

有个挺tricky的地方是要把jira里面的时间传输格式进行调整,默认的是d/MMM/yy,但是因为python对中文的支持问题,所以需要被调整成dd/mm/yy

{‘priority’: 3, ‘description’: None, ‘customFieldValues’: [[{‘values’: [[‘2014-12-23’]], ‘customfieldId’: ‘customfield_10200’}]], ‘summary’: ‘Test issue using all available defaults’, ‘project’: ‘JCTP’, ‘assignee’: ‘wangzhe’, ‘type’: 7}
{‘priority’: 3, ‘description’: None, ‘customFieldValues’: [[{‘values’: [[‘2014-12-22’]], ‘customfieldId’: ‘customfield_10200’}]], ‘summary’: ‘this is summary’, ‘project’: ‘JCTP’, ‘assignee’: ‘wangzhe’, ‘type’: 6}