热爱互联网

此贴记录使用Go碰到的一些坑

(1)
第一条不算坑,算做优化吧,对于临时变量应该尽量使用栈空间,所以最好不要去make,但有时候需要使用slice,我们对slice的认知是必须先make才能使用的,其实不然,如下:
var b [5]byte
var bs []byte
bs = b[0:5]
copy(b[0:5], “hello”)
copy(bs, “world”)
其中b是数组,bs是slice。

(2)
这是坑,刚刚哥就被坑了—-莫名的panic,此时可以尝试去删除 GOPATH/pkg/ 目录下对应的.a文件,因为你的包是go get下来的,会自动编译成.a文件,如果后期又更新了代码但.a没有重新生成,那么有可能会出现这个问题。

(3)
这不是坑,深入理解defer语句,注意下面最后一个代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// output: 1
 
package main
 
import (
	"fmt"
)
 
func test() (r int) {
	defer func() {
		r = 1
		return
	}()
	r = 2
	return 3
}
 
func main() {
	r := test()
	fmt.Println(r)
}
 
 
 
// output: 1
 
package main
 
import (
	"fmt"
)
 
func test() (r int) {
	defer func() {
		r = 1
	}()
	r = 2
	return 3
}
 
func main() {
	r := test()
	fmt.Println(r)
}
 
 
 
// output: 1
 
package main
 
import (
	"fmt"
)
 
func test() (r int) {
	defer func() {
		r = 1
	}()
	r = 2
	return r
}
 
func main() {
	r := test()
	fmt.Println(r)
}
 
 
 
// output: 12
 
package main
 
import (
	"fmt"
)
 
func test() (r int) {
	defer func() {
		r += 10
	}()
	r = 2
	return r
}
 
func main() {
	r := test()
	fmt.Println(r)
}
 
 
 
// output: 13
 
package main
 
import (
	"fmt"
)
 
func test() (r int) {
	defer func() {
		r += 10
	}()
	r = 2
	return 3
}
 
func main() {
	r := test()
	fmt.Println(r)
}

小小的总结

6.16号学校发了双证,再也不是临时工了,哈哈。

前两个星期在折腾google的gyp构建系统,各种蛋疼后,发现不能解决我们现在的工程面临的环境配置问题,所以放弃了。
然后这几天上班一直在弄linux下的交叉编译环境,挺蛋疼的,各种不熟悉,
不过热情的同事帮忙解决了各种问题,搜噶,今天编译环境配好了。
智能摄像头挺好玩的,长姿态了。。过几天把程序放到摄像头芯片里面跑跑看:)

周末主要在解决学校的遗留问题,关于golang的。主要碰到两个问题,如下:

(1)

(注意:下面只测试了sqlite3数据库,其余数据库的情况不知道。)

平时向数据库中插入数据都是这样子的:

var id, msg string
db.Exec(“insert into data(id, msg) values(?, ?)”, id, msg)

一般情况下是没问题的,但是如果msg中包含了各种不可见字符,只要是一个byte
能表示的,都可能包含在msg中,那么用这种方法将导致msg不能插入到数据库中。
(golang中的byte是无符号的)

解决方法是这样插入:
db.Exec(“insert into data(id, msg) values(?, ?)”, id, []byte(msg))

(2)

AES加密算法,每次加密解密前都必须重新设置 iv (initialization vector, 初始化向量)。

blockMode := cipher.NewCBCEncrypter(block, iv)

记住,这是 CBC 的特点。

本来想在初始化时设置,这样可以优化很多性能,结果哥错了。

惨痛的教训。。

Golang Search Engine

这就是哥的毕设,尽管很水,但是不得不写。
大四第一学期开始学的go语言,然后寒假把这个基本写好了,断断续续写了一个月,爬虫还是去表哥那里蹭网测试的。。。
这两天准备写论文了,顺便把代码整理了一遍,目前web界面有点丑,就一个table,一个button,当然遵循MVC模式,到时候把样式改好看的就行了。

记录下比较有意义的地方:

爬虫:整个代码才2500余行,spider就占了500多行,当然麻雀虽小,但五脏俱全,可以配置爬虫的各个参数,比如抓取指定网站下的url, 即使爬虫意外关闭,也可以重新启动从上次断开的地方开始爬取…。 BFS搜索,多个goroutine下载,一个goroutine负责将数据写入磁盘。原理很简单,不过从这个spider中非常能感受到go语言的 channel 和 goroutine 的优美 — 并发之美。

