逝水流年

This is a blog to record my life, my work, my feeling …

2012年年终总结

前一个公司年底总会有一个年终总结,新公司还木有这个要求,但是作为个人来说,也应该对这一年的工作和学习做一次整理了。

上半年干了一件大事,也是比较冲动的决定。老婆辞职后开始了我们的创业大计。说是大事,的确是我们家最大的事情了,但是也着实冲动了一些,没有进行详细的市场调研和分析,行业和选址都不是很合适我们,具体细节此处省略1w字…。最终还是失败了,总结起来是花钱学习自己的缺点了。一是没有选择自己熟悉并且喜欢的行业,导致后期无法坚持下去,二是高看了老婆和自己的能力(老婆吃不了苦),三是做事欠考虑太冲动,四是拉不下脸做销售。折腾了4个多月,每天白天上班,晚上回家还得干活,基本没有12点之前睡觉的时候,没有赚钱不说,身体也吃不消,最重要的是工作也没有精神做,好在公司上半年不忙,也对付过去了。

下半年是忙碌的半年,也是收获颇大的几个月。首先时因为上半年的拖拉,原定的计划完成的特别的不好。只好先重新制定计划,大概简单的计划就是要看完至少2部专业书籍,学一门新语言,完成一个项目。截至到目前为止目标基本完成,《重构》、《Effective C++ 3nd》、《Python核心编程》前两部已经看完,python还有5章,估计春节前应该可以完成。语言嘛,那就是python了,一边看书一边熟悉,加上正好项目中应用到python,自己也是用python写了几个小程序

CSDN博客备份工具 python偷偷发邮件

自娱自乐了。也算是小有收获。9月到12月加入的win phone 8项目,对这边的项目整体开发流程和各种工具的使用都真正的用上了,这里面尤其是codereview比较实用(特别喜欢俄罗斯的年轻程序员小谢同学,真的非常认真,有时候为了一个问题,可以讨论一下午的时间)。通过几个月的努力,基本上已经摸熟了大部分流程。这期间本人的英语的读写也有突飞猛进的进步,老外同事来后也能忽悠一两句了:)。11月还有幸去微软北京总部参加了“高层交流会”长了回见识!:)(是保证能拿点奖品的目的去的,大家不要鄙视我啊!)

11月看了王垠的一篇《手把手教你把Vim改装成一个IDE编程环境(图文)》,立刻被吸引到VIM上来了。找了很多关于VIM的资料来学习使用,将VS也安装了VSVim插件用来练习各种VIM常用命令,不论是win版还是linux版都尝试了一个遍,看着自己通过各种commands来操作,哈哈很有成就感啊。现在基本上也算的上更好,更快,更强阶段了。而且通过对VIM的学习,间接的也学习了gcc,makefile,gdb等等。而且是越用越有意思了,当然啦,大项目工程还是IDE比较好用。:)

今年的收获就是对unix类系统有了很大进步的了解,常用shell commands的使用,也关注了很多非常棒的博客和论坛,RSS订阅了好几个技术博客,开始慢慢形成了自己的一点风格了。

总的来说,今年过得还算充实,有不尽如意的地方,也有有所成绩的地方,但是这些即将成为过去。告别2012,进入2013,将是一个崭新的一年。也是跨入而立后的第一个年头,也算是自己的一次新生。13年将会计划的更饱满,收获将会更多!未来把握在自己的手里。

python版CSDN博客备份工具

前一阵子看了gzshun的一篇文章《自己动手编写CSDN博客备份工具-blogspider》,恰巧当时正在学习python,遂萌发了用python写一个类似的备份工具。由于当时项目太忙,导致这个想法一直被搁置到项目尾声,正好这几天bug不多,《python核心编程》基础部分也都看完了,于是就有了这个程序。

这里只记录下程序实现中遇到的问题,全部程序会上传到下载中,希望对python学习者能有所帮助。

第一个问题是http库的使用的问题,最初我选择了urllib库,因为其接口简单明确,使用起来非常方便,但是就是无法访问CSDN博客,估计是csdn blog对类似spider进行了过滤。没办法只能退而求其次使用urllib2库,指定request的headers就可以顺利访问csdn blog了。

