怎样花两年时间去面试一个人「轉」

原文鏈接:http://mindhacks.cn/2011/11/04/how-to-interview-a-person-for-two-years/

Joel Spolsky曾经感叹:招聘难,难于上青天(此处笔者稍加演绎:))。他有两个辛辣但不乏洞察力的断言:真正的牛人也许一辈子就投大概4次简历,这些家伙一毕业就被好公司抢走了,并且他们的雇主会给他们不赖的待遇,所以他们也不想挪窝。(刚刚去世的Dennis Ritchie就是这样一个人)而“人才”市场上能找到的大多都不是什么人才。招到这帮人轻则费钱重则把你公司搞挂。

(当我把这篇文章给邹欣老师review的时候,他说了另外两 点:1. 最好的人也许不投简历,就决定去哪里了。所以要在他们做决定前找到他们。2. 比较差的会投很多次简历,找不到工作的时间越多,投的简历越多,给整个pool 带来很多噪音,top10%的简历也许根本不算全部人的top10%。)

诚然,也许没有哪个行业像IT行业这样,无形资产占据公司的绝大多数资产。拒坊间传言比尔·盖茨就曾经说过类似这样的话:只要允许我带走100个人我可以再造一个微软。这话没搜到原版出处,但是从一个侧面反映了IT公司当中智力资产所占的比例之重。

所以一个自然的推论就是,招聘也许是一个公司决策当中最最重要的一个环节。Joel Spolsky把他在这方面的观察,体会和洞见集结成了一本小册子《Smart and Gets Things Done》,开篇就挑战“产品是公司成败的关键”这个传统观念,他认为创造最适合工程师生活的环境,留下最优秀的人才才是最先最重要的一步,接下来好的产品是水到渠成的事情。国内iapp4me.com创始人郝培强正是这个理念,所以他在微博上说

我们是小公司,工资开的不高,也不招太多的人,但是电脑都是iMac27,iMac21,Macbook pro15,基本上比很多大公司都好多了。软件没盗版,刚才photoshop的正版我也收了。中午管饭,公司备伞。哈哈。节日假正常放,从不加班,早晨 11点上班,下午6点下班。我是有资格说某些大公司的员工苦逼的。

事实上,米国找个人尚且难成这样,搞得Joel还费心费力写本书语重心长地劝企业们要善待好工程师,国内找个人更是难上加难,国内高质量问答社区知乎创始人周源就曾经在知乎上分享他呕心沥血的招人历程,看完真是让人慨叹这年头找个靠谱的人多不容易(这条知乎问答还有很多精彩的跟帖):

其实从 08 年到现在,我一直想这事能不能有点窍门,或者是实用的方法,结论是几乎没有。我用过的大家都用的方法:

  • 在水木上发贴子(有点效果)
  • 在蓝色理想上发贴子(无效)
  • 在技术邮件组里发贴子(无效)
  • 买 51job/智联 最便宜的服务(有点效果)
  • 给所有可以想到的人打电话,请他们推荐(无效)
  • 给所有和你讨论过创业,喝过点小酒的人打电话(无效)
  • 约前同事私下谈(有效)

我用过的大家可能没有用的方法:

  • 上 twitter,看 XXX 的 follower,一个一个看,看他们的 twitter,博客,Google Reader 分享,想办法搞到邮件,联系,半夜电话骚扰。
  • 上豆瓣,前端后端挑几本重量级的书,去找想看,看过,正在看这本书的人,一个一个看,看他们的活动,博客,Google Reader 分享,想办法搞到邮件,联系,半夜电话骚扰。
  • 找同事,问他们都看什么技术博客,想办法搞到邮件,联系,半夜电话骚扰。

正是这样的不容易,才有不少公司走内部培养的办法,这里的逻辑是:一上来就招到靠谱的人太难了,但找一块靠谱的璞玉然后雕琢雕琢相对就简单很多。这倒是个办法,但这样做的人难免就陷入了纠结:培养好了,人跑了怎么办。这也不能怪招聘的公司,的确是人之常情。其实解决的办法也很简单,培养的时候进行适当引导,让员工发挥自己的主动学习能力,这样不但人得到更多成长,公司也不会觉得投入太多患得患失。所谓师傅领进门修行在个人。

但是,这仍然还是没有解决根本的问题,就是招聘真的很困难。应聘者固然觉得自己是在“海投”,大海捞针一般。而招聘者何尝不也是这种大海捞针的感觉。这就好比两个人谈恋爱,都想和对方好上,但是偏偏就聊不到一块去。

招聘真的很困难。以至于招聘者每年需要绞尽脑汁出新笔试题,以免往年的笔试题早就被人背熟了。出题很费脑子,要出的不太简单也不太难,能够滤掉绝大 多数滥竽充数的但又要保证不因题目不公平而滤掉真正有能力的,要考虑审题人的时间成本就只能大多数用选择题,而选择题又是可以猜答案的(极少有人会在选了 答案之后还敢在空白的地方写为什么选某答案的原因的)。更悲催的是,有些题目出的连公司的员工们自己都会做错(真的是员工们做错了吗?还是题目本身就出错 了?)

笔试完了之后如果还没有被鄙视就要进入面试环节,姑且不说笔试题的种种弊端,就说面试环节,短短几个小时的面试(大多数公司也许连几个小时的面试时间都没有),既需要全面考察基本知识,又要考察编程素养,还要考察(也许最重要的)性格心态。再然后还有一项根本没法考察但却占据程序员相当一部分工作时间的:debug能力。面试官不但得找准问题,不因对方一题答对而妄下结论,也不因一题打错而就扼杀机会,还要以管窥豹,从一朵花看到整个世界,从面试人的举止言谈,分析问题的方式,甚至写程序的笔迹来观察这个人的性格,做事的方式和心态,简直是要面试官具备心理分析师的水准才行。

这厢要招人的雇主苦不堪言,那边找工作的人也是一团乱麻。绝大多数应届生直到毕业也不清楚他们想要去的公司到底需要什么样的能力,或者说,他们到底 需要具备什么样的能力才能在应聘季节拥有自己的选择权。中国虽然本科教育环境差,但是同样有很多的人在本科希望整点东西出来,他们有一腔的激情和抱负,有 强大的动力,但就是不知道自己需要掌握哪些技能才能满足雇主的要求,求告无门,整年整年苦闷的像没头苍蝇一样乱撞(我就收到过很多次这样的来信,他们往往 很想学点东西,但又不知道哪些重要哪些不重要,到底该学到什么程度,不知道导致不确定,不确定导致决策瘫痪,干脆嘛也不动,荒废时间)。

什么叫熟练?什么又叫精通?那么扎实呢?两年的YY经验又意味着什么?能这么简单的量化吗?同样是两年的“实践”有的人能真的学到点东西,有的人也 许近似一无所得。那么实习呢?很多人都一定要在简历上弄个实习经验,这个又能说明多少问题呢?大作业呢?得奖呢?有一次我面试一位同学,据简历说编译原理课的大作业得了一等奖,可我一问什么是递归下降,就傻眼了。

这个现实的结果就是,现在绝大多数应届简历而言,也许最具信息量的部分不是“精通XXX,熟悉YYY,掌握ZZZ”,不是“在UUU实习过”,也不是这个项目那个作业,反倒是越来越被认为不重要的一项:毕业学校毕业学校本不应该是最具信息量的,它之所以最具信息量只是源于一个悲剧的事实:简历上其他条目实在信息量太少了。 所以靠谱的面试者往往学会了无视简历上华而不实的内容,只相信面试的时候亲眼所见,扫两眼简历也就罢了,最后还得自己捋起袖子慢慢面。而应聘者也许也知道 招聘的也不会细细纠简历上的条目,所以什么词也都敢往上捅,反正先过了HR筛简历这关再说。从经济学角度来讲,应聘者的这种策略是正确的,没有代价(因为 目前似乎没有公司会去给已经申请过的人做一个诚信数据库),但至少有可能会带来巨大的收益。应聘成了博彩。而博彩式的应聘给招聘公司带来了巨大的筛选压 力。简历成了摆设。

那么招聘这个关系里面的第三者——学校——所处的位置呢?学校更关心的是毕业率和就业率,这似乎是件好事,有这个为目标,那么老师们似乎应该努力让 自己的学生多学点东西。可惜就业的质量似乎不是最重要的指标,此其一。其二老师本身大多数没有丰富的业界经验,根本不知道企业整整需要的人才是什么样的, 可能花了精力,但却培养不出雇主真正需要的人。另一方面,老师所起的作用很多时候甚至是一个负面的作用,例如布置大作业表面上看上去是培养学生的能力,我 们姑且不说抄袭,假设每个人都做了,那么大作业本身能够衡量多少东西呢?能否衡量代码质量,能否衡量团队协作能力?能否衡量交流能力?考虑到大作业用到的 东西往往都是书里面现成的,大作业甚至不能衡量学习能力。而学习能力简直算是这个行业最重要的能力没有之一了

