2009年3月16日星期一

用Json来存储格式化数据

这篇文章本来是写给实验室同学们的,针对的是做文本处理实验中数据的存储问题。转帖在blog上留存,呵呵。

看看这个场景:你处理了一些文档,得到格式化的结果,包括正文、标题、时间、类别标签和作者。现在你想把这些结果写到文件里面,以便后面程序读取,或是仅仅作为归档。你会选择什么格式和方法来输出这个数据文件呢?下面有几个常见选择:

  • A: 写一个文本文件,第一行是标题,第二行是时间,第三行是类别标签,第四行是作者,最后一行是正文。读取的时候写一个(不算短的)程序解析这个文件。
  • B: 利用java的DataOutputStream,按顺序把正文、标题、时间、类别标签、正文和作者输出。读取的时候按照同样的顺序读出。
  • C: 创建一个类代表一个文档,如class Doc,其中的公共成员变量代表上述的内容,然后用Java的序列化机制写到文件中。读取的时候用反序列化读出。
  • D: 我很聪明,我发明了一种格式来存取。虽然复杂,但是(可)能节约空间。

这些方法都可行,但不一定是最好的方案,其中有一些常见的陷阱。

第一个陷阱是escaping。以A为例,如果正文中含有回车字符(这很常见),那么正文就可能占很多行,如何决定 正文什么时候结束呢?你可以事先去掉所有的回车字符,那日后如果想要按行分析该怎么办呢?如果你聪明地(谜之音:小聪明!)把回车都替换成一个其他字符 串,比如"_huiche_",那当原文中出现这个字符串时怎么办?别认为这不可能,在大规模数据处理中这几乎是一定的。如果你按照通行的办法,把所有的 回车都变成"\n",那么你就得处理"\"这个字符,复杂的规则会让你写很多的代码。A和D都可能被escaping问题困扰。

第二个陷阱是二进制兼容性。以B为例,看起来这个方法简洁漂亮,可如果另一个同学用C++,他想读你的数据,等待他 的就不是一个简洁漂亮的任务了。他需要仔细了解Java输出的格式,如何处理Java诡异的writeUTF输出。如果你恰好用的是方法C,那这位同学就 掉进了一个黑洞,反复研究Java的序列化协议,被折磨的苦不堪言。如果这位同学用的是不同字节序的电脑(如老Mac,或是Sun工作站)更是一场噩梦。 B、C和D方法都可能被二进制兼容性问题困扰。

第三个陷阱是可读性。假设你的数据用A方法保存,在磁盘上睡了一学期,然后另外一个同学(谜之音:或是你自己!)要 重新用这些数据,你还能记起每行代表什么么?更糟糕的是当时的程序可能已经不见了。A方法还好,能从数据的内容大致猜测一下,如果你用的是B、C、D方 法,该怎么去猜呢?如果你已经毕业了,其他同学猜起来的难度不亚于破解密码。A、B、C和D四种方法都受到可读性问题的困扰。

用JSON表示数据

这篇文章介绍了如何利用一种标准格式,JSON,来将格式化的数据存储到文件中,或是在程序之间传递。JSON可以由任何语言读取,可读性强,没有字节序问题,就是原作者人间蒸发,也可以从数据文件中了解到每个域都保存了什么内容。

还是继续前面的例子,在Java中,我们先定义一个类代表这种数据:

class Doc {
public String content;
public String title;
public String classLabel;
public long timestamp;
public String author;
}

对于一个Doc类的对象,我们可以用Google提供的Gson库把它变成一个标准的JSON字符串:

Doc d = someDocument;
Gson g = new Gson();
String jsonStr = g.toJson(d);

非常简单对吧,jsonStr的内容就像这样

{"content":"内容\n另外一行内容","title":"文档标题","classLabel":"体育","timestamp":99817827282,"author":"某人"}

我们通过肉眼观察就能发现这个字符串代表哪些信息,就是有一天完全没人知道数据是怎么生成的,还是能够解析。