1
2
3
4
i_headers = {"User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 6.0; zh-CN; rv:1.9.1) Gecko/20090624 Firefox/3.5"}
urllib2.Request('http://blog.csdn.net/goof', headers=headers)
page = urllib2.urlopen(req)
page.read() #我的blog的主页内容

第二个问题是对字符串中特殊字符过滤问题,由于名字可能会有一些特殊字符,会导致无法创建文件或文件夹,所以必须要对字符串进行过滤。必须是对unicode的字符串的过滤。

1
2
3
not_letters_or_digits = u'!"#%\'()*,-./:;<=>;?@[\]^_`{|}~ \n\r'
translate_table = dict((ord(char), u'') for char in not_letters_or_digits)
mystring.translate(translate_table)

第三个问题是html parser,最初使用的是python自带的HTMLParser库,结果遇到当html body过大无法处理的问题,最后选择了第三方的Beautifulsoup库,一个强大而又方便使用的库。

最后的遗憾是在对正则表达式的使用上,自己还是非常的生疏,一遍查找资料一遍测试,最终虽然勉强完成了功能,但是还是对程序的机构和效率方面不慎满意。不过这也是个好事,可以继续升级维护这个工具,让它慢慢完美!

完整程序的下载地址 希望大家多批评指导。ps:python2.7.3 beautifulsoup-3.2.1

使用List::unique()代替循环判断

今天很兴奋,帮助同事改进了程序的算法,将Filter时间提高了将近5倍。

原来的程序采用循环对list进行判断,防止重复push数据,而且是循环嵌套循环,时间复杂度0(n2),而改用list::sort()和list::unique()后,效率得到了很大的提高。

看来《STL源码分析》木有白看啊!

delete对象后到底要不要将对象置为NULL

以下是我的一个美国同事丹尼尔对此的看法:

Member pointers do NOT have to be set to NULL in a destructor. A destructor, as its name implies, destroys an object and the object can’t be accessed after that. So accessing anything (even if it were a pointer set to NULL) is absolutely wrong. I usually take setting pointers to NULL out of destructors because it is not needed. If an object gets accessed after it is destroyed, in any form, than it has to be tracked down. But NOT by accessing any of its member variables.

对象指针不需要设置为NULL在析构函数中。析构函数,顾名思义,销毁一个对象而之后该对象不在被使用到。所以存取任何东西(即使指针被设置为NULL)都是绝对的错误。我一般都将设置指针为NULL的语句从析构函数中移除,因为它并不被需要。如果一个对象能够在它被销毁后存取,那么这将能被追查出。但是不是因为存取它里面的任何成员。

对此他还写了个小程序来证明他的观点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class A
{
public:
    // Constructor
    A()
    {
        m_pMember = new int;
    }
    // Destructor
    ~A()
    {
        delete m_pMember;
        // UNNECESSARY: You can't access that pointer after the destructor!
        m_pMember = NULL;
    } int* m_pMember;
};
int _tmain(int argc, _TCHAR* argv[])
{
    // Instantiate
    A* pA = new A;
    // Destroy, destructor gets called!
    delete pA;
    // THIS IS WRONG, you can't access m_pMember anymore. You can't even check if for NULL!
    // This probably won't crash in 99%  of the cases, but it can crash!
    if (pA->;m_pMember)
    {
        // Do something
    }
      return 0;
}

看起来他的程序的确能证明他的观点。但是他忘记了一点,关于对象的copying。如果对象pA在delete之前copying给了其他对象,那么结果可是大不一样啊!让我们稍微改动下程序再看看效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class A
{
public:
    A() { m_pMember = new int;}
    ~A() {
        delete m_pMember;
    }
    int* m_pMember;
};
int _tmain(int argc, _TCHAR* argv[])
{
    A* pA = new A();
    A* pB = pA;
    delete pA;
    if (pB->;m_pMember)
    {
        int temp = *pB->;m_pMember; //Crash
    }
    return 0;
}

这还只是个简单的情况,如果涉及到多线程或者多模块如多个dll之间传递对象,那么就极有可能因为某一对象已经销毁后,没有设置为NULL导致程序Crash。在这里还是建议各位一定要谨慎处理指针问题,避免引起不必要的奇怪问题。