所以,简而言之,如果把人才培养/招聘这件事情本身类比做一个项目,那么这整个项目迄今为止就是一个巨大的失败。为什么这么说呢:

  • 和需求严重脱节:作为人才需求方的雇主的需求到底是什么?绝大多数应聘者都没搞清。更严重的是,这却一点都不是应聘者的错。因为雇主是stakeholder,是雇主自己的责任得去说清楚需求是什么。结果应聘者实现的不是雇主想要的,雇主想要的应聘者没有实现。
  • 应聘者雇来培训自己的人根本不管事:学生交了学费,就相当于雇老师来培训自己,可培训者根本也不了解(或不关心)他的客户们的需求。这里,学生是需求方,老师则是实现方。弄清需求的职责在后者,可后者也弄不清。
  • 学生自己也弄不清:学生自己既是需求方(需要特定技能),也是实现方。可他们自己也弄不清需求到底是什么。

以上三点还不是最严重的,最严重的在下面:

  • 明白需求是什么的也不知道怎么实现:怎么去培养现代IT企业真正需要的人才?特别地,实战能力怎么培养?代码素养怎么培养?协作沟通能力怎么培 养?学习能力怎么培养?就算这些都知道怎么培养,又怎么给在象牙塔里头,离催命之日还遥遥无期的学生提供足够的动力呢?而学生自己就算知道该学哪些技能, 又怎么知道具体怎么着手?什么是最有效率的学习方法?又如何让自己保持学习的热情?

以上这些问题,就是当下人才培养/招聘的惨淡现状。简而言之,在雇主和学生之间,横梗着一条巨大的鸿沟,两头都很着急,两头都有动力,但就是没有方 法,君住长江头妾住长江尾。像微软谷歌这样的,干脆和高校合作,直接插手本科或硕士的教育,从而保证到时有足够强的候选,某种程度上,这的确是根本解决之 道,可一来这代价太大了,非一般企业承受得起,二来这影响面也太小了。

这一切,也许将在未来的5年发生根本的变化。

《Switch: How to Change Things When Change Is Hard》(中译《瞬变》)里面指出,表面上看来非常困难的改变,也许是因为根本就没有抓住要害。在书中作者通过大量案例分析和心理学研究,雄辩地指出以下几点促成改变的关键之处:

  • 触动内心的大象:要改变的人必须要有情感层面的动力。有一些特定的方法能够比另一些方法更能对人的情感产生触动。
  • 给出清晰、明确的目标:目标一定不能含糊,模棱两口的目标让人无所适从,导致决策瘫痪。 例如最近我们组在招实习生,我在微博上发了一条招聘信息,其中提到“扎实”的系统底层知识,有同学就写信来问,怎么叫“扎实”。我傻眼了。比尔·盖茨就以 目标清晰明确著称,不仅在战略制定上,“每个人桌面上都有一台PC”,而且居然还体现在招聘上——“如果你读完了TAOCP,那么就给我投简历吧”。多么 清晰,明确的目标啊——虽然高了点,也许这就是比尔·盖茨至今还没被应聘邮件淹没的原因:)
  • 给前进的道路扫清障碍:人是懒惰的,只要有借口就会不想往前。如果既有明确的目标,同时道路又直直指向目标,一览无余,只等你开始往前走,那么便没有借口,一往无前。

那么让我们对照上面看看,可以做什么?

首先,内心的大象不需要触动,中国有足够多的人足够早就开始焦虑就业的事情,只是不知道往哪使劲,这部分人如果把劲头用到正确的事情上面也许足以满足现在的IT企业人才饥渴了。至于其他人,好吧,也许身边的人开始动起来他们也会被触动。

然后是清晰、明确的目标。这一点上目前雇主们的做法可谓好坏参半,好的一点是大家都强调要有实践经验,要有团队协作精神,坏的一点就在基础知识和技 能的要求方面,可谓再含糊不过了:“精通XX语言”,“扎实的XX功底”,“熟悉XX技术”,甚至看上去最具量化感的描述“X年YY经验”其实都根本说明 不了多少东西,在信息量方面还不如我家门口菜市场上一家卖酥油饼的店门口挂的横幅——“三天不硬、至少六层!”。

很多朋友也许注意到一个现象,现在企业对招聘者简历的要求也在变得越来越灵活变通,例如ThoughtWorks在招聘的时候就希望招聘者能给出自己的博客地址,博客对IT行业的意义也许胜过其他所有行业,一个积累多年的技术博客比任何简历都更能说明问题。台湾的郭安定也说“为什么写技术博客对新人如此重要”。可惜这个做法也有一个弊端:并不是所有技术牛人都写博客,有人就是只干不说型的,而就算写博客,乃至动手写过一阵子的,写一个常年的博客,也远比你想象的更为困难,因为很多时候,写(说)得靠谱比做得靠谱更难。所以这个过滤器很多时候用不上。

但是这的确表明了一个思考的方向,就是寻找更具鉴别力的过滤器,Stackoverflow Careers 2.0之所以强大,是因为Joel Spolsky和Jeff Atwood这两位常年混社区的资深博主创造性地将一个人在社区的活动历史浓缩成为一系列的量化数值,由于这个历史很长期,所以鉴别力非常高。但它同样也有问题,就是对于应聘者来讲相当花费时间,而且并不是花时间(在Stackoverflow上回答问题)就一定能花到点子上。

到底什么特征才是既通用,又能够有效地鉴别高低应聘者的特征呢?这个特征必须不像博客那样难以实现,同时又必须有足够的区分度

有的地方在要求填写简历的时候必须填上平时都访问哪些技术网站。恩,很不错的尝试,可区分度仍然还是不够,因为上网站上查东西毕竟只占现阶段大多数应届生的少数信息来源,特别是当我们看重得更多的是应届应聘者的系统性的知识基础的时候,网上的东西虽然丰富,但属于提高班,也更为琐碎,什么是更系统的知识来源呢?答案其实大家都知道——

书。

我一向认为,很多时候,是否好好看完一本好书,对一个人的提升往往能达到质的区别。就算不好好看完一本好书,马马虎虎看完,只要书是真的好书,也肯定会有很大的提高。我在面试的时候就经常询问对方看过哪些技术书籍,经常上哪些网站,订哪些博客。这里头尤其数书籍这一项的区分度最高。此外,好书和坏书的差别,从本质上,就是学习效率和大方向的差别。一本烂书可以浪费你半年的时间,但一本好书却可以为你带来真正扎实的基础和开阔的视野。人们常常用“内功”来形容扎实的基础,认为学好了内功以后学什么都快,其实一点没错,好的“内功”书不仅讲清楚深刻的原理,而且指明技术的本质,刻画领域的地图。好的书抓住不变量,让人能够触类旁通。好的书不仅介绍知识,而且阐释原则,介绍那些万变不离其宗的东西。读烂书浪费时间,但读好书却节省时间

象牙塔内的学生受到视野的限制,往往择书不慎,事倍功半,烂书不仅浪费时间,还会打击人的积极性,让人对知识心生恐惧,认为很难掌握,殊不知只是作者没有讲好(或者没有翻译好)。因此,为招聘头疼的公司完全可以给出“应聘俺们公司前必读的十本书”,也不一定要每个公司都不一样,在某个技术子领域有影响力的人,或者创始人们,可以来定义具有代表性的书单。

我们姑且把这个计划叫做“书单计划”,容易看到“书单计划”具备以下几个卓越的优点:

  1. 清晰、明确。完全可度量。
  2. 防伪:读没读过,随便一问便知。而正因为应聘者也知道这事不像实习经验可以忽悠,所以也不敢乱往简历上捅词。
  3. 不在乎是否“泄题”:书单完全公开的,无所谓,本来就是要你去读的。想背题?背书吧。真能背下来说明认真看了。
  4. 管你用心不用心读,只要读了,读完了,就有区别。真正的好书,你想不被吸引都难。据我观察很多人就是不知道该去读什么书。
  5. 不存在“怎么做”的障碍:所有人都知道怎么读书——一页一页读。
  6. 不需要招聘者投入精力:书单在此,就这么简单,您看着办。
  7. 评估的负担很大程度转移到了应聘者的身上:是不是认真看完了,有没有心得体会,您自己掂量。没看完别来找我们。

“书单计划”能很大程度上起到强鉴别器的作用,看了就是看了,必然能学到东西,没看就是没看。知道和不知道,区别是本质的其实很多企业内部培训,根本上其实还不就是叫员工去看之前没看过的书或者资料嘛。最后,除了鉴别作用之外,它还是一个清晰促进的目标,是完全不花精力的培养

当然,“书单计划”的背后是另一个悲剧的现实,如果不是因为这个现实,这个计划也完全没有必要,那就是,中国IT大学教育当中要求要学的书,和企业真正需要你去读的书相比,不是完全不够用,就是写的不够好,或者更悲剧的就是根本用不上,所以在这个大背景下出来的牛人都是自己淘书自己学的。微软高级开发测试工程师,《Windows用户态程序高效排错》作者熊力就在微博上说过:“我当年毕业的时候总结了一个公式:第一份工作的月薪=大学四年买过的技术书籍价格的总和。”