用下面的方法可以把一个JSON字符串解析出来:

Doc d = gson.fromJson(jsonStr, Doc.class);

这样d的内容就是jsonStr中代表的内容了。

将JSON格式的数据保存到文件

如果要保存到文件,我建议将数据写到压缩的文本文件中,每行一个JSON字符串。假设所有的数据都放在List dataset中,数据类名是Data,那么把数据保存到文件的代码可以写为:

FileOutputStream fos = new FileOutputStream("data.gz");
GZIPOutputStream gos = new GZIPOutputStream(fos);
OutputStreamWriter osw = new OutputStreamWriter(gos, "UTF-8");
BufferedWriter output = new BufferedWriter(osw);

Gson gson = new Gson();
for (Data d : dataset) {
output.write(gson.toJson(d));
output.write('\n');
}
output.close();

读取数据到同样的一个List中,代码可以写为:

FileInputStream fis = new FileInputStream("data.gz");
GZIPInputStream gis = new GZIPInputStream(fis);
InputStreamReader isr = new InputStreamReader(gis);
BufferedReader input = new BufferedReader(isr);

Gson gson = new Gson();
List dataset = new LinkedList();
String line;
while ((line = input.readLine()) != null) {
dataset.add(gson.fromJson(line, Data.class));
}
input.close();

你可能注意到读写数据代码中壮观的一排new,是的,这很丑很麻烦。不过这么程式化的代码可以简单包一包,例如上面两段代码可以简化为

GzipTextFileWriter output = new GzipTextFileWriter("data.gz", "UTF-8");
Gson gson = new Gson();
for (Data d : dataset) {
output.writeLine(gson.toJson(d);
}
output.close();

GzipTextFileReader input = new GzipTextFileReader("data.gz", "UTF-8");
Gson gson = new Gson();
List dataset = new LinkedList();
String line;
while ((line = input.readLine()) != null) {
dataset.add(gson.fromJson(line, Data.class));
}
input.close();

只要写两个简单的wrapper class,GzipTextFileReader和GzipTextFileWriter就行了。

存储效率,空间和时间

聪明的你一定发现了,用Gson库把数据保存为JSON格式,其中多出了元信息,如"classLabel"。这些元信息在每条数据里面都有,这不是非常浪费么?另外,我们用字符串表示数字和布尔类型,这不也是浪费空间么?

如果我们不用GzipXXX,老老实实地写到文本文件里面,确实是浪费空间。但由于元信息高度重复,写入压缩文件后,JSON格式占用的空间并不比直接写二进制数据多出多少。下面我们用一个小实验来证明。

我们写10000条数据,每条数据由一个字符串,一个整数,一个浮点数和一个布尔值组成,具体的取值随机,字符串长度相似,约十多个字符。我们用三种方法来保存数据,第一种是直接用二进制的DataOutputStream来保存每条数据,没有任何冗余;第二种是利用Json格式,但不保存如"classLabel"之类的元信息;第三种是用上面介绍的Gson库,同时保存元信息和数据。三种方法我们都输出压缩和不压缩两种数据文件。最终文件的尺寸在下表中列出。


不压缩压缩
二进制396KB260KB
Json无元信息648KB264KB
Gson1024KB280KB

由表中数据可以看出,1)Gson输出虽然比前两种方法尺寸大,但是在压缩后,多出的部分非常有限 2)数据压缩和不压缩有很大差别,压缩的Gson仍比不压缩的二进制数据小很多。

听起来可能有点反常,但是输出到压缩文件比直接输出到文本文件要快,而不是慢。这是因为现代CPU的速度远快于磁 盘,写文件的时候主要的时间花在磁盘的机械动作上。如果事先对数据进行压缩,会减少实际写入的字节数,从而节约写数据的时间。而压缩数据对于现代CPU来 说易如反掌,多数情况下消耗的资源都很少。

和Hadoop联用