提取网页正文和标题通过正则表达式搞定。

分词:采用双向最大匹配, 写棵 trie 就行了,然后需要记录下状态转移。一开始是用trie去分词的,今天装逼的写了个double array trie去替换掉trie 来优化内存,妈蛋的发现内存差不多, golang里面的map内部是一个非常优化内存了的hash算法,哥的trie就是用map去记录子节点,难怪和double array trie消耗的内存差不多。

关键词提取及索引:哥一开始从人民网上下载了5万多个网页,可以看做一个小型语料库了。然后去计算每个词的逆文档频率(Inverse Document Frequency,缩写为IDF),将这些IDF保存到数据库留作后用。对爬虫下载下来的网页,计算其TF-IDF值,索引之,差不多就完事了。。目前的策略是标题的关键词的权重是正文关键词的数倍。TF-IDF与余弦相似性的应用

数据库是sqlite3,sqliteadmin这个可视化工具不错,方便查看sqlite。当然也可以改用其他数据库,改一个参数就好了。
web方面用的是net/http自带的服务器,template引擎还算凑合。
顺便还写个本地文件搜索,丫的特简单,几十行代码。。

不说了,贴代码:
https://github.com/gansidui/gose

Golang TCP Server

这两天利用空闲时间用Golang写了个简单的TCP Server.
其实用Golang写TCP Server非常简洁,每来一个连接就分配一个goroutine去处理,对于每个处理Conn的goroutine只负责接收数据,再将分好的数据包丢到缓冲区中交给另外创建的一个goroutine处理,高并发就是这么来的。

每个数据包的组成: 包长 + 类型 + 数据
–> 4字节 + 2字节 + 数据
–> uint32 + uint16 + []byte
数据部分用protobuf封装,protobuf简直太赞了.

心跳机制其实只需要服务器设置读超时,客户端发的任何一个包都可以看做是心跳包,当然为了保证可靠性,客户端还是需要每隔一段时间发送一个PING包的。

测试:
服务器接受了5000个TCP长连接,对于每个连接分别由goroutine处理,每个连接分配1K缓冲区接收数据,当数据大于1K时缓冲区会自动扩容。平均每个连接耗费大概20K内存,总共100M多一点。
一般认为系统底层默认的socket读写缓冲区各8K,当然每个系统的设置不一样,也可以手动更改这个缓冲区大小。
另外golang现在的GC很慢,非常慢,客户端断开连接后,服务器需要等很久才释放socket占用的内存,据说go1.3的GC会大大改进,期待ing。

/************************************************/
经常逛github,但这是第一次上传代码,搜噶,发现世界如此美妙。。。
https://github.com/gansidui/gotcp

PowerPoint automation with C++

这段时间一直在看COM的东西,有了那么一点的认识,混了个眼熟,下次看到这种函数肯定是认识了。
这里有不错的讲解:http://www.cnblogs.com/zxjay/tag/COM/
下面这个也不错,不过其中代码有些错误需要改动才能通过编译:
http://blog.csdn.net/liwang112358/article/details/7364676

谷歌各种搜也没找到一个靠谱的COM控制Microsoft PPT放映的Demo,这篇文章是2002年的,现在的vs编译不过,不过很具参考价值:http://www.codeproject.com/Articles/2506/Automating-MS-Office-applications
这里有个小demo,用两种方式实现了ppt的自动创建,也可以参考:http://blogs.msdn.com/b/codefx/archive/2012/06/30/sample-of-june-30th-c-application-automates-microsoft-powerpoint.aspx

这里还有个讨论:http://social.msdn.microsoft.com/forums/office/en-US/971eeacb-9f1c-4c3f-a4cd-80b2fc86bf28/how-to-get-the-name-of-a-slide-in-the-edit-window

最后的绝招就是官方文档的,但是万恶的文档只有单纯的说明,并且还是针对VBA的,有些函数并没有给出都可以使用。当然绝大部分函数都可以凭着感觉使用的,哈哈。
http://msdn.microsoft.com/en-us/library/office/ff746846(v=office.14).aspx