ListBox刷新闪烁问题

UI采用的是xaml,ListBox 的 ItemsSource 绑定 后台model 的IList,当list更新后,UI会出现闪烁。

原因竟然是出在Run属性的使用上,很奇怪。将run 去掉,改用多个TextBlock闪烁消失。

1
2
3
4
5
6
7
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">;
     <TextBlock>;
         <Run Text="{Binding Path=DistanceText}"/>;
         <Run Text=" " FontSize="2" />;
         <Run Text="{Binding Path=DistanceUnit}" />;
     </TextBlock>;
</StackPanel>;

关于老外程序员的看法

这里只写一个一直给我们review code 的谢尔盖,一个年轻的俄罗斯人。给我留下的印象是对待技术比较认真,对待每个review都非常仔细。连变量命名的大小写都没有放过。尤其是在一些功能的实现上面,如果有更好的实现,他一定会告诉你。但是你跟他矫情下,他也一定会放过你,真是一个非常有意思的人。我的一个review来来回回review了三四遍,才顺利通过。虽然费了半天劲,但是效果还是蛮好的。

谈程序员应该如何完成任务

这两天看了一篇关于重构的博客,结合前段时间看完的《重构》,对程序员的工作行为有一点点感悟,记录下来跟大家分享一下。

如果老大给你分派了一个任务,在原系统中添加某某功能。作为一名员工,你需要做的就是将功能没有BUG的,高效的实现就可以了,并不需要关心系统其他的部分代码,也有可能你在添加功能的时候阅读了一部分原有代码,也发现原有代码的一些问题,比如效率低,结构差等等,可是可能由于各种理由(工期紧,能良好运行,客户认可等等)并没有对这些问题进行修改,以后也不会对其进行修改。OK,可以说你按时,保质保量的完成了任务,还有可能得到表扬,但是作为一名程序员来说,你并不合格!同时你也丧失了一次自我提升的机会!

我决定作为程序员,不仅要对自己的程序负责,也要对产品的其他程序负责,既然看到了问题,就应该修改,或者记录下来,提供给决策者来进行后期重构或者升级时的修改项。如果是小改动,那么应该花费少量的时间直接修改过来。虽然花费了些许时间,但是对于产品来说,可能会有很大的性能提升,或者用户体验得到大大的改进,或者为以后维护提供了大大的便利;对于个人来讲也是对个人能力的展现,让后来维护人员对你的一种‘膜拜’。

所以,我认为简单的完成任务并不可取;在完成任务的同时,你应该可以获得更多东西!

使用python偷偷发邮件

以前的公司上班上网或者收发邮件都得小心翼翼的,怕一不小心被有心人瞧见,又得被说说。为了能发邮件而不被发现,嘿嘿,我就用python写了个邮件发送程序,用控制台控制,不了解的人一定以为哥还在编程工作呢。哈哈。

下面简单介绍下如何使用python发送邮件,包括普通文本内容,也可以带附件,或者HTML内容的邮件。可以说有了python,一切都变得非常的容易。 smtplib 模块是用来发送email的标准module,另外配合email.mime内的几个module实现起来就非常的简单。

1
2
3
4
5
6
7
8
9
10
 def sendmail(message):
    try:
        smtp = smtplib.SMTP(EMAIL_HOST)
        smtp.login(EMAIL_USER, EMAIL_PWD)
        smtp.sendmail(EMAIL_USER+"@"+EMAIL_POSTFIX, TO_EMAIL, message)
        smtp.quit()
        print 'email send success.'
    except Exception ,e:
        print e
        print 'email send failed.'

首先要导入smtplib模块,加入import smtplib,初始化的时候直接连上邮箱服务器,也就是上面的EMAIL_HOST(我使用的是163邮箱,所以定义EMAIL_HOST = ‘smtp.163.com’),由于目前的邮箱基本都需要登录才可以使用,所以要调用smtp.login()函数,输入用户和密码。

1
2
3
4
def sendwithoutattachment():
    msg = MIMEText(getcontent(), 'plain','utf-8')
    getheader(msg)
    sendmail(msg.as_string())