在Hadoop的map-reduce中,除了极端要求性能或明显可以节约时间的简单情况,我们都推荐用Gson+JSON来表示数据。 Hadoop的key和value如果是Text格式,都会自动进行压缩,所以大家也无需担心压缩问题。Gson本身的序列化和反序列化代码经过仔细优 化,性能很好。

结论

(谜之音:怎么结论都出来了,这是论文么???!!)

使用JSON格式和Gson库来表示数据并存储在压缩文件,这种方法更方便,更快,更节约空间。如果有朝一日忘记了数据保存的格式,看看文件内容就知道了,避免冏况发生。

参考资源

JSON格式标准说明: http://www.json.org

Gson库: http://code.google.com/p/google-gson/

后记:Google的protocol buffer格式也是一个不错的选择,不过它需要先用一种特定的语言描述数据格式,然后从格式文件创建出具体的类代码,数据的序列化和反序列化都是用生成的代码实现的。如果原数据格式描述和代码丢了,基本上也就废了。

2009年3月15日星期日

2009年3月14日星期六

Google Docs最近很不稳定啊

前几天Google Docs打不开,我以为是临时现象,结果今天spreadsheets又打不开了。不知道Google Docs那帮人在想些啥,再这样搞下去,就该流失用户了...

2009年2月19日星期四

2009年2月10日星期二

闲扯公司中VPN的安全

VPN,包括IPSec啊或是加密的PPTP啊什么的,本身是很安全的,一般都是用128位的算法来对来往数据加密。解开VPN的加密算法需要数千台计算机工作数千年。所以,迄今为止还没有听说通过破解加密算法来攻破VPN的真实案例,这也许是因为我孤陋寡闻,但是至少很稀有。

不 过,就像木桶能装多少水依靠最短的一块木板,VPN安全也有一块短板,那就是我们自己和自己的家用电脑。要想通过VPN来窃取公司机密,最方便也是最 常用的方法不是破解复杂的数学密码,而是想办法从相关人员手里偷到钥匙(偷窥到密码)。甚至在多数情况下,密码都不是“偷”去的,而是大摇大摆抄走 的,或是员工主动提供的。这听起来可笑,但是却常常发生在缺乏安全知识和意识的员工身上。下面通过几个例子来说明:

例子一:公司员工美女小A,在家里想远程连回公司办公,但是VPN总也不工作。小A想到正追求自己的宅男小C是电脑高手,于是搬来小C替自己解决。为了节约时间减少 麻烦,小A就把自己的密码“prada”告诉了小C。小C很快的解决了问题,带着小A的感谢和微笑(谜之音:还有密码!!!)回家了。小A找到帅气新男友 小D半月后,发生了公司客户数据以每条1块钱的价格泄露给竞争对手的消息,小A傻眼了(谜之音:她可能连眼都没傻)。

教训一:无论如何,不要把自己的密码泄露给其他人,包括公司的同事。公司的同事可能会利用你的身份进行信息犯罪,并栽赃给你。如果实在需要找小C帮忙,也记得自己输入密码,不要让小C看到。

例子二:公司员工美女小A,在家里想远程连回公司办公,输入了自己的用户名Amy和密码prada,顺利连入公司网络。情场失意的网络高手小C,在深夜报复社会,用程序猜测小A在公司VPN的密码。小C不是胡猜,他用一个含有很多常用密码、英文单词、从1970年到现在每天日期的文件作为猜测的备选,不出几分 钟,电脑上提示prada这个常见单词被猜中了。几天后,发生了公司客户数据以每条1块钱的价格泄露给竞争对手的消息。

教训二:不要使用简单的密码,例如自己的生日、英文单词、较短的随机字符串(例如小于5位字母,黑客完全可以把5个英文字母的所有组合尝试一遍)。密码要长,最好大于10位,要没有规律,要包含字母、数字和标点符号,例如“sak@32o8.fjs”就是一个不错的密码,黑客老死也无法猜中。记不住这么变 态的密码怎么办?多记就好了,一般来说几天之后就会背下来。我还有个小技巧,是利用符合发音规则的假词造长密码,例如coporine92simory!,这个密码比上面的好记,也相当安全。

