Permalink
Browse files

init commit

  • Loading branch information...
Zibo Wang Zibo Wang
Zibo Wang authored and Zibo Wang committed Oct 27, 2016
0 parents commit ee74ecc78a09660662eb8a6308cce38c2044b93d
Showing with 1,472 additions and 0 deletions.
  1. +32 −0 Python字符编码的一个坑.md
  2. +34 −0 QQBot.md
  3. +88 −0 chardraw.md
  4. BIN chardraw/Riemann函数.png
  5. BIN chardraw/atari_small.png
  6. BIN chardraw/电场线.png
  7. BIN chardraw/第一次运行.png
  8. BIN chardraw/第三次运行.png
  9. BIN chardraw/第二次运行.png
  10. +27 −0 cubic-backup.md
  11. +79 −0 一个面向过程变为面向对象的例子.md
  12. +85 −0 从0.99...=1说起数学中的“无穷”概念.md
  13. +124 −0 判断语句有多少种替代方法.md
  14. +35 −0 字符集与字符编码小史.md
  15. +157 −0 密码学科普.md
  16. +66 −0 幻灯片制作中的美学问题.md
  17. BIN 幻灯片制作中的美学问题/PPT版幻灯片制作教程目录.jpg
  18. BIN 幻灯片制作中的美学问题/不良的幻灯片排版.png
  19. BIN 幻灯片制作中的美学问题/合格的幻灯片排版1.png
  20. BIN 幻灯片制作中的美学问题/合格的幻灯片排版2.png
  21. BIN 幻灯片制作中的美学问题/难看的幻灯片排版.png
  22. BIN 幻灯片制作中的美学问题/难看的文档排版.png
  23. +115 −0 抢红包机器人开发过程.md
  24. BIN 抢红包机器人开发过程/口令红包.png
  25. BIN 抢红包机器人开发过程/普通红包.png
  26. BIN 抢红包机器人开发过程/消息列表.png
  27. BIN 抢红包机器人开发过程/消息窗口.png
  28. +76 −0 检查RSS是否有更新的小工具.md
  29. +93 −0 高维空间中的正图形.md
  30. BIN 高维空间中的正图形/Stereographic_polytope_16cell.png
  31. BIN 高维空间中的正图形/Stereographic_polytope_8cell.png
  32. +461 −0 黑客大赛题解.md
  33. BIN 黑客大赛题解/1.png
  34. BIN 黑客大赛题解/10.png
  35. BIN 黑客大赛题解/11.png
  36. BIN 黑客大赛题解/2.png
  37. BIN 黑客大赛题解/3.png
  38. BIN 黑客大赛题解/4.png
  39. BIN 黑客大赛题解/5.png
  40. BIN 黑客大赛题解/6.png
  41. BIN 黑客大赛题解/7.png
  42. BIN 黑客大赛题解/8.png
  43. BIN 黑客大赛题解/9.png
  44. BIN 黑客大赛题解/Feistel.png
  45. BIN 黑客大赛题解/IDA.png
  46. BIN 黑客大赛题解/code.png
  47. BIN 黑客大赛题解/cookie.png
  48. BIN 黑客大赛题解/smallgame.png
  49. BIN 黑客大赛题解/soyun.png
@@ -0,0 +1,32 @@
---
title: Python字符编码的一个坑
tags:
- Python
- Unicode
- 笔记
categories:
- 底层
date: 2016-07-22 13:05:51
---

如果Python的标准输出不是终端而是管道的话,其文件编码默认是ascii,测试时我们常常在终端下运行程序,很难想到一个测试时完全没问题的程序假如被放在管道里,试图输出Unicode字符时居然就会出现字符编码错误。之前我做微信公众号服务器时就发生了这样的情况,一段Python代码要被PHP的`system`函数调用执行,本地调试完全没有问题,但是上线工作时系统就不能处理Unicode字符。我在网上搜索了很久,终于从若干篇stackoverflow文章中总结出了问题和解决方案。

<!--more-->Python2的解决方案很简单,因为其允许向文件写入bytes,也就是Unicode字符串调用encode方法后得到的字节数据流。因此只需要把所有输出字符串都`encode('utf8')`即可。