下面是一个完整的控制台程序,自动操作Microsoft PowerPoint 2010,在 vs2010和vs2013下测试通过。
功能:首先获取活动的PPT窗口,然后跳转到第一张幻灯片,一张一张放,直到最后一张,然后再往前一张一张放,直到第一张,再往后… 循环。还可以根据官网文档实现更复杂的功能,比如:动态跳转到指定页,插入内容等等,还可以将此控制程序当做服务器来接收客户端(手机等)的指令,从而实现远程控制,这些都留着以后需要时再做了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#pragma region Includes
 
#include <stdio.h>
#include <Windows.h>
 
#pragma endregion
 
#pragma region Import the type libraries
#import "libid:2DF8D04C-5BFA-101B-BDE5-00AA0044DE52" \
	rename("RGB", "MSORGB") \
	rename("DocumentProperties", "MSODocumentProperties")
// [-or-]
//#import "C:\\Program Files\\Common Files\\Microsoft Shared\\OFFICE12\\MSO.DLL" \
//	rename("RGB", "MSORGB") \
//	rename("DocumentProperties", "MSODocumentProperties")
 
using namespace Office;
 
#import "libid:0002E157-0000-0000-C000-000000000046"
// [-or-]
//#import "C:\\Program Files\\Common Files\\Microsoft Shared\\VBA\\VBA6\\VBE6EXT.OLB"
 
using namespace VBIDE;
 
#import "libid:91493440-5A91-11CF-8700-00AA0060263B" \
	rename("RGB", "VisioRGB")
// [-or-]
//#import "C:\\Program Files\\Microsoft Office\\Office12\\MSPPT.OLB" \
//	rename("RGB", "VisioRGB")
 
#pragma endregion
 
 
 
void AutomationPowerPointByCOMAPI()
{
	CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
 
	try
	{
		PowerPoint::_ApplicationPtr app;
		HRESULT hr = app.CreateInstance(__uuidof(PowerPoint::Application));
 
		if (FAILED(hr))
		{
			wprintf(L"CreateInstance failed w/err %x\n", hr);
			return;
		}
 
		_putws(L"PowerPoint.Application is started");
 
		PowerPoint::_PresentationPtr PresentationPtr = app->GetActivePresentation();
		PowerPoint::SlidesPtr SlidesPtr = PresentationPtr->GetSlides();
		PowerPoint::SlideShowSettingsPtr SlideShowSettingsPtr = PresentationPtr->GetSlideShowSettings();
 
		_putws(L"Start show slides");
		SlideShowSettingsPtr->Run();
 
		int iSlidesCount = SlidesPtr->GetCount();
		printf("Slides count: %d\n", iSlidesCount);
 
		PowerPoint::SlideShowWindowPtr SlideShowWindowPtr = PresentationPtr->GetSlideShowWindow();
		PowerPoint::SlideShowViewPtr SlideShowViewPtr = SlideShowWindowPtr->GetView();
 
 
		int i = 1;
		bool isNext = true;
		SlideShowViewPtr->First();
 
		while (true)
		{
			Sleep(1000);
			if (isNext)
			{
				SlideShowViewPtr->Next();
				++i;
			}
			else
			{
				SlideShowViewPtr->Previous();
				--i;
			}
 
			if (i == iSlidesCount)
			{
				isNext = false;
			}
			else if (i == 1)
			{
				isNext = true;
			}
		}
 
	}
	catch (_com_error &err)
	{
		wprintf(L"PowerPoint throws the err: %s\n", err.ErrorMessage());
		wprintf(L"Description: %s\n", (LPCWSTR)err.Description());
	}
 
	CoUninitialize();
	printf("hello world\n");
}
 
int main()
{
	AutomationPowerPointByCOMAPI();
	return 0;
}

goroutine和C++ thread的生存期比较

goroutine 会一直存在直到主程序退出。
只有main函数return时,才会结束所有的goroutine,当然已经运行完的goroutine除外。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main
 
import (
	"fmt"
	"time"
)
 
func f() {
	fmt.Println("f()")
}
 
func test() {
	fmt.Println("begin test()")
	go f()
	fmt.Println("end test()")
}
 
func main() {
	test()
	time.Sleep(2 * time.Second)
	fmt.Println("end main()")
}
 
/*
 
begin test()
end test()
f()
end main()
 
*/