例子三:公司员工美女小A,在家里想远程连回公司办公,输入了自己的用户名Amy和无敌密码i~dont~th1nk~u~can~guess~this~password293,顺利连入公司网络。情场失意的网络高手 小C在深夜报复社会,通过QQ、MSN等大量发送木马病毒。小A的机器没有开防火墙(嫌麻烦)和杀毒软件(嫌慢且不想花钱),已经不幸中招几天还没有察觉。小C发 现某台电脑受到自己控制,这台电脑又有一个活动的VPN链接,于是偷偷远程潜入小A的电脑,又通过小A电脑上已经连接的VPN进入公司。几天后,发生了公司客户数据以每条1块钱的价格泄露给竞争对手的消息,小A自己的私房照片也被贴在了火爆的成人网站上。

教训三:一定要开启防火墙,一定要及时更新系统,一定要安装杀毒软件并及时更新。黑客从来不是针对你行动的,他们广撒大网,只有安全性弱的电脑才会中招。 一般公司会配备集中采购的杀毒软件,如果没有的话,可以考虑Windows防火墙,AVG免费杀毒软件,360安全卫士和墨者安全专家等软件,这些都是完全免费的,能给电脑带来不错的基本保护(但是如果涉及高度敏感数据一定要咨询相关专业人员,如公司信息安全专员)。

我选择美女小A作为主角也是有意义的。公司里面不乏计算机达人,他们自己的电脑防护的如铁桶一般百毒不侵,但是像小A这样对技术不感冒的员工就是企业安全的短板。要修补这块短板,一般就需要专职IT人员的检查和不厌其烦的培训了,呵呵。

2009年1月27日星期二

在Java中检测cpu数目

如果我们想在Java中利用多核处理器,那么就要按照实际的处理器数目来创建线程。如何得到处理器数目呢?一句话: Runtime.getRuntime().availableProcessors(); 这是一个int值,在我的macbook上返回的就是2,呵呵,简单方便。

2009年1月20日星期二

Is Java ready for multi-cores?

The title is a little big. -_-

Nowadays most computers have two independent CPU cores, for example, my MacBook has two in the Intel Core 2 Duo. Question is: Can we get any benefits from the additional core when coding with Java? Assuming we programmers take care of threading and algorithm parallelization.

The key to this question is whether we can run two Java threads simultaneously on different cores. In C/C++, we can use SetThreadAffinityMask() for Windows and thread_processor_bind_np() in POSIX systems like Linux. Is there similar mechanism in Java threading? A Google search brings up some relevant pages, like this article by Patrick on JavaWorld. Patrick argued that Java 1.1.3 VM cannot utilize more than 2 CPUs well (Oops, I have only 2..).

To get my hands dirty, I wrote a small program in Java to see if it can use all my cores.


public class MultiCoreWorker extends Thread {

@Override
public void run() {
int a = 0;
while (true) {
a = (a + 1) % 1000;
}
}

public static void main(String[] args)
throws InterruptedException {
MultiCoreWorker t1 = new MultiCoreWorker();
MultiCoreWorker t2 = new MultiCoreWorker();

t1.start();
t2.start();
Thread.sleep(1000000000);
}

}


The code basically do nothing but adding a number, which will take all available CPU cycles. When 2 MultiCoreWorker are running, the system have a 100% CPU usage on both cores. I ran the code multiple times to assure that it's stable. A screenshot of the Activity Monitor is as follows.


It seems at least Java threads can expand on 2 cores well ( On Mac OS X leopard with client JVM 6.0). I'll try to make some of my code parallel and see how much time can it saves by utilizing 2 cores.

The funny thing happened when running just a single thread. I commented out the codes with t2, and re-ran the program. Only one core was used, that's not beyond our expection. Interesting thing is in the following screenshot, the thread was actually jumping between the cores.

See the complementary curves just under the label "CPU Usage" ? :) Our code do not have any I/O operation, so there would be no I/O waiting. It clearly indicates that JVM can move threads between CPUs at any time. Cool...