但是光有“书单计划”还不够,因为书籍只能管基础知识这一块,一些更难以量化衡量的实战“能力”又怎么办呢?至 少目前为止,除了“练”之外好像还没有特别好的办法。可是在象牙塔里面做的项目,或大作业,真的能起到练的作用吗?前面说了,学生会知道自己最终要交差的 不是雇主,而是老师,于是就以老师能够评判的标准来默认要求自己了,老师能够评判编码素养?代码风格?文档?设计?协作?甚至连著名的Joel 12条的第一条“是否用源代码管理系统”都没法通过。所以大多数时候,大作业能起到的作用近乎0。

但是如果这一切是由雇主来评判的,这个“作业”是由雇主来给出的,就完全不一样了。一想到作业是要作为简历的一部分的,能不紧张嘛。能不好好做嘛。能不学到点东西嘛?

可是这事儿能实现吗?雇主能给学生出大作业吗?也许一两个关系好的高校可以,可是中国那么多学生呢?

为什么不能呢?如果像书单那样,列出各个技术领域“推荐在学校期间尝试的项目”,至于动不动手做,那是学生自己的问题。做的,自然能够得到锻炼,面试的时候自然能得到更大的优势。

可问题是,面试的人又怎么来评估呢?这不又回到了没法有效评估的怪圈了吗?答案很简单,但这个答案,直到最近几年,才真正成为现实——

GitHub

GitHub诞生于08年春天,第一年便产生了4万6千个公共项目,大约一年半之后用户就已经达到10万用户之巨。而到今年九月份,GitHub已经迎来了百万级用户。Host超过两百万个项目。

增长的太快了!就像Twitter一样。这样疯了一般的增长只能说明一个事实——人们等待这个产品太久了

Social Coding

真实的项目,真实的流程,真实的人名,一切代码review, check-in, test, build, document, 甚至讨论,计划,brianstorming,流程,一切的一切,都是项目历史的一部分,都可以像棋局那样复盘。有经验的面试者只要稍稍扫两眼一个人的 GitHub历史,挑出几个check-in历史看一看,便完全能够迅速判断这个人是否满足他的要求。不再需要费劲心机地去想题目,去观察,去揣测,去花 费大量的时间的同时还只能采样到几个极为有限的点。

不像象牙塔里面大作业,这里有源代码管理系统,自动化build,有check-in,有review,有分工,有合作,最重要的是——这是一个集市,一个超出象牙塔的集市,牛人相互吸引,你可以在互联网上找到和自己拥有共同兴趣的一帮人,真正做起一点事情,而不是交差,不需要受限于几十个人的一个小班级。Here Comes Everybody

为什么我这么有信心?因为这事儿已经发生了。这个想法也完全不是我原创的

正如很多事情一样,现在在国内发生的事情,往往是美国那头的历史。今年7月中旬,纽约一家公司的工程师老大发了一篇博客文章:Github is Your New Resume。指出一个惊人但再合理不过的事实:越来越多的IT公司在招聘的时候要求应聘者给出GitHub账号。甚至已经有人为GitHub写了根据GitHub上的历史自动生成简历的工具

仔细想想,这是必然的趋势,没有比这个再合理的事情了,既然StackOverflow的历史能够作为简历,GitHub的历史不本该就是更好的简 历吗:你想要具有实战经验,懂check-in懂review懂test和代码质量的重要性,懂交流和沟通的重要性,你本就应该在一个真实的项目当中去锻 炼这些东西,而这些在目前已经完全可以办到。正如邹欣老师所说,你的工作就是最好的面试

这件事情放在早几年,是完全没法做到的,因为我们那时候还没有GitHub。正如没有Twitter,没有微博之前,很多事情都不会成为可能一样,你有千钧之力,缺乏一个合适的支点,也没法撬动一整个社群。无组织中的组织,具有强大的杠杆效应。

这个事情里面,我唯一提出的东西就是:在目前国内这个现状下,苦闷的招聘者应该主动行动,给出一些建议项目,正如前面提到的书单计划一样,招聘者需要给出的只是引导和清晰明确的目标, 剩下的事情,应聘者自然会去完成,这些项目可以是实验项目,也可以是完全能做出点卖钱的东西的项目(如果好好做的话),唯一的不可或缺的前提是,项目不能 太小,单人就能完成的项目不理想,一两个月就能完成的项目不理想,最好足够大到能够锻炼到方方面面,偏大一点倒是无所谓的,因为一个尚未完成的项目完全可 以作为简历。当然,可以想见的是,真到了那个时候,学生们肯定又是不会满足于仅去做那些已经有许多人做过的项目了所以这里企业们一开始所建议的项目只是一个《Nudge》,是滚雪球之前需要的一点初始动能。后面的事情,他们自己会完成。

“GitHub计划”同样有一些明显的、甚至不可替代的优点:

  1. 清晰、明确,完全可度量。
  2. 防伪:同样不担心“泄题”。你伪造不了GitHub历史,伪造不了check-in历史,review comments,文档,交流记录…
  3. 它不但是招聘,也是不花精力的培养。善哉善哉。
  4. 评估的责任很大程度上交给了应聘者自己。

从你的GitHub旅程开始,你就已经一脚踏进了真正的企业,而企业的面试也已经开始。

书单+GitHub,就相当于一个两年左右的面试。

没有什么面试比持续两年的面试更具有信息量。

书单,加上项目,已经基本上覆盖了所需的全部技能。最妙的是,有太多的人在焦急的等待着他们未来的雇主给出明确的信号,他们想投入精力,去学习和实践,去成为企业需要的人,但是他们就是不知道往什么方向走,所谓有动力没方向。所以,雇主给出了清晰明确的要求,相信对于很多人来说反倒是一个解脱:“终于知道该干什么了”。《编程之美》为什么常居畅销榜?因为它透露了雇主眼中的需求,明确、清晰的需求,可以实现,并且知道怎么去实现的需求。

你提前两年就开始面试和培养未来的候选者,而且还不需要你花出一分精力,而且人家还很乐意,没有比这更完美的面试了。

想一想,以后那些没见过世面的公司看见你拿出GitHub账号给他看,该是多么惊讶同时又觉得多么合理。

而这一切,只是因为两个小小的改变:

  1. 由需求方(雇主)给出了清晰、明确的目标。
  2. GitHub这样的平台。

那么,学校/老师在这个事情当中的位置呢?说实话我不知道。没有哪个行业像IT行业这样特殊:没有什么东西不能够(应该)在互联网上学到的。自组织的力量完全大过传统的教育方式。而且,既然雇主都当了领路人了,我不知道还有中间开发商什么事儿。(注:这里说的是软件开发,并非计算机科学研究,后者另当别论

那么,这个改变会发生吗?多久会发生呢?当然,它在国外已经发生了,所以问这个问题多少有点无趣。但我还是预计很快就会在国内发生,毕竟,不是已经 有人要求出示博客,和经常浏览的网站了吗?也许5年左右(4年本科和6年硕士的中间值?))就会深刻改变整个人才培养/招聘的格局。当然,我并不是预言 家,所以不要把我的时间估计当真,我能肯定的是,这种方式是必然的大势所趋。

刚才我就收到一位同学邀请我上知乎回答一个问题“找工作的首要原则是什么?”,当然,这个问题的答案是:“弄清雇主的需求到底是什么”。