我将发送简单文本内容的邮件单独独立为一个函数,使用MIMEText生成,注意这里用到了utf-8,因为内容有可能是中文,所以要特别指定。如果要发送html内容,则讲plain更改为html即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def getheader(msg):
    msg['From'] = ME
    msg['To'] = ";".join(TO_EMAIL)
    msg['Subject'] = EMAIL_HEADER
    msg['Date'] = formatdate(localtime=True)
   getheader函数是用来设置发送者,接受者,主题和发送时间的。
def getcontent():
    path = os.getcwd()
    file = os.path.join(path, CONTENT_FILE_NAME)
    content = open(file, 'rb')
    data = content.read()
    try:
        data = data.decode('gbk')
    except :
        data = data.decode('gbk','ignore')
    content.close()
    return data

至于邮件正文,我是事先写到一个TXT文档中,读取出来。这样也比较隐蔽。:)要主意中文编码。

1
2
3
4
5
6
7
8
9
10
11
def getattachment(msg):
    ctype, encoding = mimetypes.guess_type(ACCESSORY_FULLPATH)
    if ctype is None or encoding is not None:
        ctype = 'application/octet-stream'
    maintype, subtype = ctype.split('/', 1)
    #Formating accessory data
    data = open(ACCESSORY_FULLPATH, 'rb')
    file_msg = MIMEBase(maintype, subtype)
    file_msg.set_payload(data.read( ))
    data.close( )
    encode_base64(file_msg)
#file_msg["Content-Type"] = ctype # if add type then return error 10054
file_msg.add_header('Content-Disposition', 'attachment', filename = ACCESSORY_NAME)
msg.attach(file_msg)

附件同样独立为一个函数来创建,这里要注意的是不要指定“Content-Type”类型,否则无法发送邮件。这个问题还没有解决。 以上基本包括了发送邮件的主要几个函数,具体smtplib模块和MIME等内容,资料很多,这里就不详细讲解了,有感兴趣的可以上网search。

Python编码规范之命名规范

1、不要使用小写字母’l’(el),大写字母’O’(oh),或者小写’i’作为单独变量名称。因为一些字体中,上诉字母和数字很难区分(比如:O和0,l和1)。

2、Module应该采用全小写,并且尽可能短的命名,可以在模块名中使用下划线以提高可读性。Package应该采用全部小写,并且也要尽可能短的命名,但不允许使用下划线。当一个用C 或C++ 写的扩展模块,有一个伴随的Python 模块来提供一个更高层(例如,更面向对象)的接口时,C/C++模块名有一个前导下划线(如:_socket)。

3、类名使用首字母大写单词串(CapWords) 的约定。 内部使用的类使用一个额外的前导下划线。

4、因为异常应该是类,故类命名约定也适用于异常。然而,你应该对异常名添加后缀”Error” (如果该异常的确是一个错误)。

5、对设计为通过”from M import “ 来使用的模块,应采用all 机制来防止导 出全局变量;或者使用旧的约定,为该类全局变量加一个前导下划线(可能你想表 明这些全局变量是”module non-public”)。

6、函数名应该为小写,必要时可用下划线分隔单词以增加可读性。 混合大小写(mixedCase) 仅被允许用于这种风格已经占优势的上下文(如:threading.py),以便保持向后兼容。

7、对实例的方法,总是用’self’ 做第一个参数。对类的方法,总是用’cls’ 做第一个参数。如果函数的参数名与保留关键字冲突,在参数名后加一个下划线,比用缩写、错误 的拼写要好。因此 “_print” 比 “prnt” 好。(也许使用同义词来避免冲突更好。)

8、方法名和实例变量:采用函数命名规则:小写单词,必要时可用下划线分隔单词以增加可读性。仅对non-public 方法和实例变量采用一个前导下划线。为避免与子类命名冲突,采用两个前导下划线来触发Python 的命名重整规则。Python用类名重整这些名字:如果类Foo 有一个属性名为a, 它不能以Foo.a 访问。(执著的用户还是可以通过Foo._Foo__a.得到访问权。)通常,双前导下划线仅被用来避免与基类的属性发生名字冲突。

9、常量定义全部为大写,必要时可用下划线分隔单词以增加可读性。