2008年12月23日星期二

2008年12月21日星期日

Performance comparison 性能比较 : Java vs python vs c vs c++

哈哈,标题很大,内容很弱。
今天无聊,做了个小实验。假设我们有个任务,输入是一个文本文件,每行两个整数用空格分开,输出是一个文本文件,每行是输入对应行的和。这个例子虽小,但确是比较经典的场景。我用java, c, c++, python写了好几个程序,每个都在接近日常编码的情况下尽量写的快一点,看看究竟速度有什么差别。

程序的输入都是一样的,一个文本文件,有10,000,000行,每行两个1000以内的随机整数。机器是Macbook Intel Core Duo 2.0G, 2G内存 DDR667, 5400转seagate磁盘 8M缓存,Mac OS X Leopard 10.5.5. 虽然这些不是重要因素,但还是要介绍一下。

时间测试用的是bash的time命令,取的是real时间。

1) java,用split,java version "1.6.0_07" Java(TM) SE Runtime Environment (build 1.6.0_07-b06-153) Java HotSpot(TM) 64-Bit Server VM (build 1.6.0_07-b06-57, mixed mode)

程序如下

TextFileReader r = new TextFileReader("/Users/sixiance/Desktop/test.txt");
TextFileWriter w = new TextFileWriter("/Users/sixiance/Desktop/test.out.txt");
String l = null;
int a, b;
while ((l = r.readLine()) != null) {
String [] cols = l.split(" ");
a = Integer.parseInt(cols[0]);
b = Integer.parseInt(cols[1]);
w.writeLine(Integer.toString(a + b));
}
r.close();
w.close();

说实话split是很消耗时间的,每次都要生成一个正则表达式Pattern对象。这程序运行四次, 时间均值17.46125
标准差0.3162429,单位都是秒。

这里面的TextFileReader/Writer就是简单包装了一下new BufferedReader(new InputStreamReader(new FileInputStream()))

2) java 不用split, VM同上

既然刚才知道split比较费时间,那我就不用split,用如下的代码片段代替

int pos = l.indexOf(" ");
a = Integer.parseInt(l.substring(0, pos));
b = Integer.parseInt(l.substring(pos+1));

结果比split好很多,均值 10.26375标准差0.133784,快了好多。

3) c, gcc -O3, version 4.0.1 (Apple Inc. build 5465)

c程序如下:

#include "stdio.h"

int main(int argc, char** argv) {
FILE* fp = fopen("/Users/sixiance/Desktop/test.txt", "r");
FILE* ofp = fopen("/Users/sixiance/Desktop/test.cout.txt", "w");
int a, b;
while (!feof(fp)) {
fscanf(fp, "%d %d\n", &a, &b);
fprintf(ofp, "%d\n", a + b);
}
fclose(fp);
fclose(ofp);
}

结果自然是无敌快:均值8.09575, 标准差0.1816670

4) python, Python 2.5.1 (r251:54863, Apr 15 2008, 22:57:26) [GCC 4.0.1 (Apple Inc. build 5465)] on darwin

代码如下

f = open("/Users/sixiance/Desktop/test.txt", "r")
of = open("/Users/sixiance/Desktop/test.pyout.txt", "w")
for line in f:
[a,b] = line.strip("\n").split()
of.write(str(int(a) + int(b)) + "\n")
f.close()
of.close()

这个也是用了split,速度是很慢的, 均值48.5335, 标准差0.2030706

5) c++, g++ -O3, gcc version 4.0.1 (Apple Inc. build 5465)

程序如下

#include
#include

using namespace std;

int main(int argc, char** argv) {
ifstream ins("/Users/sixiance/Desktop/test.txt");
ofstream outs("/Users/sixiance/Desktop/test.cppout.txt");
int a, b;
while(!ins.eof()) {
ins >> a >> b;
outs << (a+b) << endl;
}
ins.close();
outs.close();
}