列一下我所认为的,你面试微软前必须要读的十本书:

  1. Code: The Hidden Language of Computer Hardware and Software (《编码的奥秘》)
  2. Computer System: A Programmer’s Perspective (《深入理解计算机系统》) / Windows via C/C++ (《Windows核心编程》 / 《程序员的自我修养》
  3. Code Complete 2(《代码大全》)/ The Pragmatic Programmer (《程序员修炼之道》,我也把这本书称为《代码小全》)
  4. Programming Pearls (《编程珠玑》) / Algorithms / Algorithm Design / 《编程之美》
  5. The C Programming Language
  6. The C++ Programming Language / Programming: Principles and Practice Using C++ / Accelerated C++
  7. The Structure and Interpretation of Computer Programs (《计算机程序的构造和解释》)
  8. Clean Code / Implementation Patterns
  9. Design Patterns (《设计模式》) / Agile Software Development, Principles, Patterns, and Practices
  10. Refactoring (《重构》)

(注:1. 以上同一条目下用“/”隔开的表示任选,当然你也可以都读了,相信我,时间是足够的。2. 读这些书并不意味着逐字逐句从第一页读到最后一页——当然你也可以这么做。怎么是聪明高效的读法,可以参考我之前写的关于如何阅读和查找/鉴别书籍/资料的博文

注意:以上是我个人认为你面试微软开发职位前必须要读的10本书,它不代表我的雇主的观点。它也只是一个初步的书单,肯定会受到我个人经验和眼界的限制。欢迎大家提意见。

此外,IT不同子领域的必读书单可能千差万别,所以在发布之前我把这篇文章发给了一些朋友,他们给出了自己的书单(你是不是能看到一些有趣的共同点呢):

云风(中国游戏编程先行者,前网易游戏部门资深程序员,简悦创始人):

如果面试,我会挑以下的我自己读过的书,让人选择他也读过的部分,再了解他对这些书的理解。这些书其实本质上就是两类,对所面对的东西(程序语言也好,操作系统也好,底层设施也好)本身的理解程度。以及另一类:对设计思想和原则的理解:

  1. C++编程思想
  2. Effective C++
  3. 深度探索C++对象模型
  4. C++语言的设计和演化
  5. C专家编程
  6. C陷阱与缺陷
  7. C语言接口与实现
  8. Lua程序设计
  9. Linkers and Loaders
  10. COM本质论
  11. Windows核心编程
  12. 深入解析Windows操作系统
  13. 程序员修炼之道
  14. 代码大全
  15. UNIX编程艺术
  16. 设计模式
  17. 代码优化:有效使用内存
  18. 深入理解计算机系统
  19. 深入理解LINUX内核
  20. TCP/IP 详解

冯大辉(丁香园CTO,贝塔咖啡创始人):

  1. 软件随想录
  2. 黑客与画家
  3. 重来
  4. UNIX编程艺术
  5. 编程人生

洪强宁(豆瓣技术总监):

StackOverflow上有一个程序员必读书单帖子,这里仅列出top10,更多参考这里

  1. Code Complete 2
  2. The Mythical Man-Month (《人月神话》)
  3. Code: The Hidden Language of Computer Hardware and Software (《编码的奥秘》)
  4. TAOCP (不解释)
  5. The Pragmatic Programmer (《程序员修炼之道》)
  6. Design Patterns (《设计模式》)
  7. The Structure and Interpretation of Computer Programs (《计算机程序的构造和解释》)
  8. Refactoring (《重构》)
  9. The C Programming Language
  10. Introduction to Algorithms (《算法导论》)

郑昀(窝窝团研发副总裁):

  1. 工程师入门:
    1. Code Complete 2
    2. 程序员修炼之道
    3. 深入理解计算机系统
  2. 工程师升级:
    1. 设计模式
    2. 重构——改善既有代码的设计
  3. 工程师转型:
    1. 快速软件开发——有效控制与完成进度计划
    2. 人月神话
    3. IT项目管理那些事儿
    4. 软件随想录
    5. 最后期限
    6. 走出软件作坊
    7. 你的灯亮着吗?——发现问题的真正所在

张峥(微软亚洲研究院副院长):

  1. Algorithms (by Sanjoy Dasgupta, Christos Papadimitriou and Umesh Vazirani)
  2. Data Structure and Algorithms
  3. The C Programming Language
  4. The Design of the UNIX Operating System
  5. Compilers (龙书)
  6. Computer Architecture: A Quantitative Approach
  7. Flow
  8. Outliers (why hard work and luck are both important)

邹欣(MSRA创新工程中心首席研发经理):

关于创新的书籍(http://book.douban.com/doulist/1253169/):

  1. The Myths of Innovation
  2. The Innovator’s Dilemma
  3. The Innovator’s Solution
  4. Crossing the Chasm
  5. Inside Intuit
  6. 盛田昭夫
  7. 杰克·韦尔奇自传
  8. 梦断代码
  9. Innovation
  10. 浪潮之巅

关于“精通”的一篇博客《技能的反面:魔方和模仿》:

http://www.cnblogs.com/xinz/archive/2011/08/07/2129751.html

在我教的《现代软件工程》课上,除了教科书,每个学生要看另一本相关的书籍并写读书分析。这个博客有一些同学的读书报告:
http://www.cnblogs.com/OMG-Team/archive/2011/10/25/2223247.html


读好书是如此的重要,因为好书往往带领你去到更好的书,更大的世界。

经典排序算法总结与实现「轉」

原文鏈接:http://xiaok.me/2015/08/27/%E7%BB%8F%E5%85%B8%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E6%80%BB%E7%BB%93%E4%B8%8E%E5%AE%9E%E7%8E%B0/

 

暑假快过完了也就大四了,马上就到了校招季了,作为一个才接触Android半年多的渣渣真的很慌张。最近打算复习一些算法和数据结构的知识,提升一下基础。这一篇是对经典排序算法的总结,共七个,可以说是手到擒来,提笔就要会的了。为了使代码易读,就用最简单的C语言实现了。

后面的排序默认从小到大排列

冒泡排序(Bubble sort)

原理

冒泡排序是一种简单的排序算法。它重复访问要排序的数列,每一次比较两个元素,如果前一个大于后一个元素,则交换数据。那么在一次全部访问过程中,最大的元素就’浮’动到数列的最后。然后重复进行方法,知道再没有数据交换,也就是数列已经排序完成。

步骤

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

实现

void BubbleSort(int arr[], int n) {
    int i, j;
    i = n;
    bool flag = true;
    while (flag) {
        flag = false;
        for (j = 1; j < i; j++) {
            if (arr[j-1] > arr[j]) {
                swap(arr[j-1], arr[j]);
                flag = true;
            }
        }
        i--;
    }
}

在上面的代码中加入了一个flag来标记是否有数据交换,如果在排序过程中没发生数据交换,则表示已经排列好了,后面就不需要在遍历了。

冒泡排序算是最简单的排序算法了,但毕竟是一种效率低下的排序算法,再数据量不大的情况下可以使用。

插入排序(Insertion sort)

原理

插入排序是一种直观的排序算法。它通过构建有序数列,对未排序的数据,在已排序的数列中从后往前扫描,找到相应的位置插入。在排序的实现上,从后向前的扫描过程中,需要反复把已排序的元素逐步向后移动,为要插入的元素留空间。

步骤

  1. 从第一个元素开始,该元素可以认为已经被排序
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
  5. 将新元素插入到该位置后
  6. 重复步骤2~5

实现

void InsertSort(int arr[], int n) {
    int i, j;
    for (i = 1;  i < n; i++) {
        if (arr[i] < arr[i-1]) {
            int temp = arr[i];
            for (j = i - 1; j >= 0 && arr[j] > temp; j--) {
                arr[j+1] = arr[j];
            }
            arr[j+1] = temp;
        }
    }
}

插入排序不适合对于数据了比较大的排序应用。但是,如果排序数据了很小,比如一千左右,那插入排序是一个不错的选择。

选择排序(Selection sort)

原理

选择排序与插入排序很像,插入排序是将一个数插入已经排好的序列,而选择排序是在未排序的序列中找到最小(大)元素,放在排序序列的起始位置。然后,再从剩下的未排序的序列中再次寻找最小(大)元素,放在已排序序列的末尾,反复重复,知道所有元素排序完成。

步骤

  1. 初始时,数组全为无序区为a[0..n-1]。令i=0
  2. 在无序区a[i…n-1]中选取一个最小的元素,将其与a[i]交换。交换之后a[0…i]就形成了一个有序区。
  3. i++并重复第二步直到i==n-1。排序完成。

实现

void SelectSort(int arr[], int n){
    int i, j, minIndex;
    for (i = 0; i < n; i++) {
        minIndex = i;
        for (j = i+1; j < n; j++) {
            if (arr[minIndex] > arr[j]) {
                minIndex = j;
            }
        }
        if (minIndex != i) {
            Swap(arr[i],arr[minIndex]);
        }
    }
}

选择排序中,如果某个元素位于正确的最终位置,则不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移动到最终位置上,因此对n个元素的表进行排序总共进行至多n-1次交换。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。

希尔排序(Shell sort)

原理

希尔排序,也称作递减增量排序算法,是插入排序的一种更高效版本。它以一定的增量将序列分为若干个组,然后每一组进行插入排序,然后减少增量反复分组进行插入排序。直到增量为1时,就为普通的插入排序,但是此时序列已经基本排列完成,只需要进行少量的移动即可完成。

步骤

将数组列在一个表中并对列排序(用插入排序)。重复这过程,不过每次用更长的列来进行。最后整个表就只有一列了。将数组转换至表是为了更好地理解这算法,算法本身仅仅对原数组进行排序(通过增加索引的步长,例如是用i += step_size而不是i++)

例如,假设有这样一组数[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ],如果我们以步长为5开始进行排序,我们可以通过将这列表放在有5列的表中来更好地描述算法,这样他们就应该看起来是这样:

13 14 94 33 82
25 59 94 65 23
45 27 73 25 39
10

然后我们对每列进行排序:

10 14 73 25 23
13 27 94 33 39
25 59 94 65 82
45

将上述四行数字,依序接在一起时我们得到:[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ].这时10已经移至正确位置了,然后再以3为步长进行排序:

10 14 73
25 23 13
27 94 33
39 25 59
94 65 82
45

排序之后变为:

10 14 13
25 23 33
27 25 59
39 65 73
45 94 82
94

最后以1步长进行排序(此时就是简单的插入排序了)。

实现

void ShellSort(int arr[], int n) {
    int i, j, gep;
    for (gep = n/2; gep > 0; gep /= 2) {
        for (i = gep; i < n; i++) {
            int temp = arr[i];
            for (j = i - gep; j >= 0 && arr[j] > temp; j-=gep) {
                arr[j+gep] = arr[j];
            }
            arr[j+gep] = temp;
        }
    }
}

希尔排序步长的选择十分灵活,只要最终步场为1的任何步长序列都可以工作。以上的代码从n/2开始,每一次减半,最终步长为1,算法变为插入排序,就保证了数据一定会被排序。

归并排序(Merge sort)

原理

归并排序是创建在归并操作上的一种有效的排序算法,基本思想是分治法。它时将两个已经排序的序列合并成一个序列的操作。

步骤

归并操作

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  4. 重复步骤3直到某一指针到达序列尾
  5. 将另一序列剩下的所有元素直接复制到合并序列尾

归并排序

  1. 将序列每相邻两个数字进行归并操作,形成floor(n/2)个序列,排序后每个序列包含两个元素
  2. 将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素
  3. 重复步骤2,直到所有元素排序完毕

实现

//归并操作
void _merge_array(int arr[], int first, int mid, int last, int temp[]) {
    int i = first, j = mid + 1;
    int m = mid, n = last;
    int k = 0;
    while (i <= m && j <= n) {
        if (arr[i] < arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }
    
    while (i <= m) {
        temp[k++] = arr[i++];
    }
    
    while (j <= n) {
        temp[k++] = arr[j++];
    }
    
    for (i = 0; i < k; i++) {
        arr[first + i] = temp[i];
    }
}

//归并排序
void _merge_sorte(int arr[], int first, int last, int temp[]) {
    if (first < last) {
        int mid = (first + last) / 2;
        _merge_sorte(arr, first, mid, temp);
        _merge_sorte(arr, mid+1, last, temp);
        _merge_array(arr, first, mid, last, temp);
    }
}

void MergeSort(int arr[], int n) {
    int *p = (int *)malloc(sizeof(int) * n);
    if (p != nullptr) {
        _merge_sorte(arr, 0, n-1, p);
    }
    free(p);
}

归并排序是分治法的典型应用,当一个数组的左右两边都有序然后归并整个数组就有序了,利用递归逐层分治,然后合并上来就排好序了。

快速排序(Quick sort)

快速排序算是我最喜欢的一个排序了,记得第一次接触的时候惊讶排序还可以这么排…题外话了

原理

快速排序也适用分治法,已一个数作为基准,左边全为笔它小的数,右边全为比它大的数。然后左右递归重复即可。

步骤

  1. 从数列中挑出一个元素,称为”基准”(pivot)
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

实现

下面列两种实现一种MoreWindows大神已挖坑填数总结的,一种是算法导论上的实现。

MoreWindows:

void QuickSort(int arr[], int l, int r) { 
    if (l < r) {
        int i = l, j = r;
        int k = arr[i];
        
        while (i < j) {
            while (i<j && arr[j] > k) {
                j--;
            }
            if (i<j) {
                arr[i++] = arr[j];
            }
            
            while (i<j && arr[i] <= k) {
                i++;
            }
            if (i<j) {
                arr[j--] = arr[i];
            }
        }
        arr[i] = k;
        
        QuickSort(arr, l, i-1);
        QuickSort(arr, i+1, r);
    }
}

算法导论 :

int quick_sort(int arr[], int left, int right) {
    int index = left;
    int k = arr[index];
    
    Swap(arr[index],arr[right]);
    for (int i = left; i < right; i++) {
        if (arr[i] < k) {
            Swap(arr[index++], arr[i]);
        }
    }
    Swap(arr[index], arr[right]);
    return index;
}

void QuickSort(int arr[], int left, int right) {
    if (left < right) {
        int index = quick_sort(arr, left, right);
        QuickSort1(arr, left, index-1);
        QuickSort1(arr, index+1, right);
    }
}

快速排序递归下去的最低情形,是数列的大小0或1,也就是永远排好了序。在每一次递归中至少有一个数会摆到它最后的位置。

堆排序(Heap sort)

堆排序对于我而言算是比较难搞懂的排序了

原理

堆排序是利用堆这种数据结构所设计的一种排序算法。最大(小)堆的根节点 为整个序列的最大(小)数。堆每取出一个根节点,堆被破坏,然后堆就会调整使之符合最大(小)堆。那么依次取出堆的根节点,依次放入新的数列中,直到堆元素为0位置,那么新的数列已经排好序了。

步骤

堆节点的访问:

通常堆是通过一维数组来实现的。在起始数组为0的情形中:

  • 父节点i的左子节点在位置(2*i+1);
  • 父节点i的右子节点在位置(2*i+2);
  • 子节点i的父节点在位置floor((i-1)/2);

堆的操作:

在堆的数据结构中,堆中的最大值总是位于根节点。堆中定义以下几种操作:

  • 最大堆调整(Max_Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
  • 创建最大堆(Build_Max_Heap):将堆所有数据重新排序
  • 堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算

原地堆排序:

  1. 创建一个堆H[0..n-1]
  2. 把堆首(最大值)和堆尾互换
  3. 把堆的尺寸缩小1,并调用MaxPrcdown(),目的是把新的数组顶端数据调整到相应位置
  4. 重复步骤2,直到堆的尺寸为1

实现

void MaxPrcdown(int arr[], int i, int n) {
    int temp = arr[i];
    int chlid = 2 * i + 1;
    while (chlid < n) {
        if (chlid + 1 < n && arr[chlid] < arr[chlid+1]) {
            chlid++;
        }
        
        if (arr[chlid] <= temp) {
            break;
        }
        
        arr[i] = arr[chlid];
        i = chlid;
        chlid = 2 * i + 1;
    }
    arr[i] = temp;
}

void HeapSort(int arr[], int n) {
    for (int i = n / 2 - 1; i>=0; i--) {
        MaxPrcdown(arr, i, n);
    }
    for (int i = n - 1; i >= 1; i--) {
        Swap(arr[0], arr[i]);
        MaxPrcdown(arr, 0, i);
    }
}

总结

 

排序算法 最差时间复杂度 平均时间复杂度 空间复杂度 稳定性
冒泡排序 O(n^2) O(n^2) O(1) 稳定
插入排序 O(n^2) O(n^2) O(1) 稳定
选择排序 O(n^2) O(n^2) O(1) 稳定
归并排序 O(nlogn) O(nlogn) O(n) 不一定
希尔排序 O O O(1) 不稳定
快速排序 O(n^2) O(nlogn) O(logn)~O(n) 不稳定
堆排序 O(n*log2n) O(n*log2n) O(1) 不稳定

 

参考

谷歌新Logo如何做到只有305字节「轉」

google-logo-01

谷歌新旧Logo

谷歌换logo已经有一段时间了,对于更换Logo的问题,大家讨论的最多的是到底新老Logo哪个更好看。

但也有个别同学注意到了一个事实:谷歌的新Logo只有305字节那么大,而老的Logo则有14000字节。

咳咳,按照谷歌的网络流量,单单一个Logo就能为整个互联网省下不少带宽啊。新Logo为什么就这么小呢?

谷歌的旧Logo使用了serif字体,而这类字体只能通过贝塞尔曲线来创建。如下图所示,从谷歌的Logo可以看到,旧Logo有多达100个锚点,因此最终产生的的Logo文件达到约6Kb(6380 字节)大小也不足为奇。进行压缩以后,Logo的大小大约 2 KB (2145字节)大小。

google-logo-02

谷歌旧Logo

谷歌新版的Logo在进行了大量简化,除了小写的字母g以外,其他字母均可以由圆形和矩形构造出来。如下图所示:

google-logo-03

谷歌新Logo

整个新Logo由如下部分组成:

  1. 10个圆形。(大写字母G占2个圆,小写字母g占2个圆,字母e占两个圆,每个字母O占2个圆)。
  2. 5个矩形。(大写字母G占2个矩形,小写字母g占1个矩形,字母e占两个矩形)。
  3. 一个由7个锚点组成的图形(小写字母g的下半部分)。

我创建了大写字母G的SVG矢量图,生成的文件大小是302字节,压缩后只有195字节。如下是未压缩的图形代码(由两个矩形和两个圆构成)。

上面的代码生成的图形如下图右边的G所示。

google-logo-04

当然,还有另外一种绘制方式。我们可以使用strokes方式而不是fills方式来绘制Logo,这种方式最终产生的的文件更小,只有290字节。代码如下所示:

使用strokes的绘制方式,整个Logo只需要两个圆(两个字母O),四条路径(分别对应字母G、 g、 l,和e)即可绘制完成。如下图所示:

google-logo-05

 

出处:Quora   译文:code123

本文链接:http://www.code123.cc/1723.html (转载请保留出处链接)

文件操作中的几个大坑 「轉」

 

在程序开发中你经常会碰到很多关于路径的问题的。比如读取文件,写文件等,加载一张图片,读取一个配制,你必要传一个文件的路径。然而你也许常常碰到这样的问题:逻辑是对的,但就出不来你想要的结果。这是为什么呢?因为对于文件操作的路径问题,有很多你稍不慎就会掉入的大坑。
这里写图片描述

说几个掉进大坑的惨痛经历

1.编译一个C++的工程,工程中要用到cmake工具,而cmake安装在C:\Program Files (x86)\cmake2.8(程序默认安装时,安装到这个目录是很正常不是吗)。然而问题来了,工程怎么编不过,报一个很诡异的异常:

Microsoft.CppCommon targets(151, 5):error MSB6006:”cmd.exe” exited with code 3.

后来我把cmake安装目录的整个文件夹拷到了C:\Program Files\cmake2.8,工程编译正常。但就因为这一个问题,半天的时间一下就没了……
2.由于工程配制的需要,要写一个脚本,脚本的功能很简单,就把一个文件从A目录拷贝(复制)到B目录。于是很自然地就写了这样一行命令:

copy /y D:\C++Workspace\ProjectA\config.txt D:\C++Workspace\ProjectB\

很简单吧,没有逻辑错误吧!但结果呢?结果这样的:
 这里写图片描述
我瞅了一眼又一眼,config.txt这个文件确实是存在的,而且路径也是正确的。然后我进行了一个尝试:
这里写图片描述
然后我直接把config.txt复制一份到D:\config.txt,再进行了一次尝试:
Ok,它成功了!作为一个C++程序员,取一个“C++Workspace“这样文件夹作为工作目录也是可以理解的吧!但结果却是花了我半个多小时解决上面这个问题。于是我不得不把”C++Workspace“改成了”CppWorkspace“,然后写这样一行命令:

copy /y D:\CppWorkspace\ProjectA\config.txt D:\CppWorkspace\ProjectB\

这里写图片描述
Ok,一切正常!
3.还有的还有,我就不一一举例了,总之关于路径问题,坑很多……

如何跳过大坑?

那如何避免掉进大坑呢?大体总结以下几点,可依次循着这个思路逐步排查:

1. 检查写的路径是否正确;
2. 检查要操作的目录、文件是否具有权限,如读写权限(Linux系统或类Unix系统中还有执行权限);
3. 检查路径中是否包含特殊的字符,如’(‘、’)’、’<’、’>’、’ ‘、’+’、’-‘、’:’、’%’、’&’、’#’、’$’、’!’、’|’、’*’、’@’、’?’、’,’等;
4. 注意绝对路径与相对路径的问题。当你怎么也搞不定一个问题,并且逻辑也没有错误时,就不要闷头苦干了!按照上面的思路检查一下是不是路径的问题吧。

源碼與設計「原」

推薦幾款工具
  • 1,openscad

CAD工具,官方首頁:http://www.openscad.org/

  • 2,kicad

電路設計軟件,官方首頁:http://kicad-pcb.org/

  • 3,frescobaldi

打譜軟件,官方首頁:http://www.frescobaldi.org/

他們共同的特徵
  • 1,開源軟件
  • 2,window,linux等多平台下可使用
  • 3,都是設計類軟件
  • 4,輸出為文本文件形式

其中以最後兩點需要注意

採用文本形式的設計文件符合程序員的開發習慣,可以視其為一門語言來進行開發,並將很多程序開發的方法思想應用到其中,例如版本管理,例如定製編輯器提高編碼開發效率,甚至將一些程序開發思想融入到裡面。

文本文件是可以被直接讀取的,相對來說,人更能直觀容易的理解其內容,為其開發插件工具,或者進行移植等工作更加簡單可行。

這裡其實是一種一切設計皆用源碼表現的思想

沿着這個思路,我們可以尋找其他類似的工具,嘗試感受一下來

 

WordPress离线客户端编辑器大全【轉】

作为一个WPer,我们应该对WordPress的TinyMCE编辑器非常的熟悉,尽管它经常被人吐槽像狗屎,但是有CKEditor For WordPressTinyMCE Advanced来擦屁股。然而在线编辑器却有着其固有的缺陷:一旦在网络瘫痪的时候点击了“发布”,一切都会功亏一篑;对文章的编辑效率和效率远没有PC客户端高,有时还得粘贴到Emeditor等编辑器内处理。于是乎离线的客户端编辑器得到了我的青睐,在此分把那些我整理的WP离线编辑器分享出来。

 

跨平台的离线客户端编辑器

Qumana

Qumana横跨MacLinuxWindows三大平台,虽然首页没有列出Linux版本,但是下载页却是有的。Qumana支持WordPress, MovableType, Live Spaces, LiveJournal, Drupal, TypePad, Tripod, Blogger, Squarespace等诸多平台。Qumana的功能特色:方便插入自己广告到文章(这个功能很独特)、插入多媒体、将文章发布多个博客、拼写检查、ping功能和插入Tag。

但是目前官网只有首页,其它页面统一无法访问,都下载不到,肿么了?被黑了?

WordPress离线客户端编辑器大全

价格:免费

Bleezer

Bleezer同样横跨Windows, Mac以及Linux系统,包含拼写检查、插入多媒体、ping和插入Tag功能,但是不支持多博客发布。上传多媒体文件需要FTP设置与支持。

可惜作者已经五年没有更新了,下载包也无法正常解压缩,试用都没法试用了。

WordPress离线客户端编辑器大全

价格:免费

Thingamablog

Thingamablog也是横跨三大平台的免费离线编辑器,使用Java语言编写,因此需要Java 1.4.2以上版本后方可运行。Thingamablog支持拼写检查、ping、多博客发布,支持通过email远程发布,还有内置RSS阅读器以及设置代理发布文章功能非常值得称赞。但是它不支持插入多媒体和Tag,编辑器也确实挺简陋的。使用Thingamablog后才知道它是使用模板生成HTML静态页面,然后通过FTP或SFTP上传,或者直接保存到本地,因此Thingamablog是不需要支持cgi/php的主机,或者MySQL等数据库的。

Thingamablog的弊端显而易见,它生成的是静态网页,当然不适用于WordPress类动态内容的博客,后期文章的更新和维护肯定也会存在不少的问题。

WordPress离线客户端编辑器大全

价格:免费

Windows离线客户端编辑器

Zoundry Raven

Zoundry Raven已经有很多网友介绍过,是一款非常棒的离线编辑器,而且算是用过的唯一一款包含简体中文语言的。它支持WordPress, Blogger, TypePad, Movable Type, Live Spaces, LiveJournal等平台,支持多媒体文件插入、支持拼写检查、ping以及多博客发布和管理。写好文章好,还可以使用博客模板预览你将要发布的文章。

Zoundry Raven可以为不同的博客设置不同的多媒体库,如Picasa、Image Shack、Ripway、自己设置的FTP服务器等,多博客可以共享同一个媒体库,也可以单独使用自己的媒体库,方便地管理多个博客。安装Raven时可以选择便携安装方式,这样就可以将其放在U盘等移动设备上,随身携带。

在本人看来,目前Zoundry Raven主要的缺陷是不支持WordPress自定义字段,以及不支持获取原有的Tag并插入的功能。作者鉴于自己没有时间去维护,于是决定开源啦!现在有技术的童鞋可以在Zoundry Raven的开源页面,和作者一道完善这款非常棒但仍还不完善的编辑器。

WordPress离线客户端编辑器大全

价格:免费

Windows Live Writer

微软的东东,大家很熟悉,也是用的最多的。支持的博客平台: Windows Live, WordPress, Blogger, MovableType, LiveJournal, Live Journal, TypePad等等。在Windows Live Writer中编辑文章、文字格式、插入图片都是很轻松的事情,它具备拼写检查、多媒体插入、插入Tag、ping功功能,另外图片编辑、插入Bing地图以及主题预览也是难得一见的,通过一定的设置还可以支持WordPress特色图像,强大极了!

WordPress离线客户端编辑器大全

还嫌其功能不够强大,可以添加免费的Windows Live Writer插件。不会使用Windows Live Writer,可以学习学习Windows Live Writer教程

价格:免费

BlogDesk

BlogDesk是一款简单而实用的编辑器,它支持发布文章到WordPress、MovableType、Drupal、Serendipity和ExpressionEngine。BlogDesk支持多媒体插入、拼写检查、多博客发布、插入图片并对其进行裁剪、旋转、阴影效果处理等,甚至可以生成图片的缩略图,但不支持Ping功能。

不得不说的是BlogDesk支持wordpress自定义字段,可以为文章设置密码访问,还可以插入常用短语(类似于手机短信模板功能),当插入和编辑MP3和PDF文件链接时,BlogDesk会自动将它们上传到服务器中。更强的是Tags-Generator(Tags生成器),使用它可以非常方便的插入预先定义的Tag,而且支持WordPress Tags的哦。不会使用的朋友,请前往BlogDesk教程

WordPress离线客户端编辑器大全

价格:免费

Blog Jet

Blog Jet号称自己是最好的Windows离线编辑器,支持WordPress、MovableType、TypePad、Drupal、Blogger以及Live Spaces等主流博客平台,支持草稿、页面、添加图片、文件附件等关键的功能,拼写检查、多博客发布、ping、插入Tag(可以刷新wordpress现有的Tag,然后选择插入)、集成 YouTube和Flickr服务(可惜天朝都是摆设),支持从iTunes和Windows Media Player插入多媒体,最新版已支持wordpress自定义字段了。

WordPress离线客户端编辑器大全

价格:$39.95每用户,不限博客数量,30天试用期

Post2Blog

Post2Blog支持WordPress、TypePad、MovableType和Blogger博客,具备拼写检查和多媒体插入功能,支持ping和插入Tags,而且还提供Firefox和IE插件、MS Word工具栏,使得撰写文章变得更加的方便,难得功能吧。但是Post2Blog不支持多博客发布。官网当前无法访问,所以我也无法试用这款编辑器。

价格:免费

w.bloggar

w.bloggar是个免费的离线编辑器,支持WordPress、MovableType、TypePad、Blogger、Live Spaces等等,支持拼写检查、多媒体插入、多博客发布,不支持Ping、插入Tag功能。现在w.bloggar已经停止开发,连发布的页面都没有了,请前往CNET下载

Mac离线客户端编辑器

Blogo

Blogo支持WordPress, Drupal, Expression Engine以及许多微博服务,支持ping和多博客发布。

WordPress离线客户端编辑器大全

价格:$25

MarsEdit

MarsEdit含多种文章编辑器,支持多媒体插入。支持WordPress, Blogger, TypePad, Movable Type, LiveJournal, Drupal, Vox, Tumblr等平台. MarsEdit 2 has an integrated 拼写检查和多媒体插入功能、Ping、Tags,不支持多博客发布。

价格:$39.95,30天免费试用

WordPress离线客户端编辑器大全

ecto

ecto能够Mac、iPhoto、iTunes和Address Book之间同步使用数据,支持WordPress, MovableType, TypePad, Blogger, Squarespace等等平台,具备拼写检查、多媒体插入、ping、trackback和插入Tag功能,不支持多博客发布。

价格:$19.95,免费试用21天

Linux离线客户端编辑器

Blogilo

Blogilo是开源的博客离线编辑器,具备基础的功能,但是关键是这都是完全免费的。

WordPress离线客户端编辑器大全

Lekhonee

WordPress离线客户端编辑器大全

QTM

QTM使用QT作为GUI, 同样被WordPress作为Nokia App的GUI。QTM界面简洁,看似没有多少的按钮,但是点击左侧下来菜单,会有许多的高级功能。

WordPress离线客户端编辑器大全

将Vim改造为强大的IDE—Vim集成Ctags/Taglist/Cscope/Winmanager/NERDTree/OmniCppComplete(有图有真相) 【轉】

工欲善其事,必先利其器。一个强大的开发环境可以大大提高工作效率。好吧,我知道这是废话。。。不过,我想一定有很多跟我一样打算进入Linux平台开发 的新手,一开始都为找不到一个像Windows下的VS那样可以一键安装并且功能几乎完美无缺的开发工具而郁闷不已,甚至打算收回刚刚迈出的脚步。所幸的 是,通过几天努力,我总算配置出了一个功能完备的基于Vim的开发环境。这个开发环境除了基本的Vim外,还包括Ctags,Taglist,Cscope,SuperTab,OmniCppComplete,Winmanager,NERDTree和 MiniBufExplorer等组件。

在开始操作前,先普及下基础概念,然后约定一下表达规范。
1)Vim存在多个配置文件vimrc,比如/etc/vimrc,此文件影响整个系统的Vim。还有~/.vimrc,此文件只影响本用户的Vim。而且~/.vimrc文件中的配置会覆盖/etc/vimrc中的配置。这里我们只修改~/.vimrc文件。
2)Vim的插件(plugin)安装在Vim的runtimepath目录下,你可以在Vim命令行下运行”set rtp“命令查看。这里我们选择安装在~/.vim目录,没有就创建一个。
3)当本文说”在Vim命令行下运行cmdxx命令“时,意思是指在Vim的命令行模式下运行cmdxx命令,即在Vim的正常模式下通过输入冒号”:”进入命令行模式,然后紧接着输入命令cmdxx。在后文描述中都会省略冒号”:”输入。
4)如果没有说明“在Vim命令行下运行某命令”,则是在shell中执行该命令。
5)如果命令中间被空白符间隔或有与正文容易混淆的字符,我会用双引号将命令与正文区分。所以读者在实际操作时,不要输入命令最前面和最后面引号。
6)本文关于组合快捷键的描述,形如a-b形式的快捷键表示同时按下a键和b键,而形如”a-b c”形式的快捷键,则表示先同时按下a键和b键,然后放开ab键,再按下c键。

 
1,安装使用Ctags
Ctags工具是用来遍历源代码文件生成tags文件,这些tags文件能被编辑器或其它工具用来快速查找定位源代码中的符号(tag/symbol), 如变量名,函数名等。比如,tags文件就是Taglist和OmniCppComplete工作的基础。这里介绍从源代码包安装,安装步骤跟大多数软件的从源代码安装步骤一样。

