blogv3_html/feed.xml
2017-08-16 18:09:40 +08:00

327 lines
157 KiB
XML
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?xml version="1.0" encoding="UTF-8"?><rss version="2.0">
<channel>
<title>风笑痴</title>
<link>http://lunny.info/</link>
<description>风笑痴,一个脱离了低级懒惰的人</description>
<managingEditor>xiaolunwen@gmail.com (lunny)</managingEditor>
<pubDate>Wed, 16 Aug 2017 18:09:39 +0800</pubDate>
<item>
<title>Gop - 编译和管理在GOPATH之外的Go工程</title>
<link>http://lunny.info/2017/8/15/Gop---编译和管理在GOPATH之外的Go工程.html</link>
<description>&lt;h1 id=&#34;安装&#34;&gt;安装&lt;/h1&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;go get github.com/lunny/gop&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h1 id=&#34;起子&#34;&gt;起子&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;自开始使用Go进行开发之后工程一直都保存在GOPATH之下Go1.5支持 &lt;code&gt;vendor&lt;/code&gt; 机制之后开始使用 &lt;code&gt;govendor&lt;/code&gt; 来管理依赖项。其实一直都有需求要将 Go 的工程放在 GOPATH 之外,因为在一个大的项目中,各种语言写的内容放在一个 git 工程的子文件夹中,但一直没有很好的工具来解决依赖的问题。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;几个月之前,这个问题已经严重影响到工作了,终于不能忍受了。于是动手写了 &lt;code&gt;Gop&lt;/code&gt; 的首个版本最近又升级到了0.3版本。最新版本的 &lt;code&gt;Gop&lt;/code&gt; 工程目录结构如下:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;&amp;lt;project root&amp;gt;&#xA;├── gop.yml&#xA;├── bin&#xA;├── doc&#xA;└── src&#xA; ├── main&#xA; │ └── main.go&#xA; ├── models&#xA;  └── models.go&#xA; ├── routes&#xA; │ └── routes.go&#xA; └── vendor&#xA; └── github.com&#xA;    ├── go-xorm&#xA;    │   ├── builder&#xA;    │   ├── core&#xA;    │   └── xorm&#xA;    └── lunny&#xA; ├── log&#xA;    └── tango&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;通过以上的目录结构可以看到其实gop是兼容GOPATH的。只要把 &lt;code&gt;&amp;lt;project root&amp;gt;&lt;/code&gt; 设置到GOPATH中即使没有安装&lt;code&gt;gop&lt;/code&gt;命令,通过&lt;code&gt;go&lt;/code&gt;命令也可以编译,但是这时对 &lt;code&gt;vendor&lt;/code&gt; 管理也是不太方便。但如果使用 &lt;code&gt;gop&lt;/code&gt; 来管理项目,则问题迎刃而解。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;其中 &lt;code&gt;gop.yml&lt;/code&gt; 的结构如下:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yml&#34;&gt;targets:&#xA;- name: myproject1&#xA; dir: main&#xA; assets:&#xA; - templates&#xA; - public&#xA; - config.ini&#xA; - key.pem&#xA; - cert.pem&#xA;- name: myproject2&#xA; dir: web&#xA; assets:&#xA; - templates&#xA; - public&#xA; - config.ini&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;除了可以放到 &lt;code&gt;GOPATH&lt;/code&gt; 之外Gop 工程还具有以下特性:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;多编译目标支持&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;默认的编译目标是 &lt;code&gt;src/main&lt;/code&gt; 目录,编译名字是 &lt;code&gt;src&lt;/code&gt; 的父目录的名字,当然你也可以通过 &lt;code&gt;gop.yml&lt;/code&gt; 来自定义。如果输入&lt;code&gt;gop build&lt;/code&gt; 命令,默认会编译第一个编译目标。如果指定了编译目标,如 &lt;code&gt;gop build myproject2&lt;/code&gt; 那么将会编译指定的目标。默认编译好的可执行文件会放在 &lt;code&gt;src/main/&lt;/code&gt; 目录下,也可以通过 &lt;code&gt;gop build -o ./bin/my.exe&lt;/code&gt; 来进行指定。除了 &lt;code&gt;&amp;lt;target&amp;gt;&lt;/code&gt; 参数外,其它的参数和 &lt;code&gt;go build&lt;/code&gt; 的参数相同。&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;依赖管理&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;一般只需要执行 &lt;code&gt;gop ensure -g&lt;/code&gt; 即可将所有的依赖项全部下载且拷贝到 &lt;code&gt;vendor&lt;/code&gt; 下。如果依赖项需要更新,也可以使用 &lt;code&gt;gop ensure -u&lt;/code&gt; 将所有的依赖项更新到最新的版本。不再需要的依赖项,可以通过 &lt;code&gt;gop rm &amp;lt;package1&amp;gt; &amp;lt;package2&amp;gt;&lt;/code&gt; 删除,通过 &lt;code&gt;gop status&lt;/code&gt; 可以查看默认编译目标的依赖项的安装情况。&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;编译并运行某个目标&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;gop run&lt;/code&gt; 可以让你编译并运行某个目标,通过 &lt;code&gt;-w&lt;/code&gt;,在源码修改之后还可以自动重新编译并运行。&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;发布管理&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;&lt;code&gt;gop release&lt;/code&gt; 编译,打包资源并拷贝到 &lt;code&gt;bin&lt;/code&gt; 目录下。&lt;/p&gt;&#xA;</description>
<pubDate>Tue, 15 Aug 2017 22:04:00 +0000</pubDate>
</item>
<item>
<title>Go语言Web框架Tango中的中间件应用级别</title>
<link>http://lunny.info/2016/5/12/Go语言Web框架Tango中的中间件应用级别.html</link>
<description>&lt;p&gt;Tango在创建之初就支持了全局中间件的支持经过最近的改进同时支持了Group级别和Route级别的中间件。下面我们就一起来看下这三种级别的中间件&lt;/p&gt;&#xA;&#xA;&lt;p&gt;比如我们自定义了一个新的中间件:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;func MyMiddleware() tango.HandlerFunc {&#xA; return func(ctx *tango.Context) {&#xA; ctx.Info(&amp;quot;I&#39;m a middleware&amp;quot;)&#xA; ctx.Next()&#xA; }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h1 id=&#34;全局中间件&#34;&gt;全局中间件&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;全局中间件在任何一个请求调用时均会进行调用。用法如下:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;t := tango.Classic()&#xA;t.Use(MyMiddleware())&#xA;t.Get(&amp;quot;/&amp;quot;, func() string {return &amp;quot;global&amp;quot;})&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h1 id=&#34;group中间件&#34;&gt;Group中间件&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;Group中间件在Group下匹配的路由被调用时会被调用。用法如下&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;t := tango.Classic()&#xA;t.Group(&amp;quot;/group&amp;quot;, func(g *tango.Group) {&#xA; g.Use(MyMiddleware())&#xA; g.Get(&amp;quot;/&amp;quot;, func() string {return &amp;quot;group&amp;quot;})&#xA;})&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h1 id=&#34;route中间件&#34;&gt;Route中间件&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;Route中间件在该Route被调用时会被调用。如果有多个会按照先后顺序调用。用法如下&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;t := tango.Classic()&#xA;t.Get(&amp;quot;/route&amp;quot;, func() string {return &amp;quot;route&amp;quot;}, MyMiddleware())&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h1 id=&#34;中间件优先级&#34;&gt;中间件优先级&lt;/h1&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;全局中间件被先调用Group中间件次之最后是Route中间件&lt;/li&gt;&#xA;&lt;li&gt;相同级别的中间件,先加入的被先调用&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
<pubDate>Thu, 12 May 2016 14:32:00 +0000</pubDate>
</item>
<item>
<title>Go语言Web框架Tango在Session中间件中的设计</title>
<link>http://lunny.info/2016/2/18/Go语言Web框架Tango在Session中间件中的设计.html</link>
<description>&lt;p&gt;Tango在创建之初的目标就是既有Beego的效率又有martini的灵活中间件。因此Session也是在核心库 &lt;a href=&#34;https://github.com/lunny/tango&#34;&gt;tango&lt;/a&gt; 之外作为官方中间件存在,地址是 &lt;a href=&#34;https://github.com/tango-contrib/session&#34;&gt;tango-session&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Session因为有着灵活的设计因此可以完成很多我们希望的功能。首先看下Session中间件的选项&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type Options struct {&#xA; MaxAge time.Duration&#xA; SessionIdName string&#xA; Store Store&#xA; Generator IdGenerator&#xA; Tracker Tracker&#xA; OnSessionNew func(*Session)&#xA; OnSessionRelease func(*Session)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;其中我将要着重讲的是StoreGenerator和Tracker。&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;store&#34;&gt;Store&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Store是一个接口主要作用是存储Session中的内容其定义如下&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type Store interface {&#xA; Add(id Id) bool&#xA; Exist(id Id) bool&#xA; Clear(id Id) bool&#xA;&#xA; Get(id Id, key string) interface{}&#xA; Set(id Id, key string, value interface{}) error&#xA; Del(id Id, key string) bool&#xA;&#xA; SetMaxAge(maxAge time.Duration)&#xA; SetIdMaxAge(id Id, maxAge time.Duration)&#xA;&#xA; Run() error&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;默认的内核自带了&lt;code&gt;MemoryStore&lt;/code&gt;这将会把所有Session内容保存在内存中。同时官方中间件中也提供了&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://github.com/tango-contrib/session-nodb&#34;&gt;nodb&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://github.com/tango-contrib/session-redis&#34;&gt;redis&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://github.com/tango-contrib/session-ledis&#34;&gt;ledis&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://github.com/tango-contrib/session-ssdb&#34;&gt;ssdb&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;这几种方式进行Session内容的存储。当然如果你愿意也可以自己来实现一个Store。&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;generator&#34;&gt;Generator&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Generator是一个接口主要封装了SessionID的生成算法其定义如下&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type IdGenerator interface {&#xA; Gen(req *http.Request) Id&#xA; IsValid(id Id) bool&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;默认的Generator是Sha1Generator他是通过&lt;code&gt;req.RemoteAddr&lt;/code&gt;,当前时间和随机字符串生成。&#xA;当然你也可以自定义更好的算法来生成SessionID。&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;tracker&#34;&gt;Tracker&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Tracker是一个接口主要封装了Session的跟踪方式其定义如下&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type Tracker interface {&#xA; SetMaxAge(maxAge time.Duration)&#xA; Get(req *http.Request) (Id, error)&#xA; Set(req *http.Request, rw http.ResponseWriter, id Id)&#xA; Clear(rw http.ResponseWriter)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;默认的Tracker实现是&lt;code&gt;CookieTracker&lt;/code&gt;就是我们最常见的将SessionID保存在cookie中通过cookie来进行跟踪。Session中间件中也同时提供了&lt;code&gt;HeaderTracker&lt;/code&gt;支持将SessionID保存在自定义的Http Header中。当然你也可以自定义Tracker比如通过URL参数来进行跟踪等等方式。&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;最后&#34;&gt;最后&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;看起来似乎很复杂,但是一般情况下都不需要去改变,你只需要&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;t := tango.Classic()&#xA;t.Use(session.New())&#xA;t.Run()&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;</description>
<pubDate>Thu, 18 Feb 2016 16:00:00 +0000</pubDate>
</item>
<item>
<title>Go语言库net/smtp发送邮件故障一则</title>
<link>http://lunny.info/2016/1/7/Go语言库net/smtp发送邮件故障一则.html</link>
<description>&lt;p&gt;在通过net/smtp包发送邮件时遇到一个奇怪的错误在mac和windows上运行都能够成功发送邮件但是将程序跨平台编译后上传到debian linux执行报&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;x509: failed to load system roots and no roots provided&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;的错误,看样子是在认证邮件服务器证书的根证书时没有找到本地的根证书库。最后通过运行&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;#apt-get install ca-certificates&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;之后,邮件成功发送。&lt;/p&gt;&#xA;</description>
<pubDate>Thu, 07 Jan 2016 16:00:00 +0000</pubDate>
</item>
<item>
<title>Tango v0.4版本发布,带来统一高性能的新路由</title>
<link>http://lunny.info/2015/4/6/Tango-v0.4版本发布,带来统一高性能的新路由.html</link>
<description>&lt;h1 id=&#34;起子&#34;&gt;起子&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;自Tango发布之后大概有3个月了受到了很多人的关注也提了很多的改进意见。我自己也通过不断&#xA;的用他开发从而发现问题不断改进。昨天我发布了0.4.0版本,很明显,最近版本号升得比较快。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;从0.3到0.4,最重大的改变是路由方面的。路由基本上完全重写,目前路由变得更统一,更灵活,性能上也有不少提升。来看看新的路由如何使用。&lt;/p&gt;&#xA;&#xA;&lt;h1 id=&#34;新路由&#34;&gt;新路由&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;目前支持4种路由静态路由命名路由Catch-All路由正则路由。下面分别说说4种路由&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;静态路由&#34;&gt;静态路由&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;静态路由是最普遍的默认的net/http目前只支持静态路由比如&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;tango.Get(&amp;quot;/&amp;quot;, new(Action))&#xA;tango.Get(&amp;quot;/public/bootstrap.min.css&amp;quot;, new(Action))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2 id=&#34;命名路由&#34;&gt;命名路由&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;在0.4之前的版本中,命名路由的形式一般是&lt;code&gt;:name&lt;/code&gt;在0.4版本兼容以前的模式,并且新增了&lt;code&gt;(:name)&lt;/code&gt;的形式。这个新的设计将允许使用字符或者数字作为分隔符。比如,新旧版本都支持这种形式:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;tango.Get(&amp;quot;/:name&amp;quot;, new(Action))&#xA;tango.Get(&amp;quot;/:name1-:name2&amp;quot;, new(Action))&#xA;tango.Get(&amp;quot;/:name1/:name2&amp;quot;, new(Action))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;而新版本增加了对下列形式的支持:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;tango.Get(&amp;quot;/(:name1)abc(:name2)&amp;quot;, new(Action))&#xA;tango.Get(&amp;quot;/(:name1)123(:name2)&amp;quot;, new(Action))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2 id=&#34;catch-all路由&#34;&gt;Catch-All路由&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;Catch-All路由是0.4新增的一种路由形式。Catch-All路由和命名路由很相似他们的的唯一区别在于&#xA;命名路由不能匹配/而Catch-All路由可以匹配/。比如:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;tango.Get(&amp;quot;/*name&amp;quot;, new(Action))&#xA;tango.Get(&amp;quot;/*name/123&amp;quot;, new(Action))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;比如当访问 &lt;code&gt;/name1/name2/123&lt;/code&gt; 时,第二个路由会匹配出 &lt;code&gt;*name = name1/name2&lt;/code&gt;。当访问&lt;code&gt;/name1/name2&lt;/code&gt;时,则第一个路由会匹配出 &lt;code&gt;*name = name1/name2&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;获取Catch-All路由请通过 &lt;code&gt;Params.Get(&amp;quot;*name&amp;quot;)&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;正则路由&#34;&gt;正则路由&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;&lt;strong&gt;注意0.4版本的正则路由和老版本的正则路由不兼容&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;为了统一表达形式0.4版本抛弃了以前版本中完全使用Go的正则的方式来进行匹配的方式&lt;/p&gt;&#xA;&#xA;&lt;p&gt;0.3版本以前:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;tango.Get(&amp;quot;/(.*)&amp;quot;, new(Action))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;而获取参数通过 Params.Get(&amp;rdquo;:0&amp;rdquo;)来获取。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;0.3版本到0.4版本之间:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;tango.Get(&amp;quot;/(?P&amp;lt;name&amp;gt;.*)&amp;quot;, new(Action))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;而获取参数通过 Params.Get(&amp;rdquo;:name&amp;rdquo;)来获取,同时也可以使用&lt;code&gt;Params.Get(&amp;quot;:0&amp;quot;)&lt;/code&gt;来获取。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;0.4版本不兼容前面的语法,再次提醒注意,而采用如下方法,匿名的正则已不被允许,&lt;code&gt;Params.Get(&amp;quot;:0&amp;quot;)&lt;/code&gt;也不再受支持:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;tango.Get(&amp;quot;/(:name.*)&amp;quot;, new(Action))&#xA;tango.Get(&amp;quot;/(:name[0-9]+)&amp;quot;, new(Action))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;而获取参数通过 Params.Get(&amp;rdquo;:name&amp;rdquo;)来获取也可以通过Params[0].Value来获取Params目前变成了一个Slice。&lt;/p&gt;&#xA;&#xA;&lt;h1 id=&#34;路由优先级&#34;&gt;路由优先级&lt;/h1&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;静态路由和其它路由都匹配时,静态路由优先,跟添加的顺序无关;&lt;/li&gt;&#xA;&lt;li&gt;其它路由之间根据添加的顺序,先添加的优先。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;p&gt;例如:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;t := tango.Classic()&#xA;t.Get(&amp;quot;/:name&amp;quot;, new(Others))&#xA;t.Get(&amp;quot;/admin&amp;quot;, new(Admin))&#xA;t.Run()&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;以上代码,当请求 &lt;code&gt;/admin&lt;/code&gt; 时, &lt;code&gt;Admin&lt;/code&gt;&lt;code&gt;Get&lt;/code&gt; 方法将被执行。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;t := tango.Classic()&#xA;t.Get(&amp;quot;/:name&amp;quot;, new(Admin))&#xA;t.Get(&amp;quot;/*name&amp;quot;, new(Others))&#xA;t.Run()&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;以上代码,当请求 &lt;code&gt;/admin&lt;/code&gt; 时, &lt;code&gt;Admin&lt;/code&gt;&lt;code&gt;Get&lt;/code&gt; 方法将被执行;当请求 &lt;code&gt;/admin/ui&lt;/code&gt;, &lt;code&gt;Others&lt;/code&gt;&lt;code&gt;Get&lt;/code&gt; 方法将被执行。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;t := tango.Classic()&#xA;t.Get(&amp;quot;/*name&amp;quot;, new(Admin))&#xA;t.Get(&amp;quot;/:name&amp;quot;, new(Others))&#xA;t.Run()&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;以上代码, &lt;code&gt;Others&lt;/code&gt;&lt;code&gt;Get&lt;/code&gt; 方法将永远不会被执行, 因为所有匹配的请求均会调用 &lt;code&gt;Admin&lt;/code&gt;&lt;code&gt;Get&lt;/code&gt; 方法。&lt;/p&gt;&#xA;&#xA;&lt;h1 id=&#34;注意事项&#34;&gt;注意事项&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;关于新路由也有几点需要注意的事项:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;p&gt;命名和正则等可以混合使用,如:&lt;code&gt;/:name1-(:name2[0-9]+)&lt;/code&gt;但是要注意必须要有分隔符没有分隔符在新增路由时会panic比如如下形式就不受支持&lt;code&gt;/:name1:name2&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;命名理论上是可以相同的并且程序不会报错但是不建议这样使用。通过Params.Get(&amp;rdquo;:name&amp;rdquo;)只会返回第一个可通过自己遍历Params来获取多个相同的名字。比如&lt;code&gt;/:name-:name&lt;/code&gt;,这种规则是允许的,但是不推荐。&lt;/p&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h1 id=&#34;收尾&#34;&gt;收尾&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;新的路由形式上更加统一相信可以满足大家的绝大部分需求。希望大家多提宝贵意见可加QQ群369240307或者在 &lt;a href=&#34;https://github.com/lunny/tango&#34;&gt;github.com/lunny/tango&lt;/a&gt; 以及 &lt;a href=&#34;http://git.oschina.net/lunny/tango&#34;&gt;git.oschina.net/lunny/tango&lt;/a&gt; 中提出issue。&lt;/p&gt;&#xA;</description>
<pubDate>Mon, 06 Apr 2015 15:23:00 +0000</pubDate>
</item>
<item>
<title>在Xorm中使用Join和Extends标记</title>
<link>http://lunny.info/2015/1/13/在Xorm中使用Join和Extends标记.html</link>
<description>&lt;p&gt;本文主要针对对Xorm已经有了一定了解的读者如果您是第一次了解Xorm请先阅读&lt;a href=&#34;http://xorm.io/docs&#34;&gt;xorm操作手册&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Xorm的基本操作都是比较简单的可能大家也都比较熟悉了。今天主要讲解extends标记和join的使用。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;一般我们会针对数据库中的每一个表,建立一个对应的结构体。比如:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type User struct {&#xA; Id int64&#xA; Name string&#xA;}&#xA;&#xA;type Account struct {&#xA; Id int64&#xA; UserId int64 `xorm:&amp;quot;index&amp;quot;`&#xA; Amount int64&#xA;}&#xA;&#xA;type Car struct {&#xA; Id int64&#xA; UserId int64 `xorm:&amp;quot;index&amp;quot;`&#xA; Type int&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;我们定义了三个结构体,对应数据库的三个表,我们在启动时通过:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;engine.Sync2(new(User), new(Account), new(Car))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;来进行数据库结构的同步。在这个数据库结构中我们假设一个用户拥有一个Account一个用户拥有多个Car。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;OK。复杂需求来了。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;1我们需要获得所有的用户的姓名和对应的账户的余额&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type AccountUser struct {&#xA; Account `xorm:&amp;quot;extends&amp;quot;`&#xA; User `xorm:&amp;quot;extends&amp;quot;`&#xA;}&#xA;&#xA;var accounts = make([]*AccountUser, 0)&#xA;engine.Table(&amp;quot;account&amp;quot;).Join(&amp;quot;INNER&amp;quot;, &amp;quot;user&amp;quot;, &amp;quot;account.user_id = user.id&amp;quot;).Find(&amp;amp;accounts)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;OK。这样我们就取出了user和对应的account我们通过&lt;code&gt;account.Account&lt;/code&gt;可以获取到Account的信息通过&lt;code&gt;account.User&lt;/code&gt;可以获取到User的信息。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;这个是两个表Join那么如果是三个表也是类似的做法。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;2我只需要用户名不需要其它的内容&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type AccountUser struct {&#xA; Account `xorm:&amp;quot;extends&amp;quot;`&#xA; Name string&#xA;}&#xA;var accounts = make([]*AccountUser, 0)&#xA;engine.Table(&amp;quot;account&amp;quot;).Join(&amp;quot;INNER&amp;quot;, &amp;quot;user&amp;quot;, &amp;quot;account.user_id = user.id&amp;quot;).Find(&amp;amp;accounts)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;其实我们代码也是差不多的但是这里我们实际上在查询数据库的时候是查询了user表的所有内容的。只是在最后赋值到结构体时按需赋值。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;3更复杂的我们还想知道每人有几辆车。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type AccountUser struct {&#xA; Account `xorm:&amp;quot;extends&amp;quot;`&#xA; Name string&#xA; NumCars int&#xA;}&#xA;var accounts = make([]*AccountUser, 0)&#xA;engine.Sql(&amp;quot;select account.*, user.name, (select count(id) from car where car.user_id = user.id) as num_cars from account, user where account.user_id = user.id&amp;quot;).Find(&amp;amp;accounts)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;在这样的复杂需求下我们使用了Sql函数和extends标记结合来完成这个操作。&lt;/p&gt;&#xA;</description>
<pubDate>Tue, 13 Jan 2015 10:13:00 +0000</pubDate>
</item>
<item>
<title>Tango微内核可扩展的Go语言Web框架</title>
<link>http://lunny.info/2015/1/7/Tango微内核可扩展的Go语言Web框架.html</link>
<description>&lt;h1 id=&#34;简介&#34;&gt;简介&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;Golang的web框架基本上处于一个井喷期那么为什么要再造一个轮子。这是因为目前可扩展性比较强的都是基于函数作为可执行体的而以结构体作为执行体的框架目前可扩展性都不够强包括我原先写的框架xweb也是如此。因此一个全新的框架出来了先上地址&lt;a href=&#34;https://github.com/lunny/tango&#34;&gt;https://github.com/lunny/tango&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;初看Tango框架感觉和Martini及Macaron非常的像。比如这段代码&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;package main&#xA;&#xA;import &amp;quot;github.com/lunny/tango&amp;quot;&#xA;&#xA;func main() {&#xA; t := tango.Classic()&#xA; t.Get(&amp;quot;/&amp;quot;, func() string {&#xA; return &amp;quot;Hello tango!&amp;quot;&#xA; })&#xA; t.Run()&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;这种其实大家都支持的。再看这个吧:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;package main&#xA;&#xA;import &amp;quot;github.com/lunny/tango&amp;quot;&#xA;&#xA;type Action struct {}&#xA;func (Action) Get() string {&#xA; return &amp;quot;Hello tango!&amp;quot;&#xA;}&#xA;&#xA;func main() {&#xA; t := tango.Classic()&#xA; t.Get(&amp;quot;/&amp;quot;, new(Action))&#xA; t.Run()&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Tango同时支持函数和结构体作为执行体不过主打是结构体函数只作为兼容而用。下面对Tango的各种功能一一道来。&lt;/p&gt;&#xA;&#xA;&lt;h1 id=&#34;路由&#34;&gt;路由&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;Tango目前同时支持3种路由规则:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;p&gt;静态路由&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;tg.Get(&amp;quot;/&amp;quot;, new(Action))&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;命名路由&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;tg.Get(&amp;quot;/:name&amp;quot;, new(Action))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;命名路由对应的参数内容可通过&lt;code&gt;ctx.Params().Get(&amp;quot;:name&amp;quot;)&lt;/code&gt;来获得&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;正则路由&lt;/p&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;tg.Get(&amp;quot;/(.*)&amp;quot;, new(Action))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;正则路由对应的参数内容可通过&lt;code&gt;ctx.Params().Get(&amp;quot;:0&amp;quot;)&lt;/code&gt;来获得&lt;/p&gt;&#xA;&#xA;&lt;p&gt;这里要注意命名路由和正则路由不可混用。&lt;/p&gt;&#xA;&#xA;&lt;h1 id=&#34;执行体&#34;&gt;执行体&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;同时支持函数执行体和结构体执行体支持的函数执行体形式如下可以有零个或一个返回值返回值由Return插件提供后面会再提&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;func()&#xA;func(http.ResponseWriter, *http.Request)&#xA;func(*tango.Context)&#xA;func(http.Response.Writer)&#xA;func(*http.Request)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;同时支持包含&lt;code&gt;Get,Post,Head,Options,Trace,Patch,Delete,Put&lt;/code&gt;作为成员方法的结构体作为执行体。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type Action struct {}&#xA;func (Action) Get() string {&#xA; return &amp;quot;Get&amp;quot;&#xA;}&#xA;&#xA;func (Action) Post() string {&#xA; return &amp;quot;Post&amp;quot;&#xA;}&#xA;&#xA;func (Action) Head() string {&#xA; return &amp;quot;Head&amp;quot;&#xA;}&#xA;&#xA;func (Action) Options() string {&#xA; return &amp;quot;Options&amp;quot;&#xA;}&#xA;&#xA;func (Action) Trace() string {&#xA; return &amp;quot;Trace&amp;quot;&#xA;}&#xA;&#xA;func (Action) Patch() string {&#xA; return &amp;quot;Patch&amp;quot;&#xA;}&#xA;&#xA;func (Action) Delete() string {&#xA; return &amp;quot;Delete&amp;quot;&#xA;}&#xA;&#xA;func (Action) Put() string {&#xA; return &amp;quot;Put&amp;quot;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;在使用路由时可以用对应的方法或者Any来加路由&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;t := tango.Classic()&#xA;t.Get(&amp;quot;/1&amp;quot;, new(Action))&#xA;t.Any(&amp;quot;/2&amp;quot;, new(Action))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h1 id=&#34;路由分组&#34;&gt;路由分组&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;Tango提供了Group来进行路由分组Group的最简单形式如下&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;g := tango.NewGroup()&#xA;g.Get(&amp;quot;/1&amp;quot;, func() string {&#xA; return &amp;quot;/1&amp;quot;&#xA;})&#xA;g.Post(&amp;quot;/2&amp;quot;, func() string {&#xA; return &amp;quot;/2&amp;quot;&#xA;})&#xA;&#xA;t := tango.Classic()&#xA;t.Group(&amp;quot;/api&amp;quot;, g)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;这样访问/api/1就会返回/1访问/api/2就会返回/2&lt;/p&gt;&#xA;&#xA;&lt;p&gt;同时也可以这样:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;t := tango.Classic()&#xA;t.Group(&amp;quot;/api&amp;quot;, func(g *tango.Group) {&#xA; g.Get(&amp;quot;/1&amp;quot;, func() string {&#xA; return &amp;quot;/1&amp;quot;&#xA; })&#xA; g.Post(&amp;quot;/2&amp;quot;, func() string {&#xA; return &amp;quot;/2&amp;quot;&#xA; })&#xA;})&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Group也支持子Group&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;t := tango.Classic()&#xA;t.Group(&amp;quot;/api&amp;quot;, func(g *tango.Group) {&#xA; g.Group(&amp;quot;/v1&amp;quot;, func(cg *tango.Group) {&#xA; cg.Get(&amp;quot;/1&amp;quot;, func() string {&#xA; return &amp;quot;/1&amp;quot;&#xA; })&#xA; cg.Post(&amp;quot;/2&amp;quot;, func() string {&#xA; return &amp;quot;/2&amp;quot;&#xA; })&#xA; })&#xA;})&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;最后Group也支持逻辑分组&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;o := tango.Classic()&#xA;o.Group(&amp;quot;&amp;quot;, func(g *tango.Group) {&#xA; g.Get(&amp;quot;/1&amp;quot;, func() string {&#xA; return &amp;quot;/1&amp;quot;&#xA; })&#xA;})&#xA;&#xA;o.Group(&amp;quot;&amp;quot;, func(g *tango.Group) {&#xA; g.Post(&amp;quot;/2&amp;quot;, func() string {&#xA; return &amp;quot;/2&amp;quot;&#xA; })&#xA;})&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;这样,即使路由间只是逻辑上分开,而并没有上级路径分开,也是可以分成不同的组。&lt;/p&gt;&#xA;&#xA;&lt;h1 id=&#34;返回值&#34;&gt;返回值&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;通过前面的例子我们会发现所有执行体函数可以有一个返回值或者没有返回值。Tango的内部插件ReturnHandler来负责根据函数的第一个返回值的类型来自动的生成输出默认规则如下&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;p&gt;&lt;code&gt;string&lt;/code&gt;&#xA;返回string则string会被转换为bytes并写入到ResponseWriter默认状态码为200&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;&lt;code&gt;[]byte&lt;/code&gt;&#xA;返回[]byte, 则会直接写入ResponseWriter默认状态码为200&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;&lt;code&gt;error&lt;/code&gt;&#xA;返回error接口如果不为nil, 则返回状态码为500内容为&lt;code&gt;error.Error()&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;&lt;code&gt;AbortError&lt;/code&gt;&#xA;返回tango.AbortError接口如果不为nil则返回状态码为&lt;code&gt;AbortError.Code&lt;/code&gt;,内容为&lt;code&gt;AbortError.Error()&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;当然了你可以撰写你自己的插件来判断更多的返回值类型。返回值结合tango的一些tricker可以极大的简化代码比如&lt;/p&gt;&#xA;&#xA;&lt;p&gt;如果Action结构体包含匿名结构体&lt;code&gt;tango.Json&lt;/code&gt;或者&lt;code&gt;tango.Xml&lt;/code&gt;,则返回值结果如下:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;如果包含&lt;code&gt;tango.Json&lt;/code&gt;匿名结构体,则返回头的&lt;code&gt;Content-Type&lt;/code&gt;会自动设置为:&lt;code&gt;application/json&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;p&gt;如果返回值为error则返回值为{&amp;ldquo;err&amp;rdquo;: err.Error()}状态码为200&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;如果返回值为AbortError则返回值为{&amp;ldquo;err&amp;rdquo;: err.Error()}状态码为err.Code()&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;如果返回值为string则返回值为{&amp;ldquo;content&amp;rdquo;: content}状态码为200&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;如果返回值为[]byte则返回值为{&amp;ldquo;content&amp;rdquo;: string(content)}状态码为200&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;如果返回值为mapslice结构体或其它可自动Json化的内容则返回值为map自动json对应的值状态码为200&lt;/p&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;例如:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type Action struct {&#xA; tango.Json&#xA;}&#xA;&#xA;var i int&#xA;func (Action) Get() interface{} {&#xA; if i == 0 {&#xA; i = i + 1&#xA; return map[string]interface{}{&amp;quot;i&amp;quot;:i}&#xA; }&#xA; return errors.New(&amp;quot;could not visit&amp;quot;)&#xA;}&#xA;&#xA;func main() {&#xA; t := tango.Classic()&#xA; t.Any(&amp;quot;/&amp;quot;, new(Action))&#xA; t.Run()&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;以上例子访问时会始终返回json第一次访问会返回map第二次返回error。这里只是演示代码实际执行i必须加锁&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Compress&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;Tango拥有一个默认的压缩中间件可以按照扩展名来进行文件的压缩。同时你也可以要求某个Action自动或强制使用某种压缩。比如&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type CompressExample struct {&#xA; tango.Compress // 添加这个匿名结构体,要求这个结构体的方法进行自动检测压缩&#xA;}&#xA;&#xA;func (CompressExample) Get() string {&#xA; return fmt.Sprintf(&amp;quot;This is a auto compress text&amp;quot;)&#xA;}&#xA;&#xA;o := tango.Classic()&#xA;o.Get(&amp;quot;/&amp;quot;, new(CompressExample))&#xA;o.Run()&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;以上代码默认会检测浏览器是否支持压缩如果支持则看是否支持gzip如果支持gzip则使用gzip压缩如果支持deflate则使用deflate压缩。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type GZipExample struct {&#xA; tango.GZip // add this for ask compress to GZip, if accept-encoding has no gzip, then not compress&#xA;}&#xA;&#xA;func (GZipExample) Get() string {&#xA; return fmt.Sprintf(&amp;quot;This is a gzip compress text&amp;quot;)&#xA;}&#xA;&#xA;o := tango.Classic()&#xA;o.Get(&amp;quot;/&amp;quot;, new(GZipExample))&#xA;o.Run()&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;以上代码默认会检测浏览器是否支持gzip压缩如果支持gzip则使用gzip压缩否则不压缩。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type DeflateExample struct {&#xA; tango.Deflate // add this for ask compress to Deflate, if not support then not compress&#xA;}&#xA;&#xA;func (DeflateExample) Get() string {&#xA; return fmt.Sprintf(&amp;quot;This is a deflate compress text&amp;quot;)&#xA;}&#xA;&#xA;o := tango.Classic()&#xA;o.Get(&amp;quot;/&amp;quot;, new(DeflateExample))&#xA;o.Run()&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;以上代码默认会检测浏览器是否支持deflate压缩如果支持deflate则使用deflate压缩否则不压缩。&lt;/p&gt;&#xA;&#xA;&lt;h1 id=&#34;static&#34;&gt;Static&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;Static 让你用一行代码可以完成一个静态服务器。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;func main() {&#xA; t := tango.New(tango.Static())&#xA; t.Run()&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;然后,将你的文件放到 &lt;code&gt;./public&lt;/code&gt; 目录下,你就可以通过浏览器放问到他们。比如:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;http://localhost/images/logo.png --&amp;gt; ./public/images/logo.png&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;当然,你也可以加入&lt;code&gt;basicauth&lt;/code&gt;或者你自己的认证中间件,这样就变为了一个私有的文件服务器。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;func main() {&#xA; t := tango.New()&#xA; t.Use(AuthHandler)&#xA; t.Use(tango.Static())&#xA; t.Run()&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h1 id=&#34;handler&#34;&gt;Handler&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;Handler 是tango的中间件。在tango中几乎所有的事情都由中间件来完成。撰写一个你自己的中间件非常简单并且我们鼓励您只加载需要的中间件。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;tango的中间件只需要符合以下接口即可。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type Handler interface {&#xA; Handle(*tango.Context)&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;同时tango也提供了&lt;code&gt;tango.HandlerFunc&lt;/code&gt;,以方便你将一个函数包装为中间件。比如:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;func MyHandler() tango.HandlerFunc {&#xA; return func(ctx *tango.Context) {&#xA; fmt.Println(&amp;quot;this is my first tango handler&amp;quot;)&#xA; ctx.Next()&#xA; }&#xA;}&#xA;&#xA;t := tango.Classic()&#xA;t.Use(MyHandler())&#xA;t.Run()&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;正常的形式也可以是:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type HelloHandler struct {}&#xA;func (HelloHandler) Handle(ctx *tango.Context) {&#xA; fmt.Println(&amp;quot;before&amp;quot;)&#xA; ctx.Next()&#xA; fmt.Println(&amp;quot;after&amp;quot;)&#xA;}&#xA;&#xA;t := tango.Classic()&#xA;t.Use(new(HelloHandler))&#xA;t.Run()&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;当然你可以直接将一个包含tango.Context指针的函数作为中间件&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;tg.Use(func(ctx *tango.Context){&#xA; fmt.Println(&amp;quot;before&amp;quot;)&#xA; ctx.Next()&#xA; fmt.Println(&amp;quot;after&amp;quot;)&#xA;})&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;为了和标准库兼容tango通过UseHandler支持http.Handler作为中间件&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;tg.UseHandler(http.Handler(func(resp http.ResponseWriter, req *http.Request) {&#xA;&#xA;}))&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;老的中间件会被action被匹配之前进行调用。&lt;/p&gt;&#xA;&#xA;&lt;h1 id=&#34;call-stack&#34;&gt;Call stack&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;以下是中间件的调用顺序图:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;tango.ServeHttp&#xA;|--Handler1&#xA; |--Handler2&#xA; |-- ...HandlerN&#xA; |---Action(If matched)&#xA; ...HandlerN--|&#xA; Handler2 ----|&#xA; Handler1--|&#xA;(end)--|&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;在中间件中,您的中间件代码可以在&lt;code&gt;Next()&lt;/code&gt;被调用之前或之后执行Next表示执行下一个中间件或Action被执行如果url匹配的话。如果不调用Next那么当前请求将会被立即停止之后的所有代码将不会被执行。&lt;/p&gt;&#xA;&#xA;&lt;h1 id=&#34;注入&#34;&gt;注入&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;更多的注入方式参见以下示例代码:&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;context&#34;&gt;Context&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type Action struct {&#xA; tango.Ctx&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2 id=&#34;logger&#34;&gt;Logger&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type Action struct {&#xA; tango.Log&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2 id=&#34;params&#34;&gt;Params&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type Action struct {&#xA; tango.Params&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2 id=&#34;json&#34;&gt;Json&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type Action struct {&#xA; tango.Json&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2 id=&#34;xml&#34;&gt;Xml&lt;/h2&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;type Action struct {&#xA; tango.Xml&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h1 id=&#34;第三方插件&#34;&gt;第三方插件&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;目前已经有了一批第三方插件,更多的插件正在陆续开发中,欢迎大家进行贡献:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/tango-contrib/session&#34;&gt;session&lt;/a&gt; - Session管理&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/tango-contrib/xsrf&#34;&gt;xsrf&lt;/a&gt; - 生成csrf token和进行校验&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/tango-contrib/bind&#34;&gt;bind&lt;/a&gt; - 自动绑定参数到结构体&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/tango-contrib/renders&#34;&gt;renders&lt;/a&gt; - Go模板引擎插件&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/tango-contrib/dispatch&#34;&gt;dispatch&lt;/a&gt; - 多应用支持&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/tango-contrib/tpongo2&#34;&gt;tpongo2&lt;/a&gt; - &lt;a href=&#34;https://github.com/flosch/pongo2&#34;&gt;Pongo2&lt;/a&gt; 模板引擎支持&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/tango-contrib/captcha&#34;&gt;captcha&lt;/a&gt; - 验证码插件&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/tango-contrib/events&#34;&gt;events&lt;/a&gt; - 事件插件&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/tango-contrib/flash&#34;&gt;flash&lt;/a&gt; - 在requests之间共享数据&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h1 id=&#34;案例&#34;&gt;案例&lt;/h1&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/go-tango/wego&#34;&gt;Wego&lt;/a&gt; - golanghome.com论坛的修改版&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://github.com/gofxh/blog&#34;&gt;Blog&lt;/a&gt; - 一个新型博客&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://godaily.org&#34;&gt;Godaily&lt;/a&gt; - &lt;a href=&#34;https://github.com/godaily/news&#34;&gt;github.com/godaily/news&lt;/a&gt; - Go新闻&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;http://github.com/go-xorm/dbweb&#34;&gt;dbweb&lt;/a&gt; - go语言写的数据库管理web界面&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
<pubDate>Wed, 07 Jan 2015 16:00:00 +0000</pubDate>
</item>
<item>
<title>foxmail for mac试用效果不错</title>
<link>http://lunny.info/2013/3/12/foxmail-for-mac试用效果不错.html</link>
<description>&lt;p&gt;foxmail for mac v1.0 今天发布了。链接地址:&lt;a href=&#34;http://foxmail.com.cn/mac/&#34;&gt;http://foxmail.com.cn/mac/&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;稍微试用了下初一看简直就是sparrow的clone。再仔细用了用感觉有几点吸引人的地方&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;p&gt;有邮件规则这个功能,特别是增加了通知邮件这个功能,把常见的一些规则内置在邮箱中。&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;gmail的标签直接平铺在界面上而不是像sparrow那样需要点击才能够查看更方便了不过颜色不太好看。&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;邮件分组算法似乎比sparrow要好点。&lt;/p&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;总之果断转到了foxmail删除了sparrow。&lt;/p&gt;&#xA;</description>
<pubDate>Tue, 12 Mar 2013 09:51:00 +0000</pubDate>
</item>
<item>
<title>VIM快捷键大全</title>
<link>http://lunny.info/2013/2/27/VIM快捷键大全.html</link>
<description>&lt;h1 id=&#34;vim快捷键大全&#34;&gt;VIM快捷键大全&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;Vim是一个超级牛的编辑器可以说是专为程序员设计的编辑器强大的有些不可思议 。不过其学习曲线稍显陡峭,前两天在网上找到一个小图表,比较全,如果对表中所列的命令学习熟悉以后,处理日常的文本就已经足够,所以贴出来,大家参考参考。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Vim的牛B之处不在于其功能之繁多更不在于其学习曲线之陡峭而在于这些命令大都可以进行组合 比如9yy命令表示复制9行内容9表示要复制的行数同样100dd表示删除100行当数字和命令合作的时候就比单纯的命令更强大同样c命令表示擦除w表示word即单词那么cw就表示擦除一个单词c5w就表示删除5个单词等等。将这些简单命令合成在一起就可以发挥出难以想象的强大功能而且可以使你的编辑工作充满乐趣。&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;宏命令-macros&#34;&gt;宏命令(Macros)&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;高级一些的编辑器都会包含宏功能vim当然不能缺少了在vim中使用宏是非常方便的&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;:qx 开始记录宏并将结果存入寄存器x&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;q 退出记录模式&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;@x 播放记录在x寄存器中的宏命令&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;稍微解释一下当在normal模式下输入:qx后你对文本的所有编辑动作将会被记录下来再次输入q即退出了记录模式然后输入@x对刚才记录下来的命令进行重复此命令后可跟数字表示要重复多少次比如@x20可以重复20次。这个在文本的批处理中是非常有用的。&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;同时编辑多个文件&#34;&gt;同时编辑多个文件&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;在vim众多的插件中有一个叫minibuffer的插件就是下面所说的标签页功能了可以支持同时编辑多个文件。&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;标签命令&#34;&gt;标签命令&lt;/h3&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;:tabe fn 在一个新的标签页中编辑文件fn&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;gt 切换到下一个标签页&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;gT 切换到上一个标签页&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;:tabr 切换到第一个标签页&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;:tabl 切换到最后一个标签页&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;:tabm [N] 把当前tab移动到第N个tab之后&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;正如你所想象的那样跟eclipse, ue等的标签页是一个意思&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;窗口命令&#34;&gt;窗口命令&lt;/h3&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ctrl+w s 水平分割窗口&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;ctrl+w w 切换窗口&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;ctrl+w q 退出当前窗口(由于同时有多个文件,此命令不会影响其他窗口)&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;ctrl+w v 垂直分割窗口&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h3 id=&#34;其他&#34;&gt;其他&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;vim在保存之前不会对文件做实际的修改只是加载到缓冲区中对文件的编辑其实是对缓冲区的编辑直到:w时才会存入物理文件。&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;:e file 把file加载到新的缓冲区中&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;:bn 跳转到下一个缓冲区&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;:bd 删除缓冲区(关闭文件)&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;:sp fn 分割窗口并将fn加载到新的窗口中&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h3 id=&#34;退出编辑器&#34;&gt;退出编辑器&lt;/h3&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;:w 将缓冲区写入文件,即保存修改&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;:wq 保存修改并退出&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;:x 保存修改并退出&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;:q 退出,如果对缓冲区进行过修改,则会提示&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;:q! 强制退出,放弃修改&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h3 id=&#34;查找替换&#34;&gt;查找替换&lt;/h3&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;/pattern 向后搜索字符串pattern&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;?pattern 向前搜索字符串pattern&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;n 下一个匹配(如果是/搜索,则是向下的下一个,?搜索则是向上的下一个)&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;N 上一个匹配(同上)&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;:%s/old/new/g 搜索整个文件将所有的old替换为new&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;:%s/old/new/gc 搜索整个文件将所有的old替换为new每次都要你确认是否替换&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h3 id=&#34;复制粘贴&#34;&gt;复制粘贴&lt;/h3&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;dd 删除光标所在行&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;dw 删除一个字(word)&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;x 删除当前字符&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;X 删除前一个字符&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;D 删除到行末&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;yy 复制一行此命令前可跟数字标识复制多行如6yy表示从当前行开始复制6行&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;yw 复制一个字&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;y$ 复制到行末&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;p 粘贴粘贴板的内容到当前行的下面&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;P 粘贴粘贴板的内容到当前行的上面&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;]p 有缩进的粘贴vim会自动调节代码的缩进&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;&amp;ldquo;a 将内容放入/存入a寄存器可以支持多粘贴板&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;附:比如常用的一个寄存器就是系统寄存器,名称为+所以从系统粘贴板粘贴到vim中的命令为&amp;rdquo;+p,注意此处的+不表示操作符,二十一个寄存器。&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;移动光标&#34;&gt;移动光标&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;在vim中移动光标跟其他的编辑器中有很大的区别不过一旦学会了就会飞速 的在文本中移动了。&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;h,j,k,l 上,下,左,右&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;ctrl-f 上翻一页&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;ctrl-b 下翻一页&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;% 跳到与当前括号匹配的括号处,如当前在{,则跳转到与之匹配的}处&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;w 跳到下一个字首,按标点或单词分割&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;W 跳到下一个字首长跳如end-of-line被认为是一个字&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;e 跳到下一个字尾&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;E 跳到下一个字尾,长跳&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;b 跳到上一个字&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;B 跳到上一个字,长跳&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;0 跳至行首不管有无缩进就是跳到第0个字符&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;^ 跳至行首的第一个字符&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;$ 跳至行尾&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;gg 跳至文件的第一行&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;gd 跳至当前光标所在的变量的声明处&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;[N]G 跳到第N行如0G就等价于gg100G就是第100行&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;fx 在当前行中找x字符找到了就跳转至&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;; 重复上一个f命令而不用重复的输入fx&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;tx 与fx类似但是只是跳转到x的前一个字符处&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;Fx 跟fx的方向相反&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;),( 跳转到上/下一个语句&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;* 查找光标所在处的单词,向下查找&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;# 查找光标所在处的单词,向上查找&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;`. 跳转至上次编辑位置&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h3 id=&#34;在屏幕上移动&#34;&gt;在屏幕上移动&lt;/h3&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;H 移动光标到当前屏幕上最上边的一行&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;M 移动光标到当前屏幕上中间的一行&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;L 移动光标到当前屏幕上最下边的一行&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h3 id=&#34;书签&#34;&gt;书签&lt;/h3&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ma 把当前位置存成标签a&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;`a 跳转到标签a处&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h3 id=&#34;编辑&#34;&gt;编辑&lt;/h3&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;r 替换一个字符&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;J 将下一行和当前行连接为一行&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;cc 删除当前行并进入编辑模式&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;cw 删除当前字,并进入编辑模式&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;c$ 擦除从当前位置至行末的内容,并进入编辑模式&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;s 删除当前字符并进入编辑模式&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;S 删除光标所在行并进入编辑模式&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;xp 交换当前字符和下一个字符&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;u 撤销&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;ctrl+r 重做&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;. 重复上一个编辑命令&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;~ 切换大小写,当前字符&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;g~iw 切换当前字的大小写&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;gUiw 将当前字变成大写&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;guiw 将当前字变成小写&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;&amp;gt;&amp;gt; 将当前行右移一个单位&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;&amp;lt;&amp;lt; 将当前行左移一个单位(一个tab符)&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;== 自动缩进当前行&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2 id=&#34;插入模式&#34;&gt;插入模式&lt;/h2&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;i 从当前光标处进入插入模式&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;I 进入插入模式,并置光标于行首&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;a 追加模式,置光标于当前光标之后&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;A 追加模式,置光标于行末&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;o 在当前行之下新加一行,并进入插入模式&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;O 在当前行之上新加一行,并进入插入模式&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;Esc 退出插入模式&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2 id=&#34;可视模式&#34;&gt;可视模式&lt;/h2&gt;&#xA;&#xA;&lt;h3 id=&#34;标记文本&#34;&gt;标记文本&lt;/h3&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;v 进入可视模式,单字符模式&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;V 进入可视模式,行模式&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;ctrl+v 进入可视模式,列模式 类似于UE的列模式&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;o 跳转光标到选中块的另一个端点&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;U 将选中块中的内容转成大写&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;O 跳转光标到块的另一个端点&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;aw 选中一个字&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;ab 选中括号中的所有内容,包括括号本身&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;aB 选中{}括号中的所有内容&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;ib 选中括号中的内容,不含括号&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;iB 选中{}中的内容,不含{}&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;h2 id=&#34;对标记进行动作&#34;&gt;对标记进行动作&lt;/h2&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&amp;gt; 块右移&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;&amp;lt; 块左移&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;y 复制块&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;d 删除块&lt;br /&gt;&lt;/li&gt;&#xA;&lt;li&gt;~ 切换块中内容的大小写&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;好了这张简表就说到这里当然它涵盖的范围有限但是正如片头说的那样vim的强大之处在于这些命令可以组合起来使用那样才能体现出其强大的编辑功能。我在网上找到的这个文档也顺便贴出来大家可以同时参考。如果有机会我尽量找些例子来说明这些命令在实际中的用法那样才可以更好的使用它提高我们的工作效率。&lt;/p&gt;&#xA;</description>
<pubDate>Wed, 27 Feb 2013 02:49:39 +0000</pubDate>
</item>
<item>
<title>三步在Debian中创建自己的KVM虚拟机</title>
<link>http://lunny.info/2012/8/15/三步在Debian中创建自己的KVM虚拟机.html</link>
<description>&lt;p&gt;1 电脑一台必须的并且注意必须要支持VT-x同时在BIOS中Enable。然后就安装Debian吧推荐64位的6.0版本,安装过程基本都是下一步,就不用详细介绍了。&#xA; 捎带一笔国内比较快的Debian的源mirrors.163.com速度还可以。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;2 下一步就是要安装虚拟机软件了。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;apt-get install qemu-kvm bridge-utils uml-utilities&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;安装完毕就需要配置网络了,修改/etc/network/interfaces将eth0的配置注释掉加入br0的配置&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;auto br0 &#xA;iface br0 inet static #dhcp &#xA;bridge_ports eth0 &#xA;address 192.168.1.39 &#xA;netmask 255.255.255.0 &#xA;gateway 192.168.1.6&#xA;name-servers 8.8.8.8&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;修改完毕记得重启网络,实在不行就重启吧。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;3 第三步自然就是创建虚拟机,安装运行了。&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;先创建磁盘啊&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;kvm-img create disk.img 20G&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;然后安装系统了&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;kvm -boot d -cdrom xp.iso -m 1024 -hda ./disk.img -net nic -net tap -vnc :1&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;最后运行啊tap网卡会自动创建的不用担心&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;kvm -boot c -m 1024 -hda ./disk.img -net nic -net tap -vnc :1&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;当然如果需要后台运行的话建议安装screen&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;apt-get install screen&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;</description>
<pubDate>Wed, 15 Aug 2012 08:06:04 +0000</pubDate>
</item>
<item>
<title>py2exe打包BeautifulSoup遇到问题及解决</title>
<link>http://lunny.info/2012/3/25/py2exe打包BeautifulSoup遇到问题及解决.html</link>
<description>&lt;p&gt;今天用py2exe打包一个小程序但是怎么都不能够找到BeautifulSoup模块经过Google得知py2exe无法读取压缩的egg文件解决方法重新用解压方法安装BeautifulSoup同时发现pip比easy_install要更方便于是安装了pip。OK打包成功大功告成。&lt;/p&gt;&#xA;</description>
<pubDate>Sun, 25 Mar 2012 04:49:39 +0000</pubDate>
</item>
<item>
<title>一个json格式在线错误检测的网址</title>
<link>http://lunny.info/2012/3/8/一个json格式在线错误检测的网址.html</link>
<description>&lt;p&gt;&lt;a href=&#34;http://json.parser.online.fr/&#34;&gt;http://json.parser.online.fr/&lt;/a&gt;&lt;/p&gt;&#xA;</description>
<pubDate>Thu, 08 Mar 2012 01:04:57 +0000</pubDate>
</item>
<item>
<title>开源许可证的区别图示</title>
<link>http://lunny.info/2012/1/19/开源许可证的区别图示.html</link>
<description>&lt;p&gt;看了&lt;a href=&#34;http://www.ruanyifeng.com&#34;&gt;阮一峰&lt;/a&gt;的一片关于开源许可证的文章&lt;a href=&#34;http://www.ruanyifeng.com/blog/2011/05/how_to_choose_free_software_licenses.html&#34;&gt;《如何选择开源许可证?》&lt;/a&gt;,感觉里面这个图还有改进的余地,因此在他这个图的基础上作了一点改动。所有的许可证从左至右是从限制较多到限制较少的进行排列。如下图所示:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2012/01/OpenSource.png&#34;&gt;&lt;img src=&#34;/static/media/images/2012/01/OpenSource.png&#34; alt=&#34;开源许可证&#34; title=&#34;开源许可证&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;</description>
<pubDate>Thu, 19 Jan 2012 02:48:11 +0000</pubDate>
</item>
<item>
<title>Chrome扩展更新Smart RSS Subscriber for Google Reader™v0.1.5</title>
<link>http://lunny.info/2011/8/25/Chrome扩展更新Smart-RSS-Subscriber-for-Google-Reader™v0.1.5.html</link>
<description>&lt;p&gt;就是原先的Google Reader RSS Subscriber因为google的品牌政策所以修改了扩展名称另做了少量修改和修复。扩展地址为&#xA;&lt;a href=&#34;https://chrome.google.com/webstore/detail/ngjinemddlnikfdlbnbdbajdhagbcjmg&#34;&gt;https://chrome.google.com/webstore/detail/ngjinemddlnikfdlbnbdbajdhagbcjmg&lt;/a&gt;&lt;/p&gt;&#xA;</description>
<pubDate>Thu, 25 Aug 2011 07:06:18 +0000</pubDate>
</item>
<item>
<title>用VBScript实现生成目录树以Excel格式保存</title>
<link>http://lunny.info/2011/7/4/用VBScript实现生成目录树以Excel格式保存.html</link>
<description>&lt;p&gt;帮朋友实现这个功能本想采用Python熟门熟路的但是朋友机器可能没有安装。对于小白担心他会有更多的麻烦只好试试好久没用的VBScript了。保存为Excel格式其实偷了懒保存的是HTML格式不过可以很容易的转到Excel中。实现代码如下&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-vbscript&#34;&gt;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#xA;&#39; Copyright (C) 2011&#xA;&#39; Author: Lunny Xiao&#xA;&#39; Email: xiaolunwen@gmail.com&#xA;&#39; Date: 2011-7-1&#xA;&#39; Description: traversing directory and building directory tree as html file format. If you want excel format,&#xA;&#39; you can open the html, select all content and paste to a new excel file. That&#39;s all.&#xA;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#39;&#xA;&#xA;&#39;Option Explicit&#xA;&#xA;set fso = createobject(&amp;quot;scripting.filesystemobject&amp;quot;)&#xA;&#xA;Sub TraverFolder(fd, folder, level)&#xA; Set f = fso.GetFolder(folder)&#xA; Set files = f.Files&#xA; Set sfs = f.SubFolders&#xA; dim i&#xA; dim j&#xA; i = 0&#xA; For Each a in sfs&#xA; fd.write(&amp;quot;\n&amp;quot;)&#xA; For j = 1 To level&#xA; fd.write(&amp;quot;\n\n&amp;quot;)&#xA; Next&#xA; fd.write(&amp;quot;\n&amp;quot; &amp;amp; a.name &amp;amp; &amp;quot;\n\n&amp;quot;)&#xA; TraverFolder fd, a.Path, (level+1)&#xA; Next&#xA;&#xA; i = 0&#xA; For Each b In files&#xA; fd.write(&amp;quot;\n&amp;quot;)&#xA; For j = 1 To level&#xA; fd.write(&amp;quot;\n\n&amp;quot;)&#xA; Next&#xA; fd.write(&amp;quot;\n&amp;quot; &amp;amp; b.name &amp;amp; &amp;quot;\n\n&amp;quot;)&#xA; Next&#xA; If i = 0 Then&#xA; fd.write(vbcrlf)&#xA; End If&#xA;End Sub&#xA;&#xA;msg=&amp;quot;请输入根目录:&amp;quot;&#xA;dir=Inputbox(msg, &amp;quot;根目录&amp;quot;) &#xA;&#xA;set ts = fso.opentextfile(&amp;quot;d:\\res.html&amp;quot;, 2, true)&#xA;ts.write(&amp;quot;\n&amp;quot;)&#xA;TraverFolder ts, dir, 0&#xA;ts.write(&amp;quot;\n&amp;quot;)&#xA;ts.close()&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;使用方法&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;将上述代码保存为一个.vbs文件直接双击该文件会弹出框询问根目录可从资源管理器复制粘贴。点击确定会在D盘根目录生成一个res.html文件。打开该文件全选复制粘贴到一个新的Excel文件即可。&lt;/p&gt;&#xA;</description>
<pubDate>Mon, 04 Jul 2011 01:03:32 +0000</pubDate>
</item>
<item>
<title>Python实现图书10位ISBN号转换为13位</title>
<link>http://lunny.info/2011/5/25/Python实现图书10位ISBN号转换为13位.html</link>
<description>&lt;p&gt;ISBN是图书的一个国际通用编号详细信息可参考维基百科中的词条&lt;a href=&#34;http://zh.wikipedia.org/wiki/ISBN&#34;&gt;http://zh.wikipedia.org/wiki/ISBN&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;ISBN在07年前使用的是10位编号07年后并入国际货品编号升级为13位以下为将10位编号转换为13位编号的Python代码&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;def isbn10to13(isbn):&#xA; if len(isbn) != 10:&#xA; return None&#xA; isbn = &amp;quot;978&amp;quot;+isbn[:-1]&#xA; sum = 0&#xA; for i, b in enumerate(isbn):&#xA; sum += (1 if i % 2 == 0 else 3) * int(b)&#xA; return &amp;quot;%s%d&amp;quot; % (isbn, (10 - sum % 10) % 10)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;输入内容为10位的ISBN号去除连接符“-”的字符串输出内容为13位的ISBN号去除连接符“-”的字符串。主要的工作在于计算出最后一位的校验码。&lt;/p&gt;&#xA;</description>
<pubDate>Wed, 25 May 2011 05:28:03 +0000</pubDate>
</item>
<item>
<title>MySQL中的insert ignore into, replace into, insert select, on duplicate key update的用法小结</title>
<link>http://lunny.info/2011/5/23/MySQL中的insert-ignore-into,-replace-into,-insert-select,-on-duplicate-key-update的用法小结.html</link>
<description>&lt;p&gt;在MySQL中进行条件插入数据时可能会用到以下语句现小结一下。我们先建一个简单的表来作为测试&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sql&#34;&gt;CREATE TABLE `books` (&#xA; `id` int(11) NOT NULL AUTO_INCREMENT,&#xA; `name` varchar(200) NOT NULL,&#xA; PRIMARY KEY (`id`),&#xA; UNIQUE KEY `NewIndex1` (`name`)&#xA;) ENGINE=InnoDB DEFAULT CHARSET=utf8;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2 id=&#34;insert-ignore-into&#34;&gt;insert ignore into&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;当插入数据时如出现错误时如重复数据将不返回错误只以警告形式返回。所以使用ignore请确保语句本身没有问题否则也会被忽略掉。例如&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sql&#34;&gt;insert ignore into books (name) values (&#39;MySQL Manual&#39;)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2 id=&#34;on-duplicate-key-update&#34;&gt;on duplicate key update&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;当primary或者unique重复时则执行update语句如update后为无用语句如id=id则同1功能相同但错误不会被忽略掉。例如为了实现name重复的数据插入不报错可使用一下语句&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sql&#34;&gt;insert into books (name) values (&#39;MySQL Manual&#39;) on duplicate key update id = id&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2 id=&#34;insert-select-where-not-exist&#34;&gt;insert &amp;hellip; select &amp;hellip; where not exist&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;根据select的条件判断是否插入可以不光通过primary 和 unique来判断也可通过其它条件。例如&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sql&#34;&gt;insert into books (name) select &#39;MySQL Manual&#39; from dual where not exists (select id from books where id = 1)&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;h2 id=&#34;replace-into&#34;&gt;replace into&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;如果存在primary or unique相同的记录则先删除掉。再插入新记录。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sql&#34;&gt;replace into books SELECT 1, &#39;MySQL Manual&#39; FROM books&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;</description>
<pubDate>Mon, 23 May 2011 04:29:27 +0000</pubDate>
</item>
<item>
<title>Redis在Windows下的安装配置及Python调用</title>
<link>http://lunny.info/2011/5/17/Redis在Windows下的安装配置及Python调用.html</link>
<description>&lt;p&gt;Redis是一个Key-Value数据库优点是简单高效稳定缺点是所有数据存放于内存会定期dump到磁盘当然内存小时可以配置虚拟内存不过性能会有所下降。&#xA;官方只提供源代码下载并且不支持Windows可从第三方下载Windows下的预编译文件下载地址&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/dmajkic/redis/downloads&#34;&gt;https://github.com/dmajkic/redis/downloads&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;解压后所有命令均在一个目录下服务器端直接运行redis-server.exe即可如果需要特殊配置可以指定配置文件或者使用redis-client进行配置。配置文件其实就是配置命令的一个集合。redis的配置命令2.2之后可以做到在不重启服务的情况下生效包括redis版本更新slave转为master等等。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Redis的Python接口目前比较成熟的是redis-py项目地址为&lt;a href=&#34;https://github.com/andymccurdy/redis-py通过git可将源码下载到本地git的安装和使用请参考[《Git使用123》](http://www.lunny.info/html/2011/01/24/625330.html)。&#34;&gt;https://github.com/andymccurdy/redis-py通过git可将源码下载到本地git的安装和使用请参考[《Git使用123》](http://www.lunny.info/html/2011/01/24/625330.html)。&lt;/a&gt;&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;git clone https://github.com/andymccurdy/redis-py.git&#xA;cd redis-py&#xA;python setup install&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;redis-py的使用请参考&lt;a href=&#34;https://github.com/andymccurdy/redis-py&#34;&gt;https://github.com/andymccurdy/redis-py&lt;/a&gt;&lt;/p&gt;&#xA;</description>
<pubDate>Tue, 17 May 2011 07:42:30 +0000</pubDate>
</item>
<item>
<title>一个Python函数执行MySQL的SQL文件</title>
<link>http://lunny.info/2011/4/29/一个Python函数执行MySQL的SQL文件.html</link>
<description>&lt;p&gt;有些时候我们需要通过python来执行SQL文件那么这个函数就有用武之地了。调用之前确保安装了mysql。Linux和Windows应该是都可以用的Linux下没有测试过。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-python&#34;&gt;from subprocess import Popen, PIPE&#xA;&#xA;def excSQLFile(host, db, user, passwd, charset, filename):&#xA; process = Popen(&#39;mysql -h%s -D%s -u%s -p%s --default-character-set=%s&#39; \&#xA; % (host, db, user, passwd, charset),&#xA; stdout=PIPE, stdin=PIPE, shell=True)&#xA; output = process.communicate(&#39;source &#39; + filename)[0]&#xA; return output&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;</description>
<pubDate>Fri, 29 Apr 2011 07:30:26 +0000</pubDate>
</item>
<item>
<title>在输入框/搜索框中显示提示文字的JS效果</title>
<link>http://lunny.info/2011/4/26/在输入框/搜索框中显示提示文字的JS效果.html</link>
<description>&lt;p&gt;我敢说,做前端的十个里八个做过这个。今天把我的做法记录一下哈,直接上代码:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;function trim(str){&#xA; return str.replace(/^\s*(.*?)[\s\n]*$/g,&#39;$1&#39;)&#xA;}&#xA;var ic = false&#xA;function a(){return document.getElementById(&amp;quot;a&amp;quot;)}&#xA;&#xA;function cha() {return trim(a().value).length &amp;gt; 0}&#xA;&#xA;function ifblur() {&#xA; ic = cha()&#xA; if (!ic) {&#xA; a().value = &amp;quot;我是提示文字&amp;quot;&#xA; a().style.color = &amp;quot;gray&amp;quot;&#xA; }&#xA; }&#xA;&#xA;function iffocus() {&#xA; if (!ic) {&#xA; a().value = &amp;quot;&amp;quot;&#xA; a().style.color = &amp;quot;black&amp;quot;&#xA; ic = true&#xA; }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;</description>
<pubDate>Tue, 26 Apr 2011 06:14:43 +0000</pubDate>
</item>
<item>
<title>手写JS代码十行搞定ajax异步请求</title>
<link>http://lunny.info/2011/4/26/手写JS代码十行搞定ajax异步请求.html</link>
<description>&lt;p&gt;据说要创业的人都必须会讲故事,好吧,我开始忽悠了。&#xA;开始的开始有一个需求要用非常少的js代码从后台获得数据。然后听说要用到ajax听说jquery很小很好用结果下载下来一看。。。xxKB。不是吧比页面本身还大。杀鸡焉用牛刀于是google百度复制粘贴就有了下面这段代码&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-javascript&#34;&gt;function handler() {&#xA; if(this.readyState == 4 &amp;amp;&amp;amp; this.status == 200 &amp;amp;&amp;amp; this.responseText != null) {&#xA; obj = eval(&#39;(&#39; + this.responseText + &#39;)&#39;)&#xA; alert(obj)&#xA; }&#xA;}&#xA;function getCnt() {&#xA; var objXmlHttp = null&#xA; if(window.XMLHttpRequest){&#xA; objXmlHttp = new XMLHttpRequest()&#xA; }else if(window.ActiveXObject){&#xA; try{objXmlHttp = new ActiveXObject(&amp;quot;Msxml2.XMLHTTP&amp;quot;)}&#xA; catch(failed){&#xA; try{objXmlHttp = new ActiveXObject(&amp;quot;Microsoft.XMLHTTP&amp;quot;)}catch(failed){}&#xA; }&#xA; }&#xA; if (objXmlHttp) {&#xA; objXmlHttp.onreadystatechange = handler&#xA; objXmlHttp.open(&amp;quot;GET&amp;quot;, &amp;quot;/url?v=&amp;quot;+(new Date()).valueOf(), true)&#xA; objXmlHttp.send()&#xA; }&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;最后的最后说十行夸张了点不过基本上已经很小了一般的使用应该没什么问题了。至于浏览器兼容性嘛绝大部分应该支持。ie7及chrome dev至少是经过验证的。&#xA;参考请见:&#xA;&lt;a href=&#34;http://www.w3.org/TR/XMLHttpRequest/&#34;&gt;http://www.w3.org/TR/XMLHttpRequest/&lt;/a&gt;&lt;/p&gt;&#xA;</description>
<pubDate>Tue, 26 Apr 2011 05:54:57 +0000</pubDate>
</item>
<item>
<title>编译Pandion</title>
<link>http://lunny.info/2011/3/11/编译Pandion.html</link>
<description>&lt;p&gt;Pandion潘迪安是一款Windows平台下的XMPP客户端因其界面比较简洁所以引起了我的兴趣。详细信息可访问&lt;a href=&#34;http://pandion.im/&#34;&gt;http://pandion.im/&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;编译之前,需要先安装些软件:&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;p&gt;VC++ Express 2010&#xA;&lt;a href=&#34;http://download.microsoft.com/download/5/c/1/5c156922-ca10-49d8-b7e7-9bf092c3b6eb/VS2010ExpressCHS.iso&#34;&gt;http://download.microsoft.com/download/5/c/1/5c156922-ca10-49d8-b7e7-9bf092c3b6eb/VS2010ExpressCHS.iso&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;Wix 3.5&#xA;&lt;a href=&#34;http://wix.sf.net&#34;&gt;http://wix.sf.net&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;RoboCopy如果是Vista之前的Windows版本默认没有robocopy这个命令可下载Windows Server 2003 Resource Kit Tools安装好后就有了。&#xA;&lt;a href=&#34;http://www.microsoft.com/downloads/en/confirmation.aspx?familyid=9d467a69-57ff-4ae7-96ee-b18c4790cffd&amp;amp;displaylang=en&#34;&gt;http://www.microsoft.com/downloads/en/confirmation.aspx?familyid=9d467a69-57ff-4ae7-96ee-b18c4790cffd&amp;amp;displaylang=en&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;Git因为pandion的源码是存放在github的所以需要用git来获取&lt;/p&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;下面是编译过程:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;1.安装以上软件,基本上都是“下一步”;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;2.设置环境变量%VS100COMNTOOLS%到&amp;rdquo;c:\program files\microsoft visual studio 10.0\common7\tools\&amp;rdquo;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;3.打开git bash输入命令&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;git clone https://github.com/pandion/pandion.git&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;4.找到源码执行build_all.bat即可编译成功后在Installer\Wix下会生成一个安装包文件Pandion_2.6.0.msi&lt;/p&gt;&#xA;&#xA;&lt;p&gt;5.编译过程出现一个警告和2个错误未详究暂未发现影响。&lt;/p&gt;&#xA;</description>
<pubDate>Fri, 11 Mar 2011 04:35:56 +0000</pubDate>
</item>
<item>
<title>Git使用123之</title>
<link>http://lunny.info/2011/3/8/Git使用123之.html</link>
<description>&lt;p&gt;1.如果使用ssh非共享账号方式使用Git时在Git库初始化时请加上--shared的参数加上这个参数会自动将所有生成的文件或目录设置为库根目录所在的组可读可写。否则可能会遇到一个账号push的内容另一个账号无法修改的问题。如果已经设置好了库也可使用&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;git init --bare --shared&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;来设置权限这个命令是初始化或重新初始化不会删除git库的内容。但这个命令似乎只对以后的内容权限有改变对于以前的内容可自己修改下权限。&#xA;2.git reset可恢复到某个commit但也有可能会删掉未跟踪的文件用时要小心。&#xA;3.在MyEclipse上安装Egit时最好是下载离线安装包放到dropins下然后重启MyEclipse否则经常会更新失败。如果找不到下载的地方可以自己下载源码进行编译。&#xA;4.git bash或者Egit插件使用私钥登录的问题打开git bash输入&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;ssh-keygen&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;然后yes和回车再输入:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;scp ~/.ssh/id_rsa yourname@yourserver:~/.ssh/authorized_keys&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;*此方法要求安装git时选择的是openssh而不是putty。&#xA;MyEclipse中打开Preference搜索ssh选择“load private key”找到私钥存放的目录即可。&#xA;如果不知道可在git bash下输入&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;cd ~/.ssh&#xA;pwd&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;</description>
<pubDate>Tue, 08 Mar 2011 01:42:05 +0000</pubDate>
</item>
<item>
<title>Tornado在Windows下解析文件mime类型错误一则及解决</title>
<link>http://lunny.info/2011/2/15/Tornado在Windows下解析文件mime类型错误一则及解决.html</link>
<description>&lt;p&gt;今日遇到一则错误tornado服务器在解析文件的mime类型时报错无法获取错误如下。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;codeUnicodeDecodeError: &#39;ascii&#39; codec can&#39;t decode byte 0xe4 in position 0&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;出错误原因:&#xA;Windows注册表中保存的mime类型对应项中包含非ascii字符&lt;/p&gt;&#xA;&#xA;&lt;p&gt;解决方法:&#xA;执行regedit找到&#xA;&lt;code&gt;HKEY_LOCAL_MACHINE\SOFTWARE\Classes\MIME\Database\Content Type&lt;/code&gt;&#xA;在其中找到非英文字母的mime类型例如我遇到的“视频/x-m4v”删除即可。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;如何避免:&#xA;有些程序在安装时会新增一些mime类型如果胡乱加那么肯定就会出问题。我遇到的这个问题估计是哪个视频播放软件犯了一个低级错误不该翻译的地方翻译了。这个错误一般情况下不会有什么危害真正除了问题却有可能摸不着头脑。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;附:&#xA;mime类型标准的网址&lt;a href=&#34;http://www.iana.org/assignments/media-types/&#34;&gt;http://www.iana.org/assignments/media-types/&lt;/a&gt;。访问可以看到顶级类型里不包含“视频”只有“vedio&amp;rdquo;。&lt;/p&gt;&#xA;</description>
<pubDate>Tue, 15 Feb 2011 08:13:06 +0000</pubDate>
</item>
<item>
<title>Git使用123</title>
<link>http://lunny.info/2011/1/24/Git使用123.html</link>
<description>&lt;ol&gt;&#xA;&lt;li&gt;Git是一个分布式的源代码管理软件相较于其它类似软件有很多特性。具体的介绍及下载请访问官网&lt;a href=&#34;http://git-scm.com&#34;&gt;http://git-scm.com&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;学习Git的几个网址&#xA;1官方文档&lt;a href=&#34;http://git-scm.com/documentation&#34;&gt;http://git-scm.com/documentation&lt;/a&gt;&#xA;2Pro Git &lt;a href=&#34;http://progit.org/book/&#34;&gt;http://progit.org/book/&lt;/a&gt; 简体中文版:&lt;a href=&#34;http://progit.org/book/zh/&#34;&gt;http://progit.org/book/zh/&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;p&gt;基本上就够用了,下面只是简单记录下常用命令:&#xA;1. 在服务器上创建一个库:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;mkdir src.git&#xA;cd src.git&#xA;git init --bare&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;OK. 这就好了。&#xA;2. 从服务器上Clone代码库到本地&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;git clone lunny@myserver:/home/lunny/src.git&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;这会通过ssh协议从远程拷贝代码库到本地的src.git目录我们的源代码就可以放到这个目录下了。&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;可以将需要管理的文件都放到这个目录下。git将文件分为四种状态&#xA;[untracked]:在代码库目录下,但是没有进行跟踪的文件或目录&#xA;[unmodified]:在上一次版本基础上未修改过的文件&#xA;[modified]:在上一次版本基础上修改过的文件&#xA;[staged]:已经标记为需要在下一次提交中上传的文件&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;p&gt;使用git status可查看当前的文件跟踪状态请经常此命令进行查看。&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;跟踪新文件或新修改用&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;git add [filename]&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;如果要添加所有新增或修改过的文件可使用&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;git add .&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;从已跟踪里面删除不需要跟踪的文件用&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;git rm [filename]&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;不需要跟踪的文件,可建立.gitignore文件并将*.bak等类似语句加入进行屏蔽&lt;/li&gt;&#xA;&lt;li&gt;如果所有需要进行跟踪的文件都已添加好,则可进行一次提交&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;git commit -m &amp;quot;add a file&amp;quot;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;提交好后,如果需要推送到远程服务器上:&#xA;&lt;code&gt;sh&#xA;git push origin master&#xA;&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;如果服务器上其他人有更新,本地要进行更新:&#xA;&lt;code&gt;sh&#xA;git pull&#xA;&lt;/code&gt;&#xA;刚开始,有这几步就够用了。如果要进一步了解,特别是分支的概念,可以访问前面提到的两个网址。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;</description>
<pubDate>Mon, 24 Jan 2011 05:32:08 +0000</pubDate>
</item>
<item>
<title>加密软件TrueCrypt安装及使用简介</title>
<link>http://lunny.info/2010/12/21/加密软件TrueCrypt安装及使用简介.html</link>
<description>&lt;h2 id=&#34;3-4-创建分区-u盘加密卷&#34;&gt;3.4   创建分区/U盘加密卷&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;创建U盘加密卷和创建文件型加密卷过程大致相同。如图3-15所示在主界面选择【创建加密卷】后选择【加密非系统分区/设备】。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-15.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-15.jpg&#34; alt=&#34;3-15&#34; title=&#34;3-15&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-15&lt;/p&gt;&#xA;&#xA;&lt;p&gt;点击【下一步】将出现卷类型的界面这个和文件型卷是相同的。这里我们以“隐藏的TrueCrypt”为例进行讲解。如图3-16所示。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-16.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-16.jpg&#34; alt=&#34;3-16&#34; title=&#34;3-16&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-16&lt;/p&gt;&#xA;&#xA;&lt;p&gt;标准卷和隐藏卷是有区别的:隐藏卷实际会创建两个卷,会要求输入两次密码,第一个卷和第一次密码和标准卷一样。第二个密码是给隐藏卷的。当在使用时输入第一个密码,则会加载标准卷,当输入的密码是第二个密码,则会显示隐藏卷。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;点击【下一步】之后将到一个加密卷模式选择界面,这个界面在选择【隐藏卷】时有,在选择【标准卷】时是没有的。这里可以选择【常规模式】,即新创建卷,如果选择【直接模式】则会在已有的标准卷中新增一个隐藏卷。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-17.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-17.jpg&#34; alt=&#34;3-17&#34; title=&#34;3-17&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-17&lt;/p&gt;&#xA;&#xA;&lt;p&gt;这里我们选择【常规模式】点击【下一步】将会到一个选择加密分区或设备的界面。如图3-18所示&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-18.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-18.jpg&#34; alt=&#34;3-18&#34; title=&#34;3-18&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-18&lt;/p&gt;&#xA;&#xA;&lt;p&gt;这里可以选择加密硬盘上的一个分区或者U盘上的一个分区也可以加密整个硬盘或者整个U盘。这里有一个小提示如果加密了整个U盘那么你到一个没有安装TrueCrypt的计算机上就必须先安装好才能够将U盘挂载。所以实际上一般有两种做法&lt;/p&gt;&#xA;&#xA;&lt;p&gt;1 将U盘分成多个分区区分其中的加密分区和非加密分区。将TrueCrypt的解压版本拷贝到非加密分区中到一台新计算机上不需要安装直接运行即可。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;2 使用文件型加密卷。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;选择好设备后点击【确定】并点击【下一步】会弹出【外层加密卷】的界面先进行外层加密卷的设置如图3-19所示。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-19.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-19.jpg&#34; alt=&#34;3-19&#34; title=&#34;3-19&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-19&lt;/p&gt;&#xA;&#xA;&lt;p&gt;接下来的步骤和创建文件型加密卷并无区别,这里需要注意的是,最后一步会对磁盘分区进行格式化,所以请注意先备份该分区中原有的数据。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-20.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-20.jpg&#34; alt=&#34;3-20&#34; title=&#34;3-20&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-20&lt;/p&gt;&#xA;&#xA;&lt;p&gt;在完成之后将会进行隐藏卷的设置,设置方法和外层卷类似。这里需要注意一点,标准卷和隐藏卷的密码不要相同。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;这里有一个需要注意的地方如果一个分区是1G大小那么外层卷会显示为1G隐藏卷会显示为你设置的大小。不要在外层卷放太多文件当使用太大时会破坏隐藏卷。&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;3-5-使用分区-u盘加密卷&#34;&gt;3.5   使用分区/U盘加密卷&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;当分区或U盘加密卷创建好之后就可以使用了。如图3-21所示点击选择设备选择加密的分区或者U盘然后点击载入将会弹出密码输入框后面步骤和文件型加密卷相同。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;这里有一点需要注意的,如果创建了隐藏卷。那么输入标准卷密码程序时就加载标准卷;输入隐藏卷密码就加载隐藏卷。如果加载了一个卷,就不能加载另一个卷了。两个卷同时只能加载一个。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-21.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-21.jpg&#34; alt=&#34;3-21&#34; title=&#34;3-21&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-21&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;3-6-加密系统启动分区或者整个硬盘&#34;&gt;3.6   加密系统启动分区或者整个硬盘&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;加密方式基本和加密分区相同不同的是加密完成后在启动Windows之前会要求输入密码否则无法启动Windows。&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;3-7-修改加密卷密码&#34;&gt;3.7   修改加密卷密码&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;在加密卷已经载入后,点击界面上的【加密卷工具】,将会弹出一个菜单,第一个菜单项即为修改加密卷密码。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-22.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-22.jpg&#34; alt=&#34;3-22&#34; title=&#34;3-22&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-22&lt;/p&gt;&#xA;</description>
<pubDate>Tue, 21 Dec 2010 08:16:40 +0000</pubDate>
</item>
<item>
<title>加密软件TrueCrypt安装及使用简介</title>
<link>http://lunny.info/2010/12/21/加密软件TrueCrypt安装及使用简介.html</link>
<description>&lt;h1 id=&#34;3-truecrypt使用&#34;&gt;3       TrueCrypt使用&lt;/h1&gt;&#xA;&#xA;&lt;h2 id=&#34;3-1-基本原理&#34;&gt;3.1   基本原理&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;TrueCrypt在使用时类似于虚拟光驱之类的软件。首先将加密的卷文件、或者加密的U盘加密的分区等等都加载为某个Windows盘符然后就跟使用本地分区一样进行使用就可以了。使用完成之后再卸载掉。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;这里需要注意TrueCrypt的加密方式是先创建加密容器加密卷或者分区、设备然后将需要加密的内容存放到这个加密容器中。&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;3-2-创建文件加密卷&#34;&gt;3.2   创建文件加密卷&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;TrueCrypt启动后默认在Windows通知区有一个小图标点击该图标将显示TrueCrypt的主界面如图3-1所示。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-1.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-1.jpg&#34; alt=&#34;3-1&#34; title=&#34;3-1&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-1&lt;/p&gt;&#xA;&#xA;&lt;p&gt;点击【创建加密卷】将显示加密卷创建向导如图3-2所示&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-2.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-2.jpg&#34; alt=&#34;3-2&#34; title=&#34;3-2&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-2&lt;/p&gt;&#xA;&#xA;&lt;p&gt;加密卷有三种,分别为:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;1  文件型:即在磁盘中创建一个加密文件;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;2  加密非系统分区/设备即将某个非系统分区或者加密U盘等移动设备变成加密卷&lt;/p&gt;&#xA;&#xA;&lt;p&gt;3  加密系统分区或整个硬盘:即将硬盘某个系统分区或者整个硬盘加密。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;这里我们选择【创建文件型加密卷】点击之后会出现图3-3的界面。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-3.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/12/3-3.jpg&#34; alt=&#34;3-3&#34; title=&#34;3-3&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-3&lt;/p&gt;&#xA;&#xA;&lt;p&gt;点击【下一步】后如图3-4所示&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-4.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-4.jpg&#34; alt=&#34;3-4&#34; title=&#34;3-4&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-4&lt;/p&gt;&#xA;&#xA;&lt;p&gt;将要求选择一个文件来作为加密卷文件,这个文件的扩展名可随意,最好是用一个不容易被认出是加密文件的名称。这里要特别注意:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;1  不要选择一个已经存在的文件,否则该文件将会被覆盖。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;2  不要自己误删除这个文件,否则加密内容将丢失,无法恢复。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-5.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-5.jpg&#34; alt=&#34;3-5&#34; title=&#34;3-5&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-5&lt;/p&gt;&#xA;&#xA;&lt;p&gt;文件选择好之后会出现【加密选项】界面如图3-6所示在这里可以选择加密的算法一般选择默认即可。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-6.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-6.jpg&#34; alt=&#34;3-6&#34; title=&#34;3-6&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-6&lt;/p&gt;&#xA;&#xA;&lt;p&gt;点击【下一步】会出现输入文件加密卷大小的界面这里我们可以根据自己的需求输入一个值。除非有大文件需要存一般都不要设置太大方便保存到U盘中或文件服务器中。如果一个文件满了那么可以再建一个也可根据规则比如一个月一个文件或者一年。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-7.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-7.jpg&#34; alt=&#34;3-7&#34; title=&#34;3-7&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-7&lt;/p&gt;&#xA;&#xA;&lt;p&gt;点击【下一步】要求输入加密卷的密码图3-8这里密码一定要记牢。如果忘记了密码加密卷中的文件基本没有找回来的可能。同时这里也可以采用密钥文件的形式如果使用了文件密钥注意文件密钥一定要保存好如果丢失加密卷也无法打开。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-8.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-8.jpg&#34; alt=&#34;3-8&#34; title=&#34;3-8&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-8&lt;/p&gt;&#xA;&#xA;&lt;p&gt;密码输入完毕后将会到最后一步进行加密卷的格式化。这里可以选择FAT格式和NTFS格式。如果小于4G建议用FAT这个文件格式可以在Linux和MacOS中读写。如果选择NTFS则大小可以超出4G但是只能在Windows系列操作系统之间读写Linux中默认可以读取需要修改配置允许写入才可以写MacOS中默认无法读取和写入。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-9.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-9.jpg&#34; alt=&#34;3-9&#34; title=&#34;3-9&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-9&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-10.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-10.jpg&#34; alt=&#34;3-10&#34; title=&#34;3-10&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-10&lt;/p&gt;&#xA;&#xA;&lt;p&gt;至此,文件型加密卷创建完成,我们可以来使用了。&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;3-3-使用文件加密卷&#34;&gt;3.3   使用文件加密卷&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;文件加密卷创建完成后就跟普通文件类似可以像普通文件那样复制粘贴。在需要使用时必须通过TrueCrypt进行挂载。挂载的方式如图3-11所示&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-11.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-11.jpg&#34; alt=&#34;3-11&#34; title=&#34;3-11&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-11&lt;/p&gt;&#xA;&#xA;&lt;p&gt;在盘符列表框中选择某个盘符例如选择M然后在【加密卷】下的输入框中输入或点击【选择文件】选中刚才创建的加密卷文件点击【载入】将会弹出密码输入框如图3-12所示。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-12.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-12.jpg&#34; alt=&#34;3-12&#34; title=&#34;3-12&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-12&lt;/p&gt;&#xA;&#xA;&lt;p&gt;输入此加密卷文件的密码如果生成了密钥文件选择密钥文件的路径。点击确定如果密码正确系统将会将加密卷中的内容载入到M盘中。如图3-13所示在M盘上点击右键选择打开或者通过windows资源管理器选择M盘如图3-14所示均可打开。此时可以对M盘进行读写操作和使用普通的分区没有任何区别。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-13.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-13.jpg&#34; alt=&#34;3-13&#34; title=&#34;3-13&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-13&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/3-14.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/3-14.jpg&#34; alt=&#34;3-14&#34; title=&#34;3-14&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图3-14&lt;/p&gt;&#xA;</description>
<pubDate>Tue, 21 Dec 2010 08:14:49 +0000</pubDate>
</item>
<item>
<title>加密软件TrueCrypt安装及使用简介</title>
<link>http://lunny.info/2010/12/21/加密软件TrueCrypt安装及使用简介.html</link>
<description>&lt;h1 id=&#34;1-简介&#34;&gt;&lt;strong&gt;1       简介&lt;/strong&gt;&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;TrueCrypt是一款开源免费的数据加密软件它支持如下特性&lt;/p&gt;&#xA;&#xA;&lt;p&gt;1  支持Windows 7/Vista/XP, Mac OS X, Linux等操作系统加密文件或者设备可在不同操作系统间使用&lt;/p&gt;&#xA;&#xA;&lt;p&gt;2  可以选择AES、Serpent和Twofish等算法也可以组合他们使用暴力破解的难度非常高&lt;/p&gt;&#xA;&#xA;&lt;p&gt;3  支持加密文件加密硬盘分区加密整个硬盘加密U盘等移动设备等&lt;/p&gt;&#xA;&#xA;&lt;p&gt;4  支持密码认证和私钥文件认证;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;5  支持双加密卷,即普通卷和隐藏卷。&lt;/p&gt;&#xA;&#xA;&lt;h1 id=&#34;2-安装及配置&#34;&gt;2       安装及配置&lt;/h1&gt;&#xA;&#xA;&lt;h2 id=&#34;2-1-安装truecrypt&#34;&gt;2.1   安装TrueCrypt&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;TrueCrypt在Windows上的安装很简单一直下一步即可。双击安装程序如图1-1将显示TrueCrypt的使用许可此时选择接受。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/2-1.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/2-1.jpg&#34; alt=&#34;2-1&#34; title=&#34;2-1&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图2-1&lt;/p&gt;&#xA;&#xA;&lt;p&gt;接下来询问你是用安装版本还是解压版本如果我们是安装在台式电脑上那么我们选择安装见图2-2如果是选择将软件放到U盘中方便在别的地方试用则选择解压。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/2-2.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/2-2.jpg&#34; alt=&#34;2-2&#34; title=&#34;2-2&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图2-2&lt;/p&gt;&#xA;&#xA;&lt;p&gt;点击下一步会出现安装选项图2-3这一步可以选择给所有用户安装或者只安装个当前用户另外创建系统还原点可以不选。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/2-3.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/2-3.jpg&#34; alt=&#34;2-3&#34; title=&#34;2-3&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图2-3&lt;/p&gt;&#xA;&#xA;&lt;p&gt;点击安装系统将显示安装日志过一会儿安装完成如图2-4所示&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/2-4.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/2-4.jpg&#34; alt=&#34;2-4&#34; title=&#34;2-4&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图2-4&lt;/p&gt;&#xA;&#xA;&lt;p&gt;此时安装已经完成,会有一个快捷方式在桌面上显示。&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;2-2-配置简体中文界面&#34;&gt;2.2   配置简体中文界面&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;TrueCrypt支持多语言它是通过不同的语言文件来达成多语言的。已经下载了语言文件可以直接跳到2.2.2。&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;2-2-1-下载简体中文语言包&#34;&gt;2.2.1 下载简体中文语言包&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;通过TrueCrypt界面可以直接链接到语言包下载网址步骤如图2-5所示点击菜单栏上的【Settings】选择【Language】将显示语言切换界面如图2-6所示。点击界面上的【download language pack】将会打开语言包下载网址。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/2-5.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/2-5.jpg&#34; alt=&#34;2-5&#34; title=&#34;2-5&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图2-5&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/2-6.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/2-6.jpg&#34; alt=&#34;2-6&#34; title=&#34;2-6&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图2-6&lt;/p&gt;&#xA;&#xA;&lt;p&gt;将下载到的语言包文件解压将Language.zh-cn.xml文件拷贝到c:\program files\TrueCrypt文件夹下。关闭并重新打开TrueCrypt。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/2-7.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/2-7.jpg&#34; alt=&#34;2-7&#34; title=&#34;2-7&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图2-7&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;2-2-1将界面语言修改为简体中文&#34;&gt;2.2.1将界面语言修改为简体中文&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;点击菜单栏上的【Settings】选择【Language】将显示语言切换界面此时选择【简体中文】点击【OK】即可。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2010/12/2-8.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2010/12/2-8.jpg&#34; alt=&#34;2-8&#34; title=&#34;2-8&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;图2-8&lt;/p&gt;&#xA;</description>
<pubDate>Tue, 21 Dec 2010 08:12:02 +0000</pubDate>
</item>
<item>
<title>博客加入静态化及缓存机制,提升访问速度</title>
<link>http://lunny.info/2010/12/20/博客加入静态化及缓存机制,提升访问速度.html</link>
<description>&lt;p&gt;博客访问速度很慢256M的VPS还在美国只好做做优化了。本次优化只是将首页和日志页进行了静态化。下面记录下步骤&#xA;1.登录WordPress后台在“永久链接”里选择自定义改为“/html/%year%/%monthnum%/%day%/%post_id%.html”保存。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;2.修改nginx的配置加入以下rewrite配置加入的位置我是在server_name之后&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-nginx&#34;&gt;if (!-e $request_filename) {&#xA;rewrite ^([_0-9a-zA-Z-]+)?(/wp-.*) $2 last;&#xA;rewrite ^([_0-9a-zA-Z-]+)?(/.*.php)$ $2 last;&#xA;rewrite ^ /index.php last;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;3.安装Cos-Html-Cache插件并启用&#xA;同时需要&#xA;3.1 在wordpress的根目录创建index.bak并执行chmod 0666 index.bak&#xA;3.2 在wordpress的根目录创建html目录并执行chmod 0777 html&lt;/p&gt;&#xA;&#xA;&lt;p&gt;4.重新生成sitemap并提交给各搜索引擎&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Over.&lt;/p&gt;&#xA;</description>
<pubDate>Mon, 20 Dec 2010 08:41:27 +0000</pubDate>
</item>
<item>
<title>玲珑小巧的桌面搜索工具 - Everything</title>
<link>http://lunny.info/2010/12/17/玲珑小巧的桌面搜索工具---Everything.html</link>
<description>&lt;p&gt;如果你是一个条理非常清晰的人可能会记得自己电脑里面所有的文件的存放地点否则也许你会希望借助搜索来找到你的文件。很不幸的是Windows里面的文件搜索功能不是一般的差自从google桌面发布后类似的产品很多很多了不过大多为重量级产品。今天介绍的这款软件Everything大小只有几百K而且搜索速度飞快支持即时搜索看来这个google推出的新玩意其实早有人做了。并且支持绿色安装直接下载解压即可。不多说了下载试试就知道了。下载地址&lt;a href=&#34;http://www.voidtools.com/&#34;&gt;http://www.voidtools.com/&lt;/a&gt;&lt;/p&gt;&#xA;</description>
<pubDate>Fri, 17 Dec 2010 03:09:02 +0000</pubDate>
</item>
<item>
<title>Hibernate中映射枚举类型</title>
<link>http://lunny.info/2010/12/3/Hibernate中映射枚举类型.html</link>
<description>&lt;p&gt;Hibernate中提供了org.hibernate.type.EnumType类来进行枚举类型的映射可以将枚举实例的name或者ordinal映射到数据库具体在HBM文件中配置如下&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;property name=&amp;quot;bank&amp;quot;&amp;gt;&#xA; &amp;lt;column name=&amp;quot;BANK_MARK&amp;quot; length=&amp;quot;20&amp;quot; not-null=&amp;quot;true&amp;quot;/&amp;gt;&#xA; &amp;lt;type name=&amp;quot;org.hibernate.type.EnumType&amp;quot;&amp;gt;&#xA; &amp;lt;param name=&amp;quot;enumClass&amp;quot;&amp;gt;com.lunny.Bank&amp;lt;/param&amp;gt;&#xA; &amp;lt;param name=&amp;quot;type&amp;quot;&amp;gt;12&amp;lt;/param&amp;gt;&#xA; &amp;lt;/type&amp;gt;&#xA;&amp;lt;/property&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;其中type的值指的java.sql.Types.*的某个值12对应的是VARCHAR表示将枚举的name存到数据库中。默认是存的ordinal也可指明4即Integer。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;OK。问题来了如果枚举用的是一个自定义的数值或字符来分开的如何映射我的做法是看看EnumType的源代码自己写一个本来是想继承的但是发现很多方法都是private基本无法重用。主要的改变有如下几点&#xA;1.加入成员变量&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-java&#34;&gt;public static final String KEY = &amp;quot;key&amp;quot;;&#xA;private Getter getter;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;其中的Getter是枚举中获取key值的方法&lt;/p&gt;&#xA;&#xA;&lt;p&gt;2.重写nullSafeGet nullSafeSetsetParameterValuesobjectToSQLStringtoXMLStringfromXMLString这几个方法。&#xA;2.1 在setParameterValues方法中增加新的配置项解析用于解析Key并将key的Getter方法保存起来。&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-xml&#34;&gt;&amp;lt;property name=&amp;quot;bank&amp;quot;&amp;gt;&#xA; &amp;lt;column name=&amp;quot;BANK_MARK&amp;quot; length=&amp;quot;20&amp;quot; not-null=&amp;quot;true&amp;quot; /&amp;gt;&#xA; &amp;lt;type name=&amp;quot;org.hibernate.type.EnumType&amp;quot;&amp;gt;&#xA; &amp;lt;param name=&amp;quot;enumClass&amp;quot;&amp;gt;com.lunny.Bank&amp;lt;/param&amp;gt;&#xA; &amp;lt;param name=&amp;quot;type&amp;quot;&amp;gt;12&amp;lt;/param&amp;gt;&#xA; &amp;lt;param name=&amp;quot;key&amp;quot;&amp;gt;key&amp;lt;/param&amp;gt;&#xA; &amp;lt;/type&amp;gt;&#xA;&amp;lt;/property&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;2.2 在上述其它方法中加入判断Getter是否为空如果不为空则调用该方法来获得要存入数据库的值从而替代name或者ordianl。&lt;/p&gt;&#xA;</description>
<pubDate>Fri, 03 Dec 2010 03:34:49 +0000</pubDate>
</item>
<item>
<title>启用新域名lunny.info</title>
<link>http://lunny.info/2010/11/22/启用新域名lunny.info.html</link>
<description>&lt;p&gt;原先的域名是lunny.org想来想去我可不是什么组织就是一个人所以lunny.me或者lunny.info还比较符合可惜lunny.me已经被注册了。所以以后就用lunny.info这个域名了。&lt;/p&gt;&#xA;</description>
<pubDate>Mon, 22 Nov 2010 01:51:10 +0000</pubDate>
</item>
<item>
<title>发布Chrome扩展Google Reader RSS Subscriber Plus</title>
<link>http://lunny.info/2010/11/22/发布Chrome扩展Google-Reader-RSS-Subscriber-Plus.html</link>
<description>&lt;p&gt;写这个扩展是因为原先一直用的Google Reader RSS Subscriber扩展基本停止更新了几个必须的关键特性不支持只好自己动手丰衣足食。扩展地址为&lt;a href=&#34;https://chrome.google.com/extensions/detail/ngjinemddlnikfdlbnbdbajdhagbcjmg&#34;&gt;https://chrome.google.com/extensions/detail/ngjinemddlnikfdlbnbdbajdhagbcjmg&lt;/a&gt;。支持如下特性一键订阅支持https支持当前页支持订阅检测支持黑名单。&lt;/p&gt;&#xA;</description>
<pubDate>Mon, 22 Nov 2010 01:45:27 +0000</pubDate>
</item>
<item>
<title>Go语言的简介及Ubuntu下的安装和编译</title>
<link>http://lunny.info/2010/1/12/Go语言的简介及Ubuntu下的安装和编译.html</link>
<description>&lt;p&gt;&lt;strong&gt;简介&lt;/strong&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;GO语言是Google基于BSD发布的开源系统级编程语言目标是融合Python的开发效率和C的运行时效率于一体。该项目的网址是&lt;a href=&#34;http://golang.org。目前只支持Linuxfreebsd和Mac&#34;&gt;http://golang.org。目前只支持Linuxfreebsd和Mac&lt;/a&gt; OS X平台的amd64和386架构。&lt;/p&gt;&#xA;&#xA;&lt;h1 id=&#34;安装&#34;&gt;安装&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;有一个快速的编译器安装说明,见&lt;a href=&#34;http://golang.org/doc/install.html&#34;&gt;http://golang.org/doc/install.html&lt;/a&gt;。我在虚拟机中的Ubuntu9.10下安装过程如下:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;1.设置环境变量&lt;/p&gt;&#xA;&#xA;&lt;p&gt;一共需要设置4个变量&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;export GOROOT=$HOME/go&#xA;&#xA;export GOARCH=386&#xA;&#xA;export GOOS=linux&#xA;&#xA;export GOBIN=$HOME/bin&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;如果你的平台是AMD64请将GOARCH替换成amd64另外GOBIN是可选的可以&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;mkdir ~/bin&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;这里是存放GO语言编译器连接器的目录需要加入到PATH&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;PATH=${PATH}:$HOME/bin&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;将以上这行和上面4个export拷贝到你的.bashrc中。重新打开终端窗口。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;2.获取GO源码&lt;/p&gt;&#xA;&#xA;&lt;p&gt;GO使用C写的需要获取源码后编译在命令行执行以下命令&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;apt-get install python-setuptools python-dev&#xA;&#xA;sudo easy_install mercurial&#xA;&#xA;hg clone -r release https://go.googlecode.com/hg/ $GOROOT&#xA;&#xA;sudo apt-get install bison gcc libc6-dev ed make&#xA;&#xA;cd $GOROOT/src&#xA;&#xA;make all&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;等到出现:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;--- cd ../test&#xA;&#xA;N known bugs; 0 unexpected bugs&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;N为某个数字我这里为4后编译完成。&lt;/p&gt;&#xA;&#xA;&lt;h1 id=&#34;编写hello-go&#34;&gt;编写Hello GO!&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;针对不同的架构,编译器和链接器都是不一样的:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;386对应的编译器是8g链接器是8l&lt;/p&gt;&#xA;&#xA;&lt;p&gt;amd64对应的编译器是6g链接器是6l&lt;/p&gt;&#xA;&#xA;&lt;p&gt;OK.我们来编写一个最简单的GO程序代码如下&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-go&#34;&gt;package main&#xA;&#xA;import &amp;quot;fmt&amp;quot;&#xA;&#xA;func main() {&#xA;&#xA; fmt.Printf(&amp;quot;hello, world\n&amp;quot;)&#xA;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;将上面的代码保存为hello.go并编译之&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;8g hello.go&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;编译后生成hello.8,再链接:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;8l hello.8&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;生成hello.out执行&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;./hello.out&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;终端上将显示:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;Hello, GO!&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;如果要编写大型程序Make工具依然有效。&lt;/p&gt;&#xA;&#xA;&lt;h1 id=&#34;语法及类库文档&#34;&gt;语法及类库文档&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;语法见这里:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;http://golang.org/doc/go_spec.html&#34;&gt;http://golang.org/doc/go_spec.html&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;类库见这里:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;http://golang.org/pkg/&#34;&gt;http://golang.org/pkg/&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;有用的文档:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;http://golang.org/doc/effective_go.html&#34;&gt;http://golang.org/doc/effective_go.html&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;http://golang.org/doc/go_tutorial.html&#34;&gt;http://golang.org/doc/go_tutorial.html&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;如果以前学习的C++可以参考这里:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;http://golang.org/doc/go_for_cpp_programmers.html&#34;&gt;http://golang.org/doc/go_for_cpp_programmers.html&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;h1 id=&#34;保持最新版本&#34;&gt;保持最新版本&lt;/h1&gt;&#xA;&#xA;&lt;p&gt;目前GO语言还在不断完善中还没有到可以进入生产环境的时候如果想及时更新最新的版本如下&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;hg pull&#xA;&#xA;hg update release&#xA;&#xA;make all&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;</description>
<pubDate>Tue, 12 Jan 2010 05:40:00 +0000</pubDate>
</item>
<item>
<title>Firefox 扩展 Flashblock</title>
<link>http://lunny.info/2008/3/14/Firefox-扩展-Flashblock.html</link>
<description>&lt;p&gt;现在用Flash的东东太多广告/网络视频/某些网络应用等等估计将来这个趋势也将会继续。这个Firefox扩展可以阻止&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Macromedia Flash&lt;/li&gt;&#xA;&lt;li&gt;Macromedia Shockwave&lt;/li&gt;&#xA;&lt;li&gt;Macromedia Authorware&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;等,安装页面:&#xA;&lt;a href=&#34;https://addons.mozilla.org/en-US/firefox/addon/433&#34;&gt;https://addons.mozilla.org/en-US/firefox/addon/433&lt;/a&gt;&lt;/p&gt;&#xA;</description>
<pubDate>Fri, 14 Mar 2008 14:57:54 +0000</pubDate>
</item>
<item>
<title>IpMsg for Linux(Ubuntu)安装手记</title>
<link>http://lunny.info/2007/12/9/IpMsg-for-Linux(Ubuntu)安装手记.html</link>
<description>&lt;p&gt;ipmsg是一个开源的局域网消息和文件传送工具其最大的优点是可以直接传送文件夹并且传送速度非常快。ipmsg目前已有了windows, mac, linux版本。为了从我的LinuxUbuntu 7.04传送文件到局域网内一台windows机器我试着安装了一下。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;1.先下载源码&lt;/p&gt;&#xA;&#xA;&lt;p&gt;我下载的是for gnome2版本的源码&#xA;&lt;a href=&#34;http://www.ipmsg.org/archive/g2ipmsg-0.9.1.tar.gz&#34;&gt;http://www.ipmsg.org/archive/g2ipmsg-0.9.1.tar.gz&lt;/a&gt;&#xA;目前最新的版本是0.9.3不过这个版本在编译是会出现问题所以我选择了0.9.1版本,差别不大。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;2.解压&#xA;在ubuntu中用命令行&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;tar xzvf g2ipmsg-0.9.1.tar.gz&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;或者菜单右键用归档管理器解压即可。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;3.修改语言&#xA;用文本编辑工具比如gedit打开src/codeset.c文件将其中的CP932更改为CP936英文或者GBK中文并保存。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;4.安装编译依赖项&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;sudo apt-get install libxml-parser-perl libgnomeui-dev libpanel-applet2-dev&#xA;sudo apt-get install gettext intltool&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;5.编译&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;./configure enable-systray&#xA;make&#xA;sudo make install&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;这下可用了重启后在主菜单的附件中将会有Gnome2 IP Messenger的快捷方式。&#xA;OK完成可以在windows和linux之间传送文件或者文件夹了。&lt;/p&gt;&#xA;</description>
<pubDate>Sun, 09 Dec 2007 11:55:29 +0000</pubDate>
</item>
<item>
<title>从西宁回来</title>
<link>http://lunny.info/2007/7/5/从西宁回来.html</link>
<description>&lt;p&gt;早就知道青海却忘记了它的首府是西宁。飞机要下降的时候满眼是荒山跟新疆的山有一拼不过山与山的谷地有绿色。飞机场很小好像是筑在一个高台上我都担心跑道不够长。机场高速两边的树倒是很多比我来时的想象要好。这里海拔大概在2200米将来要是海平面上涨是不是大家都得往这边跑&lt;/p&gt;&#xA;&#xA;&lt;p&gt;没想到象我这么身体好的人居然也高原反应了。跑到青海湖旁边海拔在3200米左右居然头有点晕。老实说青海湖真没什么好看的不过黄鱼的味道还不错呵呵捕捞和吃这个好像是违法。在湖边转了转看了看藏獒就往原子城走了。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2007/07/p6260141.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2007/07/p6260141.jpg&#34; alt=&#34;青海湖&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;听说半路会看到沙漠,于是半路停车跟沙漠合了一个影,没有想象中的沙漠好&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2007/07/p6260151.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2007/07/p6260151.jpg&#34; alt=&#34;青海湖边沙漠&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;原子城,金银滩基本上在一起,据说王洛宾创作《在那遥远的地方》就是在金银滩,据说卓玛在藏语里是仙女的意思,所以叫卓玛的很多,王洛宾的这首歌就是写给一位卓玛的。这里有新鲜的牦牛酸奶,不过干喝似乎不咋样,加点蜂蜜味道还不错。原子城,就是当年爆炸中国第一颗核弹和氢弹的地方,现在开放了,爱国主义教育基地,去档案馆看了看展览,看了一场小电影。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2007/07/p6260154.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2007/07/p6260154.jpg&#34; alt=&#34;原子城&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;回到西宁,高原反应好点了。睡了一晚上,高原反应彻底消失了。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;第二天,去了藏传佛教中赫赫有名的塔尔寺,据说这个寺院是黄教的发源地,创始人就是宗喀巴,号称位于如来之下的第二佛,也是达赖和班禅的师父。这些寺院建造在八个山头之间,呈现一个莲花状。虽然不信佛,却也装得象个信徒似的拜了拜,幸好可以不用跪拜。到了供香火的地方,正好手里头有硬币,于是看到有油的地方,把硬币用力摁上去,居然没掉下来,看来还不错。小金瓦殿,大金瓦殿,感觉都差不多。一个细节要讲一讲,就是这里的门槛是不能踩的,必须跨过去。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2007/07/001.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2007/07/001.jpg&#34; alt=&#34;塔尔寺&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;西宁是东西长,南北窄的一个城市,北边是山称为北山,南边也是山,称为南山。在南山的山顶有一个很醒目的建筑,据说是上海浦东出钱建的,因为浦东和西宁是友好城市?(好像区和市是不能这样称呼吧?)从这个建筑上基本上可以看全整个西宁。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2007/07/006.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2007/07/006.jpg&#34; alt=&#34;西宁全貌&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;见过这么大的土豆吗?&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2007/07/014.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2007/07/014.jpg&#34; alt=&#34;西宁的土豆&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;这里的农家乐,土鸡店生意红火,在农家院子的石桌上吃这些东西,别有一番风味,虽然环境一般般,味道还不错。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;回到上海居然已经32度并且闷热闷热的。看来象他们说的夏天呆在西宁比呆在上海好。&lt;/p&gt;&#xA;</description>
<pubDate>Thu, 05 Jul 2007 15:11:34 +0000</pubDate>
</item>
<item>
<title>犹豫间,终于还是去买了菜回来</title>
<link>http://lunny.info/2007/6/17/犹豫间,终于还是去买了菜回来.html</link>
<description>&lt;p&gt;我的懒惰还不是一般的懒,学车回来,路过菜场,为今天要不要做饭犹豫了半天。一般此种情况,最终肯定会是我的懒惰胜利,今天却出了意外!最终还是跑到菜场里面了,跟卖菜的大妈大叔们砍砍价,终于提搂着黄瓜、西红柿、辣椒、鸡蛋,还有一只烧鸡往回走了。回到家,蒸上米饭,打两个鸡蛋,切根黄瓜,切成片,捡两片尝尝,还不错。其实做饭也没有想像的复杂,除了要做得好吃之外。老婆不在身边的日子,怎么看都不像个大老爷们,什么都得自己照顾。看看我做的黄瓜炒鸡蛋。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/2007/06/00001.jpg&#34;&gt;&lt;img src=&#34;/static/media/2007/06/00001.jpg&#34; alt=&#34;黄瓜炒鸡蛋&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;</description>
<pubDate>Sun, 17 Jun 2007 11:14:39 +0000</pubDate>
</item>
<item>
<title>最近又写得少了</title>
<link>http://lunny.info/2007/6/13/最近又写得少了.html</link>
<description>&lt;p&gt;倒不是懒了,只是有很多很多的事情要去做,总也做不完,等到想到博客的时候已是该睡觉的时候了。澳大利亚之行还是要继续写的,有部分已经写了草稿,只是还未完工。最近系统升级到&lt;a href=&#34;http://www.ubuntu.org.cn&#34;&gt;Ubuntu 7.04&lt;/a&gt;了,桌面也从中文换成了英文,发一张桌面!&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/2007/06/screenshot.png&#34;&gt;&lt;img src=&#34;/static/media/2007/06/screenshot.png&#34; alt=&#34;我的桌面&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;</description>
<pubDate>Wed, 13 Jun 2007 12:27:17 +0000</pubDate>
</item>
<item>
<title>澳大利亚之行 第一天 悉尼</title>
<link>http://lunny.info/2007/5/13/澳大利亚之行-第一天-悉尼.html</link>
<description>&lt;p&gt;其实5月8日就从澳大利亚回了不过因为照片没有到我的手中所以不能完成一篇图文并茂的文章。那样肯定会是一种遗憾因此直到今天才写下这次旅行。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;首先要讲一讲这次旅行的幸运原定14人的团结果最后基本上都是六个人每次游玩都是6个人加1个领队1个当地导游1个司机。相当于VIP团了吧呵呵。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;我们的首站是在悉尼1号晚从上海浦东上飞机飞行了一夜。第二天早上九点多我们快要降落了。此时可以从飞机上看到悉尼刚开始是一片片绿色的丘陵慢慢的开始有房子了慢慢到海边时已变得密密麻麻的了但是房子都不高这点跟上海不一样。机场是在海边修建的降落的时候飞机要从海上降落到跑道上去很像航母上飞机的降落过程。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;下了飞机,还没去宾馆,就先拉到鱼市场去吃饭。在鱼市场里吃饭,没去之前肯定会觉得不可思议。去了才明白,那里跟国内的鱼市场完全不同。空气里没有丝毫的鱼腥和血腥的味道,在里面吃东西感觉非常好。这里的海鲜看起来也非常干净,让我这个在国内非常怕吃海鲜的人也放开胆子吃了起来。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/100_1669.JPG&#34;&gt;&lt;img src=&#34;/static/media/images/100_1669.JPG&#34; alt=&#34;悉尼鱼市场&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;离开鱼市场我们去了Bandi海滩。到了海滩下车之前我们都鞋子脱掉留在车上光着脚跑到海滩上去玩。这个海滩和海南的海滩差不多不过这里沙子更细天更蓝水也蓝太阳却没有海南的那么晒。并且海边有浪浪高的时候有1米多有些老外在冲浪。在这个海滩上时间只有1小时其次也没有带下水的衣服所以最终就湿了湿裤脚照了照像就往回走了。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/100_1672.JPG&#34;&gt;&lt;img src=&#34;/static/media/images/100_1672.JPG&#34; alt=&#34;悉尼bandi海滩&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;接着去的地方是海德公园海德公园大概是这个城市的一个中心因为我们后面去的几个景点都在这里绕来绕去。海德公园附近是一个很大的教堂教堂里面很高估计超过30米是我见过的最大的教堂。在海德公园的喷池附近照了照相在这个照相的地方可以看到悉尼塔似乎就在眼前这个塔是后面的一个景点。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/200705-075.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/200705-075.jpg&#34; alt=&#34;海德公园&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;离开公园我们就去吃晚饭,在一个大概可以称为唐人街的地方。我们去的是一个广东人开的馆子,老实讲味道还可以。吃完饭就往宾馆走了。我们被安排住在一个叫&lt;a href=&#34;http://www.ibishotel.com&#34;&gt;Ibis Hotel&lt;/a&gt;的地方,类似于中国的“&lt;a href=&#34;http://www.homeinns.com&#34;&gt;如家&lt;/a&gt;”这样的连锁旅店。房间不大,不过还好,比较干净。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;第一天就这样急急忙忙的结束了,一夜的飞机没休息好,所以很快就睡着了。&lt;/p&gt;&#xA;</description>
<pubDate>Sun, 13 May 2007 10:35:03 +0000</pubDate>
</item>
<item>
<title>佳能A710 IS数码相机 在 Ubuntu Linux 6.10下 设备访问问题的解决</title>
<link>http://lunny.info/2007/4/21/佳能A710-IS数码相机-在-Ubuntu-Linux-6.10下-设备访问问题的解决.html</link>
<description>&lt;p&gt;虽然7.04已经发布了不过我还没来得及更新。买了相机拍照后要将图片导入到电脑中将相机连接到usb口此时会弹出框显示检测到了数码相机在导入照片时却出现如下错误&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;io-库 (“无法请求 USB 设备”) 中出现错误Could not claim interface 0 (Operation not permitted). Make sure no other program or kernel module (such as sdc2xx, stv680, spca50x) is using the device and you have read/write access to the device.&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;Google了一下发现了一个人的博客是一个在日本的中国程序员的他的富士通的相机解决问题的文章地址如下&#xA;&lt;a href=&#34;http://my.donews.com/booker/2007/02/10/finepix_f30_camera_on_edgy_ubuntu/&#34;&gt;http://my.donews.com/booker/2007/02/10/finepix_f30_camera_on_edgy_ubuntu/&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;于是参考了一下:步骤如下:&#xA;1. 连上数码相机打开终端在终端输入lsusb可以查看到所有连接的usb设备&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2007/04//lsusb.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2007/04/lsusb.jpg&#34; alt=&#34;lsusb&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;p&gt;可以看到 Bus 001 Device 008: ID &lt;strong&gt;04a9&lt;/strong&gt;:&lt;strong&gt;3138&lt;/strong&gt; Canon, Inc&#xA;04a9,3138这两个数字是我们需要的这两个数字根据不同的数码相机会有所不同。&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;在终端中输入sudo gedit /etc/udev/rules.d/45-libgphoto2.rules&#xA;在其中加入如下行:&lt;/p&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;pre&gt;&lt;code&gt;SYSFS{idVendor}==”04a9”, SYSFS{idProduct}==”3138”, MODE=”0660″, GROUP=”plugdev”&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;保存并关闭。&lt;/p&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;然后重启udev&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;sudo /etc/init.d/udev restart&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;拔插一下usb线晕还是不行。又搜索了一下找到另一篇文章&#xA;&lt;a href=&#34;http://forum.ubuntu.pl/viewtopic.php?t=23060&#34;&gt;http://forum.ubuntu.pl/viewtopic.php?t=23060&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&#xA;&lt;p&gt;居然是pl结尾的文章XX文不认识不过命令应该都是英文的吧。在终端中输入&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-sh&#34;&gt;sudo gedit /etc/udev/rules.d/40-permissions.rules&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;修改这一行&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;SUBSYSTEM==&amp;quot;usb_device&amp;quot;, MODE=&amp;quot;0644&amp;quot;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;SUBSYSTEM==&amp;quot;usb_device&amp;quot;, MODE=&amp;quot;0666&amp;quot;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;OK!这下终于可以了。&lt;/p&gt;&#xA;</description>
<pubDate>Sat, 21 Apr 2007 14:45:58 +0000</pubDate>
</item>
<item>
<title>买了一个新的数码相机 佳能A710IS</title>
<link>http://lunny.info/2007/4/21/买了一个新的数码相机-佳能A710IS.html</link>
<description>&lt;p&gt;性能参数710w象素6倍光学变焦1/2.5英寸传感器2.5英寸液晶屏,光学防抖。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2007/04/a710is.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2007/04/a710is.jpg&#34; alt=&#34;A710IS&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;用它拍了几张照片,放一张上来瞧瞧!&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;/static/media/images/2007/04/00001.jpg&#34;&gt;&lt;img src=&#34;/static/media/images/2007/04/00001.jpg&#34; alt=&#34;A710 IS拍的照片&#34; /&gt;&lt;/a&gt;&lt;/p&gt;&#xA;</description>
<pubDate>Sat, 21 Apr 2007 14:22:49 +0000</pubDate>
</item>
<item>
<title>Ubuntu 7.04发布了</title>
<link>http://lunny.info/2007/4/20/Ubuntu-7.04发布了.html</link>
<description>&lt;p&gt;官方下载地址:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;http://releases.ubuntu.com/7.04/&#34;&gt;http://releases.ubuntu.com/7.04/&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;迅雷下载:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;a href=&#34;http://61.129.76.84/dload1.html?cid=E871B22B075BF865DD0BB89B3E013CD6F1EBE189&#34;&gt;http://61.129.76.84/dload1.html?cid=E871B22B075BF865DD0BB89B3E013CD6F1EBE189&lt;/a&gt;&lt;/p&gt;&#xA;</description>
<pubDate>Fri, 20 Apr 2007 05:08:52 +0000</pubDate>
</item>
<item>
<title>为五一去澳大利亚旅游的准备基本完成</title>
<link>http://lunny.info/2007/4/19/为五一去澳大利亚旅游的准备基本完成.html</link>
<description>&lt;p&gt;说麻烦也不麻烦,说简单也不简单。说说过程。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;1.首先需要护照。到出入境管理处办理要照相收费和交钱200RMB可以选择将办好的护照EMS到指定地点收费20多RMB只能发本省内的。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;2.然后是签证,一般跟团走,旅游团会给你一张列表,把需要准备的东东办好给它,大概如下:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;1护照最后一页要签上大名&lt;/p&gt;&#xA;&#xA;&lt;p&gt;2银行存款证明去银行办5w以上的定期三个月证明三个月内不能取这些钱&lt;/p&gt;&#xA;&#xA;&lt;p&gt;3身份证复印件/户口簿复印件(全本)&lt;/p&gt;&#xA;&#xA;&lt;p&gt;4中英文公司 的担保信(网上有格式样本。如果找不到,随便找个旅行社,让他发一个给你,呵呵),需要公司领导签字,盖章。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;5要填一张个人资料表获得途径同上&lt;/p&gt;&#xA;&#xA;&lt;p&gt;3.说说价格四月三十号的1.4w左右,五一的1.7w左右。选好了团提前订好,交一下订金。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;4.后面就是要等着了,准备去澳大利亚的东西了。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;5.据说要给当地导游小费一般给48$,得去银行换外汇,换外汇需要身份证。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;6.当地RMB不通用所以带现金最好换成澳元不过据说刷卡比较方便如果有双币的卡不用带多少现金了。&lt;/p&gt;&#xA;</description>
<pubDate>Thu, 19 Apr 2007 14:48:45 +0000</pubDate>
</item>
<item>
<title>绝好的歌曲,每个人肯定都曾经听得耳朵生茧</title>
<link>http://lunny.info/2007/4/15/绝好的歌曲,每个人肯定都曾经听得耳朵生茧.html</link>
<description>&lt;p&gt;听听就知道了。今天偶然从一个朋友的Blog中看到的&lt;a href=&#34;/static/media/2007/04/eye.wma&#34;&gt;eye.wma&lt;/a&gt;&lt;/p&gt;&#xA;</description>
<pubDate>Sun, 15 Apr 2007 14:44:15 +0000</pubDate>
</item>
<item>
<title>好用的上网小工具Google笔记本</title>
<link>http://lunny.info/2007/4/14/好用的上网小工具Google笔记本.html</link>
<description>&lt;p&gt;首先声明我跟Google没有任何关系只是觉得这个工具对我的用处还可以所以介绍介绍&lt;/p&gt;&#xA;&#xA;&lt;p&gt;当你上网之时,是否经常想把某些东西记下来,以备不时之需?这种情况估计每个人都会碰到,那么来看看这个过程是如何完成的。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;先在浏览器中复制需要的内容然后在桌面创建一个xx.txt文件打开粘贴当下次需要的时候再打开如果需要拷贝到别的地方那么使用U盘拷贝到U盘中。如果需要到没有电脑和网的地方就需要记到纸上。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Google笔记本给了另一个选择。首先我们需要安装Google笔记本在浏览器中输入网址&lt;a href=&#34;http://www.google.com/notebook&#34;&gt;http://www.google.com/notebook&lt;/a&gt;,这时会要求你输入Google帐号和密码如果你没有可以申请一个。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;登录之后你就可以下载浏览器插件了目前支持IE和firefox安装好插件重启浏览器之后它会在浏览器状态栏的最右有一个图标和文字。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&#34;http://photo.yo2.cn/photos/7/604460px.jpg&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;接下来我们就可以使用这个工具了。在浏览器复制内容后点击浏览器右下脚的“打开笔记本”就会显示“我的笔记本”我们可以将内容粘贴到其中。到了另外一个地点我们可以登录google去获得免了U盘的烦恼也免了电脑数据丢失造成的麻烦。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&#34;http://photo.yo2.cn/photos/7/605460px.jpg&#34; alt=&#34;image&#34; /&gt;&lt;/p&gt;&#xA;</description>
<pubDate>Sat, 14 Apr 2007 07:01:45 +0000</pubDate>
</item>
<item>
<title>Linux下的两款软件Picasa和Skype</title>
<link>http://lunny.info/2007/4/4/Linux下的两款软件Picasa和Skype.html</link>
<description>&lt;p&gt;我后悔了,因为这是我第二次写这篇文档。因为刚才写完后发表居然失败了。我决定要寻找一款离线博客编写软件了。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;没兴致了,简要讲讲这两个软件吧。&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;picasa&#34;&gt;picasa&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;google的东东原先是在windows下的现在也出了linux版本我用的Ubuntu 6.10在cn99的源里面有。也可以到&lt;a href=&#34;http://picasa.google.com/linux下载。刚才在我的机器上两次无响应看来还在测试阶段。其实gnome也有一款照片管理软件F-Spot跟picasa比它少了一个上传到网络相册的功能。&#34;&gt;http://picasa.google.com/linux下载。刚才在我的机器上两次无响应看来还在测试阶段。其实gnome也有一款照片管理软件F-Spot跟picasa比它少了一个上传到网络相册的功能。&lt;/a&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&#34;/static/media/images/506.jpg&#34; alt=&#34;picasa for linux&#34; title=&#34;picasa for linux&#34; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;h2 id=&#34;skype&#34;&gt;skype&lt;/h2&gt;&#xA;&#xA;&lt;p&gt;官方网站好像没有.deb的包提供下载。Ubuntu 6.10cn99的源里也有不过版本较低一点。 在ubuntu下用的过程中断了两次都是只能一方听到另一方声音。不知道是线路原因还是skype的问题。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;&lt;img src=&#34;/static/media/images/507.jpg&#34; alt=&#34;skype for linux&#34; title=&#34;skype for linux&#34; /&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;总之,这两个软件基本还可以用。&lt;/p&gt;&#xA;</description>
<pubDate>Wed, 04 Apr 2007 14:51:09 +0000</pubDate>
</item>
<item>
<title>启用了新的域名lunny.org</title>
<link>http://lunny.info/2007/4/3/启用了新的域名lunny.org.html</link>
<description>&lt;p&gt;本来想申请lunny.net可惜被人注册去了。将blog.lunny.org转发到现在这个blog的首页设置了隐藏真实url不过居然还要递交书面申请才能有效。在&lt;a href=&#34;http://www.xinnet.com&#34;&gt;新网&lt;/a&gt;申请的域名120RMB/年,不知道别的域名提供商如何。另外申请了免费的论坛,地址&lt;a href=&#34;http://lunnyxiao.uu1001.com&#34;&gt;lunnyxiao.uu1001.com&lt;/a&gt;,是&lt;a href=&#34;http://www.phpwind.com&#34;&gt;phpwind&lt;/a&gt;的,不过还没有链接到这里来。&lt;/p&gt;&#xA;</description>
<pubDate>Tue, 03 Apr 2007 14:36:00 +0000</pubDate>
</item>
<item>
<title>终于回到上海了!</title>
<link>http://lunny.info/2007/4/1/终于回到上海了!.html</link>
<description>&lt;p&gt;历时两周的海南之行终于结束了。虽然海南风光不错温泉沙滩海水还有晒人的太阳。不过金窝银窝始终不如自己的家舒服啊。从海南上飞机的时候是37度到上海下飞机的时候却在下雨温度据说在15度真是天壤之别。到家了一榻糊涂先开窗通通风。然后是整理东西买的东西不多两个椰壳做的饰品送给老婆的妹妹两罐黄辣椒还有两盒珍珠粉还有在海边买的一些贝壳之类的东西。可惜的是这次没带相机过去所以没有拍照。明天该开始上班了今晚要早点睡。&lt;/p&gt;&#xA;</description>
<pubDate>Sun, 01 Apr 2007 11:45:07 +0000</pubDate>
</item>
<item>
<title>添加显示浏览者天气栏</title>
<link>http://lunny.info/2007/3/31/添加显示浏览者天气栏.html</link>
<description>&lt;p&gt;本来没有主动寻找这方面代码的打算,在浏览某个网站时发现了这段代码,所以就拷贝过来了.代码如下:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-html&#34;&gt;&amp;lt;iframe xsrc=&amp;quot;http://weather.265.com/weather.htm&amp;quot; mce_src=&amp;quot;http://weather.265.com/weather.htm&amp;quot; width=&amp;quot;168&amp;quot; height=&amp;quot;50&amp;quot; frameborder=&amp;quot;0&amp;quot; marginwidth=&amp;quot;0&amp;quot; marginheight=&amp;quot;0&amp;quot; scrolling=&amp;quot;no&amp;quot; name=&amp;quot;265&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;</description>
<pubDate>Sat, 31 Mar 2007 15:13:07 +0000</pubDate>
</item>
<item>
<title>打算在这里扎根了</title>
<link>http://lunny.info/2007/3/30/打算在这里扎根了.html</link>
<description>&lt;p&gt;在海口的这个下午,没有工作,只好上网。在msn上碰到qyt又去重访了他的博客&lt;a href=&#34;http://www.dup2.org/blog&#34;&gt;BT的花&lt;/a&gt;。碰巧最近也想写写博客但是对msn博客的访问速度及其不满意因此本打算自己租一个空间架设一个博客。经过他的介绍找到了&lt;a href=&#34;http://lunny.yo2.cn/www.yo2.cn&#34;&gt;www.yo2.cn&lt;/a&gt;注册试用之后感觉比较满意。再加上它使用的是WordPress一旦这个站点有什么问题我可以很容易的将内容导入到另一个WordPress中。Good我会把这里当根据地经常来这里浇浇水的&lt;/p&gt;&#xA;</description>
<pubDate>Fri, 30 Mar 2007 10:21:36 +0000</pubDate>
</item>
<item>
<title>自己动手编写c++事件模型</title>
<link>http://lunny.info/2006/7/30/自己动手编写c++事件模型.html</link>
<description>&lt;p&gt;在Java、C等语言或者说其类库中都实现了事件模型。而c++语言本身并没有定义事件机制并且在目前众多优秀的c++类库包括STL、Boost等都没有实现类似的事件机制。当我们被MFC的消息搞得头昏眼花之时是否有冲动自己去实现一个简单的事件模型呢。我想有着相同想法的人肯定很多而真正动手来写可能会碰到各种各样的困难。下面就让我们一步步来编写一个简单的事件模型。&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;了解事件模型的机制&#34;&gt;了解事件模型的机制&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;在开始之前,我们有必要简单的了解一下事件模型的机制。事实上,事件模型的机制不止一种,&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Java和c#的事件机制就不太一样不过事件模型的基础都是一样那就是一般都使用Observer设计模式。关于Observer设计模式希望详细了解的朋友可以参考《》书在这里我们就不详细的介绍只是参照c#的事件机制来实现。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;在c#中我们可以以event关键字声明一个事件然后我们可以将一个函数委托挂接在这个事件上当事件被调用时则所有挂接的函数也会被调用。这里有一个比较难以理解的词大概是委托其实也不难理解。委托其实就是一种函数包装类这种类可以把任何函数的指针保存起来等到需要调用的时候再调用并且这种类的实例必须能够挂接到事件之中。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;看了上面这段话是不是觉得其实事件模型也并不复杂。还想对事件模型了解得更多我这里就不继续了感兴趣的朋友可以查找一下相关的资料。下面我们就开始在c++中编写一个简单的事件模型。&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;设计一个简单的c-事件模型&#34;&gt;设计一个简单的c++事件模型&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;因为一个事件模型其实就是一个典型的Observer设计模式因此最重要的就是&lt;/p&gt;&#xA;&#xA;&lt;p&gt;Subject(目标类)和Observer(观察者类)的设计。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;首先我们需要一个事件类它其实是一个抽象的Subject。它是一个基类所有的其他事件都从它继承并且用户只需要从这个基类继承就可以自定义事件。我们不仿将这个类定义为CEvent。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;CEvent的声明如下&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;class CEvent&#xA;{&#xA;public:&#xA;&#xA;typedef list&amp;lt;CEventHandler&amp;gt; data_type;&#xA;&#xA;CEvent();&#xA;&#xA;virtual ~CEvent();&#xA;&#xA;void operator()()&#xA;{&#xA; data_type::iterator it;&#xA;&#xA; for (it = m_observer.begin(); it != m_observer.end(); ++it)&#xA; {&#xA; (*it)(*this);&#xA; }&#xA;}&#xA;&#xA;CEvent&amp;amp; operator+= (const CEventHandler&amp;amp; handler)&#xA;{&#xA; Register(handler);&#xA; return *this;&#xA;}&#xA;&#xA;CEvent&amp;amp; operator-= (const CEventHandler&amp;amp; handler)&#xA;{&#xA; UnRegister(handler);&#xA; return *this;&#xA;}&#xA;&#xA;void Register(const CEventHandler&amp;amp; handler)&#xA;{&#xA; m_observer.push_back(handler);&#xA;}&#xA;&#xA;void UnRegister(const CEventHandler&amp;amp; handler)&#xA;{&#xA; m_observer.remove(handler);&#xA;}&#xA;&#xA;protected:&#xA; data_type m_observer;&#xA;};&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;其次这个事件可以挂接任意的函数也就是它可以保存所有的挂接函数。在c++中我们大致有三种类型的函数全局或者静态函数、成员函数、仿函数。要在CEvent中实现一个或者多个方法来执行挂接的任务不太现实。我们参考c#的实现方法可以先实现一个委托类将各种函数保存起来。然后再使用CEvent对这个委托类进行挂接。我们不仿称这个类为CEventHandler。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;然后我们的这个委托类需要能够保存所有的函数。在c++里,我们能够很容易的得到函数的指针,只需要在函数名前加&amp;amp;但是各种函数特别是成员函数它是没有类型的这就要有一种机制将所有的函数转换为同一种类型。因为函数是没有类型也相当于每一个函数都是一种类型解决这个问题的方法其实就是使用模板类。因此我们先定义一个虚基类CFunImpl。对三种不同的函数我们分别从CfunImpl继承三种不同的子类CstaticFun、CmemFun,CFunctor。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;CstaticFun表示全局的或者静态的函数不过这必须是一个模板类对于每一个静态或者全局函数都是模板类的一个特化。类声明的代码如下&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;template&amp;lt;typename Fun&amp;gt;&#xA;class CStaticFun :&#xA;&#xA;public&#xA;&#xA;CFunImpl {&#xA;public:&#xA;CStaticFun(const Fun&amp;amp; fun) :&#xA;m_fun(fun)&#xA;{};&#xA;&#xA;virtual ~CStaticFun()&#xA;{&#xA;}&#xA;&#xA;protected:&#xA; Fun m_fun;&#xA;};&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;对于&lt;code&gt;CmemFun&lt;/code&gt;类,我们如法炮制,不过我们这时需要保存两个成员变量,一个是成员函数的指针,另一个则是该成员函数所属的对象的指针。类声明的代码如下:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;template &amp;lt;typename PointerToObj, typename PointerToMemFun&amp;gt;&#xA;class CMemFun : public CFunImpl&#xA;{&#xA;public:&#xA;&#xA;CMemFun(const PointerToObj&amp;amp; pObj, PointerToMemFun pMemFn)&#xA;:m_pObj(pObj),&#xA;m_pMemFun(pMemFn)&#xA;{&#xA;}&#xA;&#xA;virtual ~CMemFun()&#xA;{&#xA;}&#xA;&#xA;protected:&#xA;&#xA;PointerToObj m_pObj;&#xA;&#xA;PointerToMemFun m_pMemFun;&#xA;};&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;对于仿函数,我们这里先不讲,因为仿函数使用得很少,并且这还涉及到仿函数的概念,有兴趣的朋友可以在阅读完本文之后自己来实现。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;好,到现在为止,我们可以将所有的函数使用&lt;code&gt;CfunImpl&lt;/code&gt;来表示了。因此,我们可以在委托类&lt;code&gt;CEventHandler&lt;/code&gt;中保存&lt;code&gt;CfunImpl*&lt;/code&gt;来达到我们的目的。&lt;code&gt;CEventHandler&lt;/code&gt;的声明如下:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;class CEventHandler&#xA;{&#xA;public:&#xA;&#xA;virtual ~CEventHandler()&#xA;{&#xA;Clear();&#xA;}&#xA;&#xA;template&amp;lt;class Fun&amp;gt;&#xA;CEventHandler(const Fun&amp;amp; fun)&#xA;:m_pImpl(&#xA;new CStaticFun&amp;lt;Fun&amp;gt;(fun))&#xA;{}&#xA;&#xA;CEventHandler(const CEventHandler&amp;amp; fun)&#xA;:m_pImpl(NULL)&#xA;{&#xA;*this = fun;&#xA;}&#xA;&#xA;void Clear()&#xA;{&#xA; if (m_pImpl)&#xA; {&#xA; delete m_pImpl;&#xA; m_pImpl = NULL;&#xA; }&#xA;}&#xA;&#xA;CEventHandler&amp;amp; operator= (const CEventHandler&amp;amp; fun)&#xA;{&#xA; Clear();&#xA;&#xA; if (fun.m_pImpl)&#xA; {&#xA; m_pImpl = fun.m_pImpl&#xA;&#xA; -&amp;amp;gt;&#xA;&#xA; Clone();&#xA; }&#xA;&#xA; return *this;&#xA;}&#xA;&#xA;//成员函数&#xA;template &amp;lt;typename PointerToObj, typename PointerToMemFun&amp;gt;&#xA;CEventHandler(const PointerToObj&amp;amp; pObj, const PointerToMemFun&amp;amp; pMemFun)&#xA;:m_pImpl(new CMemFun&amp;lt;PointerToObj, PointerToMemFun&amp;gt;(pObj, pMemFun))&#xA;{&#xA;}&#xA;&#xA;void operator()(CEvent&amp;amp; e)&#xA;{&#xA; if (m_pImpl)&#xA; {&#xA; (*m_pImpl)(e);&#xA; }&#xA;}&#xA;&#xA;bool operator== (const CEventHandler&amp;amp; handler)&#xA;{&#xA; if (m_pImpl == NULL || handler.m_pImpl == NULL)&#xA; {&#xA; return true;&#xA; }&#xA;&#xA; if (typeid(m_pImpl) == typeid(handler.m_pImpl))&#xA; {&#xA; return (*m_pImpl) == (*(handler.m_pImpl));&#xA; }&#xA;&#xA; return false;&#xA;}&#xA;&#xA;protected:&#xA;&#xA;CFunImpl *m_pImpl;&#xA;&#xA;};&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;不过要实现事件机制,我们还需要&lt;code&gt;CfunImpl*&lt;/code&gt;的子类必须实现几个方法。第一个方法就是调用函数的方法,我们这里要求重载&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;void operator()(CEvent&amp;amp;amp; e);&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;来达到目的。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;第二个方法就是&lt;code&gt;operator==&lt;/code&gt;,为什么需要这个方法呢。因为对于&lt;code&gt;CEvent&lt;/code&gt;来说,调用&lt;code&gt;UnRegister()&lt;/code&gt;时,我们必须找到我们使用&lt;code&gt;Register()&lt;/code&gt;添加到&lt;code&gt;list&lt;/code&gt;中的函数,这时我们需要判断两个&lt;code&gt;CEventHandler&lt;/code&gt;对象是否相等,这是件麻烦事情。&lt;code&gt;CEventHandler&lt;/code&gt;保存的是&lt;code&gt;CFunImpl&lt;/code&gt;的指针,好吧,这个我们就要求&lt;code&gt;CfunImpl&lt;/code&gt;的子类必须实现重载&lt;code&gt;operator==&lt;/code&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;第三个方法,从&lt;code&gt;CfunImpl&lt;/code&gt;继承必须实现&lt;code&gt;Clone()&lt;/code&gt;方法。&lt;code&gt;Clone()&lt;/code&gt;方法可以让&lt;code&gt;CEventHandler&lt;/code&gt;实现拷贝。具体的实现方法我们可以查看源码。&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;使用事件模型&#34;&gt;使用事件模型&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;一个简单的事件模型基本完成了。现在我们可以编写一个例子来演示一下如何来使用这个事件模型。其实使用起来很简单。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;我们可以声明一个&lt;code&gt;CEvent&lt;/code&gt;或者&lt;code&gt;CEvent&lt;/code&gt;子类的实例。让需要监听的函数挂接到这个&lt;code&gt;CEvent&lt;/code&gt;中,我们实现了&lt;code&gt;Register()&lt;/code&gt;函数和&lt;code&gt;+=&lt;/code&gt;操作符,都可以使用。挂接的语句就像这样:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;CEvent evt;&#xA;&#xA;CobserverTest obj;&#xA;&#xA;evt +=CEventHandler(&amp;amp;obj&#xA;&#xA;, &amp;amp;amp;(&#xA;&#xA;CObserverTest::OnStartEvent&#xA;&#xA;));&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;当事件的&lt;code&gt;operator()&lt;/code&gt;方法调用时,就会自动调用我们的监听函数。具体的例子可以查看提供的源代码。&lt;/p&gt;&#xA;&#xA;&lt;h3 id=&#34;模型的缺陷和改进&#34;&gt;模型的缺陷和改进&lt;/h3&gt;&#xA;&#xA;&lt;p&gt;到现在为止,我们终于实现了一个简单的事件模型。它可以有效的工作,并且与平台无关。当然,要求你的编译器尽量的支持&lt;code&gt;c++&lt;/code&gt;标准。如果你的编译器对&lt;code&gt;c++98&lt;/code&gt;标准支持不够,也许它不能够工作正常。笔者在&lt;code&gt;VC6SP6&lt;/code&gt;&lt;code&gt;VC2005&lt;/code&gt;中测试均通过。&lt;/p&gt;&#xA;&#xA;&lt;p&gt;不过这个模型还有可以改进的余地。&lt;/p&gt;&#xA;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;p&gt;目前只是支持同步操作,即当你的事件进行调用时是在同一线程里顺序进行。要改进到支持异步调用就必须在每次调用时在一个新的线程中进行调用。因此我们需要一个线程机制来完成这个工作。如果在一个特定的平台中使用,如&lt;code&gt;VC&lt;/code&gt;中,我们可以在响应函数中创建一个新的线程。&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;目前的所有的函数参数必须为&lt;code&gt;(CEvent&amp;amp; e)&lt;/code&gt;,不能写成&lt;code&gt;(CchildEvent&amp;amp; e)&lt;/code&gt;。要对这个限制进行改进,就需要编译器支持虚模板成员函数的机制。不过在&lt;code&gt;VC6&lt;/code&gt;&lt;code&gt;VC2005&lt;/code&gt;中,均不支持这种特性。因此要么寻求它法,要么就等到编译器的支持吧。&lt;/p&gt;&lt;/li&gt;&#xA;&#xA;&lt;li&gt;&lt;p&gt;线程安全,这个简单的事件模型使用到了&lt;code&gt;STL&lt;/code&gt;中的&lt;code&gt;list&lt;/code&gt;组件但是并非所有的list实现都是线程安全的。建议大家采用stlport因为它的实现是线程安全的如果没有采用线程安全的实现那么在调用&lt;code&gt;Register&lt;/code&gt;&lt;code&gt;UnRegister&lt;/code&gt;时最好能够实现互斥。&lt;/p&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;p&gt;限于笔者知识水平和文笔有限不当之处肯定很多。若你有好的想法和建议可以email告诉我。&lt;/p&gt;&#xA;</description>
<pubDate>Sun, 30 Jul 2006 06:38:00 +0000</pubDate>
</item>
<item>
<title>关于函数指针和回调</title>
<link>http://lunny.info/2004/10/3/关于函数指针和回调.html</link>
<description>&lt;p&gt;最近需要用到这个函数指针的方法,搜索了一下,发现这篇文章 &lt;a href=&#34;http://vckbase.com/document/viewdoc/?id=195&#34;&gt;http://vckbase.com/document/viewdoc/?id=195&lt;/a&gt; 自己试着写了一个例子程序只是没有用到CALLBACK关键字这样应该是可以的。不过还是没有弄明白函数指针和回调的关系不明白为什么MFC中的回调函数前用CALLBACK来声明。&#xA;下面说一下我的步骤:&lt;/p&gt;&#xA;&#xA;&lt;p&gt;1.先声明一个函数指针类型&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;typedef int (*TESTCALLBACK)(BOOL);&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;2.然后声明并实现被调用函数&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;int CallbackTest1(BOOL bIs);&#xA;int CallbackTest1(BOOL bIs);&#xA;{&#xA; return bIs;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;3.声明调用函数&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;int test(TESTCALLBACK lpTest);&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&#xA;&lt;p&gt;4.在调用函数的实现中,直接使用函数指针即可&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code class=&#34;language-c++&#34;&gt;lpTest(TRUE);&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;</description>
<pubDate>Sun, 03 Oct 2004 04:59:00 +0000</pubDate>
</item>
</channel>
</rss>