都用c++的方法,不是用c的部分。这个惊人的慢:均值68.3783,标准差0.3763082。c++ stream真不是盖的,敬仰啊... 这个的sys时间花的比user还多,而且都比c和java慢了好多。

6) c++ 不用endl
思考一下,罪魁祸首是endl,每个endl都会导致stream flush(这其实挺sb的)。把endl改成"\n",均值14.734,标准差0.2281447,这次是比较正常了。

总结回顾

让我们回顾一下,把各种情况下的时间都画在一个图里面:



这些数字绝不是要说明那个语言好或是那个语言快(本身意义也不大),因为实际的问题千差万别,很难用这么小的例子来概括。但是这些数字确实可以告诉我们,在抄起家伙随心地写一些小工具和小实验程序时候,大概会有一个什么性能差距。

2008年12月14日星期日

恢复游泳

今天晚上回到久违的学校游泳馆,开始恢复游泳了。

周四按摩的时候,按摩师傅说你们这些每天坐在电脑前面的人脊椎都不太好,得经常锻炼一下。我觉得这个挺重要,好几个兄弟颈椎都已经出状况了,赶紧就问啥锻炼最好。师傅介绍了几种方法,最好的还是游泳,看来经常游游是百利而无一害啊,不错不错。

游泳馆在奥运前半年的时候就封馆支援运动员训练了,上一张游泳卡就白搭了100多在里面。今天重回游泳馆,发现奥运还是有好处的,原来走廊里面剥落的墙皮现在变成瓷砖了,隔不远还有小装饰画,感觉好很多。更衣室里也重新装修,洗澡换成了感应喷头,热水也是哇哇地好,谁说奥运没啥好处,哈哈。

今天先游了1300米热热身,争取这次在游泳卡过期之前都把它用掉~

2008年12月8日星期一

魔方,无敌魔方

办公室有一个魔方,最普通的3x3那种,我搞了半天才搞定两个面,实在是太弱智了。

共享两个视频,第一个是机器人的,第二个是活人的






叹为观止啊

2008年12月7日星期日

美国的神奇之楼,竟然倒在一起




在google maps上偶尔看到的,应该是拼接的结果,但这拼接的也太好了

2008年12月1日星期一

校园风光



发两个校园随拍,安静的冬日午后。

2008年11月26日星期三

Java是混水摸鱼者的最爱?

看到一篇老文,The Perils of Java Schools,作者是Joel,一个知名程序员。大意呢就是java本身难度太低了,只学java不足以区分出优秀的计算机毕业生和混水摸鱼者。Joel对指针、递归和函数式语言比较有爱,觉得这些才能区分出一个脑子是不是适合做好的程序员。文章写的还是比较中肯的,不过总让我有一种感觉,就是某些有了一些知识的人,总是企图把自己脑袋里面的知识神化复杂化,好创造出和其他人的区别。Joel的文章里面也提起以前大学里面要学拉丁文,不是因为拉丁文有用,而是“受过教育的人应该懂拉丁文,以示和平头百姓的区别”。那么受过教育的CS毕业生也应该懂scheme以示和草根程序员的区别么?真是扯淡,有种拉出来练练才是真的。

另:你觉得递归有那么难,可以用来区分talented brain和java library cobbler么?
更新:昨天晚上找我女朋友做实验,我成功地在十几分钟内把递归的概念教给她,她随后自己推导出了快速排序算法。需要说明的是我女朋友是文科生,高中毕业后再没学过数学或是任何理科的东西。

2008年11月20日星期四

iPod touch 二代入手


观察了好久,终于出手啦,哈哈。我的是8G版本,在百度有啊上买的,1699大元,店主竟然亲自送货,还赠送屏幕贴,值得推荐。