1)从http://ctags.sourceforge.net/下载源代码包后,解压缩生成源代码目录
2)然后进入源代码根目录执行./configure
3)然后执行make
4)编译成功后执行make install
5)在~/.vimrc中增加以下这行:
map <C-F12> :!ctags -R –c++-kinds=+p –fields=+iaS –extra=+q .<CR>

到此,Ctags已安装成功。使用Ctags的也很简单。 进入我们的项目代码根目录,执行以下命令:

ctags -R –c++-kinds=+p –fields=+iaS –extra=+q

另外,由于在前面第5条,我们已经在Vim中配置了Ctrl-F12组合快捷键,所以我们也可以进入代码根目录后,打开Vim,按下Ctrl-F12快捷键自动生成tags文件。命令执行完后,会在源代码目录生成tags文件。Vim默认会自动读取当前目录下的tags文件,所以不需要修改~/.vimrc文件。此时,我们已经具有定义跳转的功能了。有两组快捷键是最常用的。

Ctrl-]    跳转到光标所在符号的定义。
Ctrl-t    回到上次跳转前的位置。
更多功能通过命令man ctags或在Vim命令行下运行help ctags查询。

 
2,安装使用Taglist
Taglist是vim的一个插件,提供源代码符号的结构化视图。
2)进入~/.vim目录,将Taglist安装包解压,解压后会在~/.vim目录中生成几个新子目录,如plugin和doc(安装其它插件时,可能还会新建autoload等其它目录)。
3)进入~/.vim/doc目录,在Vim下运行”helptags .”命令。此步骤是将doc下的帮助文档加入到Vim的帮助主题中,这样我们就可以通过在Vim中运行“help taglist.txt”查看taglist帮助。
4)打开配置文件~/.vimrc,加入以下两行:

let Tlist_Show_One_File=1
let Tlist_Exit_OnlyWindow=1
到此安装已经完成。在Vim命令行下运行TlistToggle命令就可以打开Taglist窗口,再次运行TlistToggle则关闭。示图如下:
我们可以通过Ctrl-w快捷键或鼠标点击在Taglist窗口和编辑区之间切换焦点,在Taglist窗口用鼠标或键盘选择某个符号,然后点击或回车,就可以跳转到该符号定义的位置。更多功能可通过在Vim命令行下运行help taglist.txt查询。
3,安装使用Cscope
Cscope提供交互式查询语言符号功能,如查询哪些地方使用某个变量或调用某个函数。Cscope已经是Vim的标准特性,默认都有支持,官方网址为http://cscope.sourceforge.net/
1)在Vim下运行version查看Vim支持哪些特性,前面有前缀符号+的为支持。如果支持Cscope,则直接进入2),否则下载Cscope源代码包编译安装。步骤同Ctags安装。
2)确定Vim已支持Cscope后,将文件http://cscope.sourceforge.net/cscope_maps.vim下载到~/.vim/plugin目录。到这里,我们就可以开始使用Cscope了。
1)使用Cscope需要生成cscope数据库文件。进入项目代码根目录运行命令:
cscope -Rbq -f path/xxx.out
命令运行后会生成xxx.out文件,即cscope数据库文件。更多用法参考man cscope文档。
2)进入项目代码根目录,在Vim下运行命令:
cs add path/xxx.out
此命令将cscope数据库载入Vim。
3)Cscope常用快捷键Ctrl-\ s 查找所有当前光标所在符号出现过位置。Ctrl-\ c 查找所有调用当前光标所在函数的函数。按下快捷键查找结束后会在编辑区下方出现查找结果的列表,输入结果编号并回车,就能跳转到该查找结果在源代码中的相应位置。例如,我们将光标移到 initial_pool_size变量定义的位置,即17行,然后按下”Ctrl-\ s”组合快捷键,得到示图如下:
然后我们输入2,并回车,就能跳转到第2个查找结果。为了界面更好看,可以把Cscope的查找结果输出到quickfix窗口,需要在~/.vimrc中加入下面这行:
set cscopequickfix=s-,c-,d-,i-,t-,e-
这样,通过快捷键查找某个符号后,会立即跳转到第一个找到的该符号出现的位置。如果你对这次默认跳转的位置不满意,在Vim命令行下运行cw命令,就能在 编辑区下面quickfix窗口看到所有查找结果的列表,点击相应列表项就能跳转到相应位置。这个功能已经跟VS很接近了吧:)
更多功能可通过命令man cscope或在Vim命令行下运行help cscope查询。
 