Python3的解决方案比较复杂,其不允许向ascii编码的文件写入bytes,因此我们需要重新以UTF-8编码打开标准输出。不罗嗦了,在要处理Unicode,且可能在管道中运行的程序开头加上这一段代码,即可确保安全:

import sys
import codecs
sys.stdout = codecs.getwriter('utf8')(sys.stdout.detach())
sys.stderr = codecs.getwriter('utf8')(sys.stderr.detach())

至于`stdin`是不是也有类似的问题,我还没尝试,但很可能也是需要类似处理一下的。

顺便再说一个小问题,在某些条件下(或许是“在管道中运行”),Python3的`open`函数默认编码居然会是ascii。觉得每次调用`open`时都指定`encoding='utf8'`很丑陋的话,或者代码中调用的某些库会自己`open`文件的话,可以在代码开头加上这个来改变`open`的默认编码:

import functools
open = functools.partial(open, encoding='utf-8')

这个问题我没有仔细研究,它的产生条件,以及在Python2下的情况尚不明确。

作为我最喜欢的编程语言,Python要写出能安全处理UTF-8的程序居然如此麻烦——要引入两个库,用wrapper换掉三个系统自带对象——实在是令我失望。
@@ -0,0 +1,34 @@
---
title: QQBot
tags:
- Python
- QQ
- 作品
categories:
- 网络
date: 2015-10-13 23:27:52
---

好久没更新了,羞愧羞愧……写一下最近一直在搞的QQBot项目。

QQBot项目的目标是制作出QQ聊天库(虽然叫机器人,但目的只是制作有特定功能的机器人,并不含人工智能),也就是让程序能够收发QQ聊天消息。我很早以前就有过这个设想,但是直到前一段时间才真的见到了一个QQ机器人(科大魔方协会QQ群中的wca查询机器人),大呼“好棒”的同时终于下定决心动工。

<!--more-->首先,QQ这款软件有许多平台上的各种版本,使用着不同的网络传输数据的方式(“接口”),要在其中选择合适的一种,将我自己的程序伪装成相应的QQ客户端。简单划分的话,QQ的版本分为PC、手机、Web(曾经叫WebQQ,现在升级为SmartQQ)和3GQQ。PC和手机都是使用较为底层的协议来通信的,需要建立UDP或TCP连接,并且处理各种腾讯自己定义的数据包格式(需要在电脑上运行抓包软件来获取数据包,直接获取到的都是人类不可读的加密二进制数据,需要人工理解它的格式和编码方式)。而Web和3GQQ都是基于HTTP协议,HTTP之上是明文、人类可读的简单数据(加密靠SSL在HTTP的下层实现),比较容易处理,因此被选中作为我将要研究的版本。

要做机器人,首先要能让机器人登录上QQ。QQ登录一般就是提供号码和密码来验证,但有可能需要再输入一个验证码。如今一种新的方法是使用已经登录的手机扫描二维码来登录,这个方法的好处是不仅绝大多数情况下(我还没遇到过反例)不需要输入验证码,而且不需要把自己的账号和密码写进程序里,极大的提高了安全性。所以我选择了能够使用二维码登录的SmartQQ来研究。

<del>好吧,另一个原因是,3GQQ不是HTML的,比较难搞,数据包截取的过程中出了一些问题,于是我可耻地失败了。</del>

SmartQQ也蛮复杂的,所以我看了看几个主要过程(登录、获取好友列表及好友详细信息、检查是否有新消息、接收消息、发送消息)的逻辑流程后就<del>打开了GitHub,搜索“SmartQQ”,fork了一个看起来不错的项目来用,并且装作自己是</del>独立搞出了一个SmartQQ库。(稍后详细解释)

我的程序是Python2的,最初在自己的笔记本上运行了一段时间。之后我得知,科大有自己的云服务平台,并且每个学生都可以免费申请云主机,便迁移到了平台上运行。给自己的账号绑定的机器人在测试了一段时间后,由于不知道该让它干什么……现在已经关闭了。但为一个群专门制作的另一个功能性机器人效果很好,运行稳定。这些天的这个发展,群成员团结一致,同心同德,机器人的智商已经取得了很大的进步。

现在对于SmartQQ的一些流程总结如下,给想要做同样开发的人一些参考:

进行二维码登录流程,需要先获取一个appid(似乎永远是一个固定值?或许是SmartQQ这个接口所拥有的),然后到一个固定的地址获取二维码(jpg文件),唯一的变量是随本请求所返回的cookie。接下来带着这个cookie到另一个固定的地址轮询扫码状态,常见的结果为未扫描(再次询问)、已失效(重新下载)、已扫描(再次询问)或已登录(会获得一个较长的返回值,可以提取出QQ号码、昵称等信息,以及最重要的一些身份标识串)。最初我以为appid是不同的,用来标识身份的,所以让程序打印出二维码的地址,然后手动下载二维码。这样会导致程序在轮询状态阶段未发送cookie而遭遇403错误。

接下来可以请求好友列表、群列表,查询具体的好友/群信息,这些的接口都很简单,略过不提。

SmartQQ的一个坏处就是需要不断轮询,也就是每隔一会发送一个poll请求来查询最新接收到的消息。据我目前的了解,要实现“有新消息时收到通知”,也就是推送,似乎必须使用标准的QQ协议。每次poll到的消息是一个列表,其中一个需要注意的问题就是QQ系统使用“uid”作为用户/群的唯一标识,和QQ号/群号不同。如果需要换算,调用一个接口即可。

另外,SmartQQ会被认为是电脑登录,可以和手机并存,但是我还没发现它加载历史消息的接口,因此目前不知道该如何读取到自己的账号通过手机所发送的消息。这似乎不会在poll时产生一个消息提醒。
@@ -0,0 +1,88 @@
---
title: chardraw
tags:
- Python
- 作品
- 字符画
categories:
- 字符终端
date: 2015-09-28 17:42:53
---

我一直热衷于重复造轮子……今天又用了之前的一个轮子并且优化了一下,因此介绍一下这个项目。

chardraw项目的目标是在4x8字体的低分辨率点阵终端用字符(近似地)显示图像。目前这个项目还没完全做好,所以只能用黑白的图像来进行介绍:

![](/chardraw/电场线.png)

上图为等量异种电荷间的电场线图案的1/4。下图为一个测试图案。chardraw绘图。

![](/chardraw/Riemann函数.png)

Riemann函数在x:(0,1), y:(0, 0.5)上的图像,chardraw绘图。

<!--more-->

针对不同的设备特点,可能存在以下限制:

1. 屏幕被分割为许多8行4列的点阵区域,每个区域内只能有两种颜色,前景色和背景色。这是绝大多数低分辨率点阵终端都有的限制。
2. 在单色屏幕上,前景色只能是白色(或某个特定颜色),背景色只能是黑色。
3. 在大多数终端上,前景色和背景色只能是16个特定颜色之二。
4. 在支持修改颜色表的设备上,上述16个颜色可以被修改,但同一时间屏幕上最多只能存在16种不同的颜色。
5. 每个4x8点阵区域显示一个字符,这意味着其中前景色和背景色所组成的图案形状(pattern)只能是128个ASCII所对应的文字之一。
6. 有的终端把0x80~0xFF对应为一些特殊字符,这样上一条限制会被放宽到256种pattern之一。
7. 有的终端支持类似于斜体、粗体、下划线之类的文字效果,这使得可以选用的形状更多了,但太过于复杂,目前不打算考虑这种情况。
8. 在支持修改字体表的设备上,上述128或者256种pattern都可以被修改,但同一时间屏幕上各个4x8区域的图案最多只能有128或256种。

(可以看出,上面所展示的图片,限制是1、2、5,也就是最严格的情况)

![](/chardraw/atari_small.png)

图:非常常见的一种4x8点阵字体,前32个码位被分配给了特殊符号。可惜在我的设备上并没有这种福利,前32个码位仅仅是空白而已。

如何在这些限制之下,尽可能好地用字符显示出图片?首先就要定义“好”的标准。一开始,我的思路是:将图像分割开,将其中每一个4x8区域与字体表(如上图,含有所有字符的pattern)中所有字符进行比较。颜色相同的点越多,比较结果得分越高,然后取最高分的字符作为最相似的字符。