言归正传,说说两天体会。
  1. 手感重量很棒,可以用惊人来形容,组装的也很结实,在工业设计上给满分。
  2. 触控感觉不错,虚拟键盘用起来很顺手,输入失误率很低。官方的拼音输入法就很不错了,手写玩了玩,觉得不如虚拟键盘打字方便,就又给关了。
  3. 速度流畅。
  4. 缺点:电池有点痿。我基本上是拿他当浏览器用,而不是当mp3用,所以wifi使用时间很多。如果像我这样整天wifi的话,基本上每天就得充一次电。
  5. 缺点:safari有时候会死掉,直接退回到home界面,不到影响使用的程度,但是也不罕见。
  6. 缺点:不支持flash。据说有人移植成功了?
  7. 缺点:appstore在中国好多东西不能买,音乐和电影就算了,连很多app都不能买,例如知名的enigmo谜之机器,实在是遗憾。中国appstore里面应用少很多,注册美国的帐号需要美国信用卡。据说有办法注册,还没试过。
  8. wifi连接速度挺快的,基本上感觉不到wifi初始化连接的过程,达到了“开机就上网”的标准。
  9. 没有内置麦克风,得用iphone耳机才能录音,很不方便。
总体来说还是很好的,哈哈

2008年11月17日星期一

百度的黑锅谁来背



我写东西非常不喜欢用叹号,始终觉得叹号太哗众取宠,在网络上已经被滥用了。不过今天我不得不用一个叹号,因为百度太棒了!

用百度搜索“谷歌公正性”,第一条结果“谷歌公正性遭质疑,央视曝光竞价排名”。题目看的我一惊,央视不是曝光百度么?怎么变成曝光谷歌了?点进去一看没有语言了,这个网页里面题目明明是“百度公正性遭质疑,央视曝光竞价排名”,到了百度的搜索结果里面,“百度”二字就有如神助地变成了“谷歌”,真是得来全不费功夫。

空口无凭,截图留念。

2008年11月13日星期四

游中关村软件园

软件园景色

软件园景色


今天陪mm去公司领材料,顺便游了游中关村软件园 :) 发两张照片纪念这个美好的阳光下午。(blogger不能发照片了?)

2008年11月5日星期三

找不到的mathbbold.sty ?

最近写一篇论文,用www2009的标准latex模版,在mactex下编译的时候提示找不到mathbbold.sty。虽然编译可以继续,但是心里总是很不爽,遂挖掘了一下相关原因。

原因其实很简单,这个mathbbold.sty就在你的系统上,不过改名叫mathbbol.sty(8.3文件名复活记?)。想兼容mathbbold这种写法,就到/usr/local/texlive/2008/texmf-dist/tex/latex/jknapltx/下,sudo cp mathbbol.sty mathbbold.sty,然后运行sudo mktexlsr重新建立文件名索引。再编译一下你的文档试试?大功告成 :)

2008年10月24日星期五

站立式工作



今天在Engadget上看到一篇帖子,说站着工作要比坐下工作健康好多,还会促进脂肪的代谢,减少一型糖尿病的发病率。看罢我们办公室的几个就立刻动手,把放电脑的桌子抬高到吧台高度(上图照片)。站立工作了一天,感觉还不错,不像是在写程序做实验,倒像是在演奏乐器或是接待客人。不如大家都来试试?

推荐一个编程时可以听的在线音乐电台:GrooveSalad

有没有过这种时候:工作比较累,周围太安静(或太聒噪),想听点音乐,又不想被歌词和演唱者忘我的高潮干扰,也不想操心该听什么?SomaFM提供了一个频道叫GrooveSalad就正好满足了这个要求。

SomaFM是一个免费的在线电台,下面有几个频道,我一直听的就是GrooveSalad。GrooveSalad,顾名思义,就是节奏(groove)拌的沙拉,播放的内容是DJ精选的背景音乐,曲调简单好听,节奏感强,但又不喧宾夺主,实在是编程第一佳品,哈哈。

GrooveSalad是一个pls文件,有realplayer就可以听,mac的itunes和linux下vlc都可以,你还可以把这个pls文件保存在电脑上,下次不用上网连,直接双击打开就可以了。

经过我长期收听,鉴定结果是GrooveSalad很值得推荐 :)