4,安装使用OmniCppComplete
OmniCppComplete主要提供输入时实时提供类或结构体的属性或方法的提示和补全。跟Talist一样,OmniCppComplete也是一个Vim插件,同样依赖与Ctags工具生成的tags文件。安装步骤跟Taglist类似。从http://www.vim.org/scripts/script.php?script_id=1520下载安装包后。
1)进入~/.vim目录,将安装版解压缩
2)进入~/.vim/doc目录,在Vim命令行下运行”helptags .”
3)在~/.vimrc中加入以下几行:
set nocp
filetype plugin on
OmniCppComplete的使用几乎跟VS下的VA一样。如下图所示,输入m0.之后立即弹出my_class类中所有的函数列表,然后用上下键选择合适的函数。 
更多功能通过在Vim命令行下运行”help omnicppcomplete”查询。
5,安装使用SuperTab
SuperTab使Tab快捷键具有更快捷的上下文提示功能。跟OmniCppComplete一样,SuperTab也是一个Vim插件。从http://www.vim.org/scripts/script.php?script_id=1643下载安装版。这个安装包跟先前的几个Vim插件不同,它是一个vba文件,即Vimball格式的安装包,这种格式安装包提供傻瓜式的安装插件的方法。
1)用Vim打开.vba安装包文件。
2)在Vim命令行下运行命令“UseVimball ~/.vim”。此命令将安装包解压缩到~/.vim目录。VImball安装方式的便利之处在于你可以在任何目录打开.vba包安装,而不用切换到安装 目的地目录。而且不用运行helptags命令安装帮助文档。
3)在~/.vimrc文件中加入以下这行:
let g:SuperTabDefaultCompletionType=”context”
SuperTab使用很简单,只要在输入变量名或路径名等符号中途按Tab键,就能得到以前输入过的符号列表,并通过Tab键循环选择。 
 