用C++11做下类比,C++中的thread默认的生存周期和创建它的函数是一样的,同样也是异步调用,但需要通过 join() 来等待thread结束,或者通过 detach() 使其独立运行直到main函数结束。
下面这个代码运行是会崩溃的,test()函数结束后,线程t会被强制结束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <thread>
#include <Windows.h>
 
void f(int i, double d)
{
	std::cout << i << " " << d << " "<< std::endl;
}
 
void test()
{	
	std::thread t(f, 21, 13.14);
	std::cout << "end test()" << std::endl;
}
 
int main()
{
	test();
	Sleep(2000);
	return 0;
}

可以调用join()等待线程t,或者调用detach()使t脱离它的创建者test()独立运行,直到main()结束.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <thread>
#include <Windows.h>
 
void f(int i, double d)
{
	std::cout << i << " " << d << " "<< std::endl;
}
 
void test()
{	
	std::thread t(f, 21, 13.14);
	//t.join();
	t.detach();
	std::cout << "end test()" << std::endl;
}
 
int main()
{
	test();
	Sleep(2000);
	return 0;
}
 
/*
	输出:
	end test()
	21 13.14
*/

安装 Go 第三方包 go-sqlite3

32 位 windows 的安装

安装 sqlite3。到 http://www.sqlite.org/download.html 的页面中,找到 sqlite-dll-win32-x86-3071700.zip 下载解压,并把里面的 dll 文件复制到 windows/system32 目录下。
下载 gcc 编译器。到 http://tdm-gcc.tdragon.net/download,下载 tdm-gcc-4.7.1-2。如果是 64 位的 win,下载 tdm64-gcc-4.7.1-3。运行这个 exe 文件,安装好 gcc 编译器。
运行命令:go get github.com/mattn/go-sqlite3 ,安装 go 的 sqlite3 的驱动等。

64 位 windows 的安装
下载 gcc 编译器。到 http://tdm-gcc.tdragon.net/download,下载 tdm64-gcc-4.7.1-3。运行这个 exe 文件,安装好 gcc 编译器。
运行命令:go get github.com/mattn/go-sqlite3 ,安装 go 的 sqlite3 的驱动等。

Qt编译c++11

在 pro 文件中添加如下一行,然后正常编译即可:
QMAKE_CXXFLAGS += -std=c++0x

BOM头

BOM —— Byte Order Mark,中文名译作“字节顺序标记”。
类似WINDOWS自带的记事本等软件,在保存一个以UTF-8编码的文件时,会在文件开始的地方插入三个不可见的字符(0xEF 0xBB 0xBF,即BOM)。它是一串隐藏的字符,用于让记事本等编辑器识别这个文件是否以UTF-8编码。对于一般的文件,这样并不会产生什么麻烦。
关于BOM:http://baike.baidu.com/subview/126558/5073178.htm

解决方法,用一些编辑软件,如Notepad,转换为 UTF-8无BOM格式。

读取文件最坑人:

1
2
3
4
5
s := "ufeffhello"
fmt.Println(s)      // hello
if s == "hello" {     // not equal
	fmt.Println("fuck")
}

sublime配置c++11编译环境

Tools->Build System->New Build System…
把下面代码复制进去。
注意其中在linux下编译thread的时候需要加上 -pthread。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
     "cmd": ["g++", "-std=c++11", "${file}", "-o", "${file_path}/${file_base_name}"], // For GCC On Windows and Linux
     //"cmd": ["CL", "/Fo${file_base_name}", "/O2", "${file}"],     // For CL on Windows Only
     "file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$",
     "working_dir": "${file_path}",
     "selector": "source.c, source.c++",
 
     "variants":
     [
          {
               "name": "Run",
               "cmd": ["bash", "-c", "g++ -std=c++11 -pthread '${file}' -o '${file_path}/${file_base_name}' && '${file_path}/${file_base_name}'"]  // Linux Only
               // "cmd": ["CMD", "/U", "/C", "g++ -std=c++11 ${file} -o ${file_base_name} && ${file_base_name}"]  // For GCC On Windows Only
               //"cmd": ["CMD", "/U", "/C", "CL /Fo${file_base_name} /O2 ${file} && ${file_base_name}"]   // For CL On Windows Only
          }
     ]
}

然后保存,在保存文件对话框中把文件名字改成“C++11.sublime-build” 保存即可.
然后选择Tools->Build System->C++11。