首先一个问题是,如果字体表和颜色表能够修改,就不能把4x8区域分别看待,而要全局统筹考虑(因为只能有16个颜色和128种pattern,肯定要有一些不重要的细节被近似化,尽量满足重要的细节正确呈现),研究怎样分配颜色和pattern这两种紧缺的资源。不过这个问题太复杂,所以我暂且假设字体表和颜色表都不能修改,这样一来每个4x8区域都是独立的了。

其次的问题是,第一次运行程序后,得到的输出是这个样子的:

![chardraw第一次运行](/chardraw/第一次运行.png)

顺便一提,开发过程中我使用的测试图案一直是一幅更早之前制作的,等量异种电荷间电场线分布图案的1/4。因为这张图含有足够多的曲线,我认为可以比较全面地观察绘图质量。

恩,结果就是由于曲线太细,在许多地方程序认为空格是最接近的字符,图像变得断断续续了。头脑简单的我加粗了一下输入的图案,又画了一遍:

![chardraw第二次运行](/chardraw/第二次运行.png)

这次,太多的区域的最符合的字符居然是反色后的字符,虽然肯定是最接近的匹配结果,但屏幕上一块一块白色方框实在很难看,我想无论从哪种角度来讲,这都不能算作好的近似图案。

总结教训,结论就是,亮度很重要。一味地追求相似,结果可能是匹配到了过于暗或者过于亮的字符,影响整体效果。因此我修改了程序,使它计算出每个4x8区域的亮度(亮点数,取值范围显然为0~32),然后按照“亮度一致-暗一点-亮一点-暗两点-亮两点”的优先顺序去检查字符(把暗放在亮的前面,是为了稍微减少一些反色字符,它们实在太难看了)。这带来了两个好处:一是减少了需要进行比较的字符数量,加快了程序速度。二是最终所选择的字符的亮度误差一定在2以内,不会使结果的色调过于差劲。而且,因为有5个亮度级别的字符被检查,也能寻找到比较相似的字符。

![chardraw第三次运行](/chardraw/第三次运行.png)

现在产生的图案终于是粗细均匀,明暗一致了,我认为可以作为V1.0了。之后,我为程序中时间消耗最大的“寻找匹配的字符”部分加上了结果缓存,按最近最少使用来更新。这使得程序不再在大片的空白中耽误时间,而且能更快绘制大片相似颜色和直线线条。

以上就是之前的工作成果了。今天中午想画一个Riemann函数图象来看(虽然我也写过draw绘图库,可以画出正常的图,但既然产生了数据,为何不送进chardraw里看看呢?)。我发现对于这种散点式的图像,之前调节亮度的方法还是不够好——对于亮度值为1的一个孤立的点,最好的匹配结果就是空格,导致这些点消失不见。因此我为程序增加了一个“数学图像”开关,打开后会对亮度进行更严格的限制:

* 亮度为0的区域只能用空格匹配
* 亮度为1的区域只能用亮度为1或2的字符匹配
* 亮度为2的区域只能用亮度为1或2或3的字符匹配
* 更高亮度的保持以前的设计不变,即允许误差正负2级亮度

这样一来,孤立的点的位置的准确性有少许牺牲,但是确保了点不会消失,适合观察散点的位置。在这个模式下,我绘制了本文开头的那张Riemann函数图象。这是数学中非常有名的一个函数,当自变量为x时,它的函数值为:

* 1/q,如果x是有理数,可以表示为最简分数p/q
* 0,如果x是无理数
* 1,如果x是0

顺便一提,这个函数图像是分形的,它的任何较小部分与较大部分之间是相似的(自相似性),并且它处处极限为0,在所有有理点上不连续,在所有无理点上连续。当然这是题外话了。

* * *

补充:有人指出这个函数应该叫做Thomae's function,吓得我赶紧查了一下,幸好并没有错。以下为[英文维基百科](https://en.wikipedia.org/wiki/Thomae)原文:

> **Thomae's function**, named after [Carl Johannes Thomae](https://en.wikipedia.org/wiki/Carl_Johannes_Thomae "Carl Johannes Thomae"), has many names: the **popcorn function**, the **raindrop function**, the **countable cloud function**, the **modified Dirichlet function**, the **ruler function**,<sup id="cite_ref-1" class="reference">[[1]](https://en.wikipedia.org/wiki/Thomae%27s_function#cite_note-1)</sup> the **Riemann function**, or the **Stars over Babylon** ([John Horton Conway](https://en.wikipedia.org/wiki/John_Horton_Conway "John Horton Conway")'s name).<sup id="cite_ref-2" class="reference">[[2]](https://en.wikipedia.org/wiki/Thomae%27s_function#cite_note-2)</sup>
* * *

欢迎回复告诉我哪里有前人对这个项目更好的实现,或者如果感兴趣的话欢迎一起继续造轮子。
Binary file not shown.
BIN +730 Bytes chardraw/atari_small.png
Binary file not shown.
BIN +54.5 KB chardraw/电场线.png
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,27 @@
---
title: cubic-backup
tags:
- Python
- 作品
categories:
- 计算机
date: 2016-09-07 09:39:43
---

最近自制了一个文件备份工具,称为cubic-backup,已发布到[GitHub](https://github.com/Smart-Hypercube/cubic-backup)。这个工具用来解决的最主要的一个问题就是:当我同时有固态硬盘和机械硬盘时,如何将固态硬盘作为主要工作区域,而机械硬盘中自动备份文件历史记录?以下是我设想的一些理想场景:

* 新学期开始,要做大物实验了,为了加快打开速度,我要把大物实验相关的文档放进固态硬盘;
* 从朋友那里拿到了《生活大爆炸》全集高清资源,为了和USB3.0传输速度相匹配,我把它们复制进固态硬盘,一下子就传输完成了;
* 打算把那些《生活大爆炸》重命名一下或者放进一个文件夹里,我肯定不希望这导致它们在备份里占用两倍空间;
* 安装了Visual Studio,固态硬盘的空间有点紧张了,为了腾出空间,随便删掉了一些大文件,包括之前的《生活大爆炸》视频。
据我目前所知,cubic-backup是唯一能满足我的需求的工具。

<!--more-->首先,我尝试了Windows的“文件历史记录”功能。令人遗憾的是,这个功能设计的初衷应该是容灾性备份,不适合经常提取。要从文件历史记录中提取文件,必须使用一个速度相当慢的程序,找到一个正确的时间点(在文件创建后、删除前),才能提取出想要的文件。另外,如果将一个大文件删除后再提取出来,就会导致它在历史记录中占据两倍的空间(备份系统认为我创建了一个新文件,于是重新备份了一次),这一点是绝对不能接受的。

其次,以前常常考虑的git也不合适,它的哈希表存储系统既是优点又是缺点。一方面,它保证了相同文件只存储一份。但另一方面,如果用git管理许多大文件,每隔几分钟自动commit一次,计算哈希值会产生巨大的CPU和IO开销。

最终,我认为自己做一个工具是最顺手的。cubic-backup需要在配置文件中指定若干“缓存-库”对,所谓缓存就是固态硬盘中的一个文件夹,而库是机械硬盘中由cubic-backup管理的另一个文件夹。在提交(commit)时,它会用极小的开销检查一下变更并将新东西存进库中。除此之外,没有任何操作是需要通过cubic-backup才能实现的——即使是想从库中提取文件,也只需要直接打开库,复制想要的东西即可。与git不同,cubic-backup的库对人类非常友好,所有文件的最新版本都是用原本的相对路径和文件名直接存储的。而文件的历史版本,仅仅是文件名中多了一个日期和时间而已。

为了实现“相同文件只存储一次”,我借鉴了git的思路并加以改进,决定让所有相同文件在库中互为硬链接。所谓“硬链接”,是一种特殊的文件系统功能——两个看起来不同的“文件”的文件名不同,路径也可以不一样,但实际上是同一个,在硬盘中只存一份,打开任何一个并修改,都会导致两个文件一起发生变更。然而,这个技术的不利之处是增加了用户不小心编辑库中文件的坏处——所有库中的相同文件都会变化,产生不可预料的结果。不过好在这种错误至少是可以检查出的,由于“.cubicpool”这个目录的存在,每个文件正确的哈希值都是有记录的,若定期检查其中各文件哈希是否相符,即可知道库有没有遭到破坏。

最后,顺便一提,按照我的一贯标准,这个工具是跨平台兼容,并在各平台上都能做到文件名安全(无论处理的文件名多么奇怪都不会有问题)的~
Oops, something went wrong.

0 comments on commit ee74ecc

Please sign in to comment.