6,安装使用Winmanager,NERDTree和MiniBufExplorer
前面介绍的几个工具和插件,主要提供快捷的编辑功能,如定义跳转,符号查询,符号提示与补全等。这里的三个插件,主要优化布置VIm的界面。具体来 说,NERDTree提供树形浏览文件系统的界面,MiniBufExplorer提供多文件同时编辑功能,而Winmanager将这NERDTree 界面和Taglist界面整合起来,使Vim更像VS!分别从http://www.vim.org/scripts/script.php?script_id=1658http://www.vim.org/scripts/script.php?script_id=159http://www.vim.org/scripts/script.php?script_id=95下载NERDTree,MiniBufExplorer和Winmanager安装包(Winmanager还有个更新的vba版本http://www.vim.org/scripts/script.php?script_id=1440,这里选用旧版本的Winmanger)。
1)像其它插件一样,将NERDTree安装包解压到~/.vim目录。并进入doc目录,在Vim命令行下运行”helptags .”命令。
2)MiniBufExplorer只有一个.vim文件,将其拷贝到~/.vim/plugin目录。
3)在~/.vimrc文件中加入以下几行:
let g:miniBufExplMapWindowNavVim = 1
let g:miniBufExplMapWindowNavArrows = 1
let g:miniBufExplMapCTabSwitchBufs = 1
let g:miniBufExplModSelTarget = 1
let g:miniBufExplMoreThanOne=0

4)将Winmanager安装包解压到~/.vim目录。

5)在~/.vimrc文件中加入以下几行:

let g:NERDTree_title=”[NERDTree]”
let g:winManagerWindowLayout=”NERDTree|TagList”
function! NERDTree_Start()
    exec ‘NERDTree’
endfunction
function! NERDTree_IsValid()
    return 1
endfunction
nmap wm :WMToggle<CR>

6)这个版本的Winmanager好像有个小bug,你在打开Winmanager界面时,会同时打开一个空的文件。这会影响后续使用,所以我们要在打 开Winmanager时关掉这个空文件。在~/.vim/plugin目录下的winmanager.vim文件中找到以下函数定义并在第5行下添加第 6行的内容:

function! <SID>ToggleWindowsManager()
   if IsWinManagerVisible()
      call s:CloseWindowsManager()
   else
      call s:StartWindowsManager()
      exe ‘q’
   end
endfunction
到这里,就大功告成了!现在进入我们的项目目录,打开Vim,按下组合快捷键w-m就可以我们的崭新的Vim了!再次按下w-m就可关闭界面。示图如下:
界面最上面的一条窄边就是MiniBufExplorer,可以看到我打开了两个文件cache.c和assoc.c,是不是很像VS的标签?紧靠MiniBufExplorer下方左边的矩形区域就是NERDTree。在这个窗口,我们可以用鼠标或键盘方便的浏览整个文件系统,在某个文件上点击或回车,就可以在右边编辑区域打开该文件。NERDTree下方的就是前面安装的Taglist界面。
7,其它有用的~/.vimrc设置
设置配色方案,我用的是eveing方案,配色方案保存在/usr/share/vim/vimXY/colors
colo evening
显示行数
set nu
与自动缩进相关的选项
set autoindent
set tabstop=4
set shiftwidth=4
set mouse=a

linux下搭建属于自己的博客(WordPress安装)

WordPress简介

WordPress 是一种使用 PHP语言和 MySQL数据库开发的开源、免费的Blog博客网志)引擎,用户可以在支持 PHP 和 MySQL 数据库的服务器上建立自己的 Blog。WordPress 是一个功能非常强大的博客系统,插件众多,易于扩充功能。安装和使用都非常方便。目前 WordPress 已经成为主流的 Blog 搭建平台。

现在我们开始搭建属于自己的blog:

1.首先,你需要下载wordpress(WordPress 是目前最为流行的 PHP Blog 程序,目标是美学、Web 标准和易用性的统一。它基于 GPL 许可协议,完全免费)。下载地址:

http://wordpress.org/download/

2.然后就是安装Wordpress必须的组件:

1)安装apache服务器:

sudo apt-get install apache2

安装后在浏览器中打开:http://localhost/或者http://127.0.0.1

如果出现It works!那证明OK了

2)安装php服务:

sudo apt-get install php5

测试:

打开gksudo gedit /var/www/testphp.php

然后随意输入点东西(我输入的是KH)再保存。

然后在浏览器中输入http://127.0.0.1/testphp.php或者http://localhost/testphp.php

如果显示出你输入的东西即为成功

3)重启apache服务器

sudo /etc/init.d/apache2 restart

此时浏览器就可以正确解析php文件了。

4)安装mysql服务:

sudo apt-get install mysql-server

sudo apt-get install mysql-admin

sudo apt-get install mysql-client

安装过程中提示输入数据库root用户的密码

3.为Wordpress新建mysql数据库:

在终端下打开mysql管理器:

$mysql -u root -p

创建新数据库:

mysql>CREATE DATABASE wordpress

4.解压wordpress的tar.gz压缩包

$sudo tar -zxvf wordpress-3.2.1.tar.gz

得到wordpress文件夹,然后按要求编辑wp-config.php文件,主要是提供数据库的名字(如这里的wordpress),用户名(如root),密码(如安装mysql时键入的密码)。

5.将wordpress文件夹拷贝到/var/www目录下:

sudo cp -a ./wordpress /var/www

此时在浏览器中访问http://localhost/wordpress/wp-admin/install.php,就会发现出现了上面所描述的乱码。我们首先利用phpMyAdmin来看一下这个乱码到底是什么问题

6.安装phpMyAdmin:

sudo apt-get install phpmyadmin

此时的phpmyadmin文件夹被安装在/usr/share/phpmyadmin下,为了能在浏览器中访问到phpmyadmin,需要在/var/www下做一个软连接到该文件夹:

进入/var/www文件夹,在该目录下执行如下操作:

sudo ln -s /usr/share/phpmyadmin

此时在浏览器中键入http://localhost/phpmyadmin,会发现出现了如下错误:

Cannot load mysql extension. Please check your PHP configuration.

这其实就是上面乱码所说的错误。

幸运的是,phpmyadmin同时给出了该问题的解决方案:

7.这样就很明确了,我们安装php-mysql包即可:

sudo apt-get install php-mdb2-driver-mysql

8.安装完毕后别忘了重启apache 和 mysql:

sudo /etc/init.d/apache2 restart

sudo /etc/init.d/mysql restart

然后便可以正常访问访问http://localhost/wordpress/wp-admin/install.php并安装wordpress了。