Google JavaScript Style Guide 中文简要翻译 – Language Rules部分

组内计划依照Google的JavaScript编程规约来进行开发。非常认真的学习了一下,于是顺手简单地翻译成了中文。
Style Rules部分等有空再翻。因为是原创,虽然可能别人也翻译过了,但转载时仍请注明出处。

余刚学习JavaScript不久,不可避免对原文的理解有错误,如有错误或不当地方请指出。

Google JavaScript Style Guide 中文简要翻译

1. Language Rules部分

1.1 var
总是使用var声明变量。(理由不多说了)

1.2 Constants
使用大写字母和下划线『_』来声明常量。可以在注释中适当使用@const标签。
但不要使用const限定词来修饰变量。IE浏览器不会解析const。
例如,对于简单的类型,命名规则已经足够:

/**
 * The number of seconds in a minute.
 * @type {number}
 */
goog.example.SECONDS_IN_A_MINUTE = 60;

对于复杂类型,使用@const标签:

/**
 * The number of seconds in each of the given units.
 * @type {Object.<number>}
 * @const
 */
goog.example.SECONDS_TABLE = {
  minute: 60,
  hour: 60 * 60
  day: 60 * 60 * 24
}

这允许编译器强制检查是否有改变。

1.3 Semicolons
总是使用分号『;』来结束语句。(理由也不多说了)

1.4 Nested functions
嵌套函数(函数体内定义的函数):可以使用。
嵌套函数有时候非常有用,自己决定在需要时使用。

1.5 Function Declarations Within Blocks
在块中定义函数:不要这样干。
虽然大多数JS解析器支持,但这不是ECMAScript标准。
ECMAScript标准只允许在脚本的全局环境或者函数体内定义函数。
请使用匿名函数并赋值给一个变量来替代。

错误的用法:

if (x) {
  function foo() {}
}

正确的用法:

if (x) {
  var foo = function() {}
}

1.6 Exceptions
应当使用异常处理。异常是不可避免的。

1.7 Custom exceptions
自定义异常:可以使用。自己决定在需要时使用。

1.8 Standards features
为了保持最大兼容,使用标准特性而不要使用非标准特性。
例如使用string.charAt(3)而不是使用string[3]。
又如使用DOM的函数来操作HTML元素而不是使用特定的省略记法。

1.9 Wrapper objects for primitive types
包装内置数据类型:没有理由这样干,而且这样做很危险。

不要这样干:

var x = new Boolean(false);
if (x) {
  alert('hi');  // Shows 'hi'.
}

但类型转换是OK的:

var x = Boolean(0);
if (x) {
  alert('hi');  // This will never be alerted.
}
typeof Boolean(0) == 'boolean';
typeof new Boolean(0) == 'object';

1.10 Multi-level prototype hierarchies
(自定义的)多层原型继承体系:不推荐。
如果你以自定义的类B为原型导出类D,那么你就构造了一个多层原型继承体系。
这些体系比它们乍看起来要难正确使用。

所以,使用Google的Closure库(the Closure Library)中的goog.inherits()或类似的类库来实现。

function D() {
  goog.base(this)
}
goog.inherits(D, B);

D.prototype.method = function() {
  ...
};

注:过长的原型链同时会导致性能问题。

1.11 Method definitions
将方法定义为原型方法。
原型方法定义的例子:

Foo.prototype.bar = function() { ... };

当有多个原型方法要添加时:

Foo.prototype.bar = function() {
  /* 注释 */
};

注:原型方法比构造函数中定义的对象方法效率要高
例如1.11-1的getFullName效率不如1.11-2的getFullName。
1.11-1

function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;

    this.getFullName = function() {
        return this.firstName + " " + this.lastName;
    };
}

1.11-2

function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
}

Person.prototype.getFullName = function() {
    return this.firstName + " " + this.lastName;
};

1.12 Closures
闭包:可以使用,但要慎重。
这里有一份闭包指南:http://jibbering.com/faq/notes/closures/

要注意到的一点是,一个闭包函数会在它的作用域内保留一个指针。
如果将一个闭包函数赋给DOM元素,会引起循环引用,并导致内存泄漏。例如:

function foo(element, a, b) {
  element.onclick = function() { /* uses a and b */ };
}

闭包函数会保持对element,a和b的引用,即使它从来不使用element。而element也保持对闭包的引用,这构成了一个循环,占用的内存将不会被回收。

应当使用下面的方式代替:

function foo(element, a, b) {
  element.onclick = bar(a, b);
}

function bar(a, b) {
  return function() { /* uses a and b */ }
}

注:闭包可以轻易产生循环引用,导致闭包和在闭包作用域链上的变量都不会被释放。这也正是能够通过闭包方式实现类私有成员的方法之一。对于不涉及DOM/ActiveX元素的闭包,在代码执行完毕后循环引用可以被JS引擎检测到,从而成功释放内存。当加入DOM/ActiveX后,会截断JS引擎的检测,导致内存泄漏。

阅读全文…

4,153 次浏览 | 2 条评论
2012年2月6日 | 归档于 程序

ExtJS4本地化

工作后干的活有点乱七八糟,被折磨死
每一段时间就要学新的语言。2011下半年是C#,2012年,轮换到了JavaScript

最近的任务是用ExtJS设计前端,这玩意强大到足以取代Silverlight,非常适合配合RESTful API使用,使用AJAX获取JSON或XML类型的数据,前端页面的生成完全不需要PHP/JSP,仅HTML+JS已经足够。这种情况下,前端可以和API所在服务端完全分离,部署在不同的服务器上,甚至前端可以放在用户本地运行

第一个任务是攻克多语言化(老大乃将这种任务扔给素人情何以堪)
网上搜索了一下,还有人专门写了插件(ext-locale-loader),但这种需要给每一自设计的页面弄一份语言拷贝的方式让余菊花一紧
后来阅读ExtJS的自带文档,发现有本地化的详细指引($EXTJS_FOLDER/docs/index.html#!/guide/localization)
一步步来就实现了ExtJS自身的UI元件在用户选择不同语言时的本地化

实现后的效果

演示页面:extlocalize.html
ExtJS语言列表RawData:languages.js(仅保留4个语言)
逻辑+UI:extlocalize.js
切换语言的逻辑其实非常简单,判断页面的传入参数(没错,静态页面也可以有参数),利用AJAX加载语言文件,然后执行语言文件中的代码,更新字符串

extlocalize.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Localization example</title>
    <!-- Ext Library Files -->
    <link rel="stylesheet" type="text/css" href="ext/resources/css/ext-all.css">
    <script src="ext/ext-all-debug.js"></script>
    <!-- App Scripts -->
    <script src="languages.js"></script>
    <script src="extlocalize.js"></script>
</head>
<body>
    <div id="languages"></div>
    <div id="datefield"></div>
    <div id="emailfield"></div>
    <div id="grid"></div>
</body>
</html>

languages.js

/**
 * by kuyur@kuyur.info
 * 2012.2.3
 */
Ext.namespace('Ext.local');

Ext.local.languages = [
    ['en', 'English'],
    ['ja', 'Japanese(日本語)'],
    ['zh_CN', 'Simplified Chinese(简体中文)'],
    ['zh_TW', 'Traditional Chinese(繁體中文)']
];

extlocalize.js

/**
 * by kuyur@kuyur.info
 * 2012.2.3
 */
Ext.Loader.setConfig({enabled: true});
Ext.Loader.setPath('Ext.ux', 'ext/examples/ux/');
Ext.require([
    'Ext.data.*',
    'Ext.tip.QuickTipManager',
    'Ext.form.*',
    'Ext.ux.data.PagingMemoryProxy',
    'Ext.grid.Panel'
]);

Ext.onReady(function() {

    MultiLangDemo = (function() {
        return {
            init: function() {
            	var store = Ext.create('Ext.data.ArrayStore', {
            	    fields: ['code', 'language'],
            	    data  : Ext.local.languages //from languages.js
            	});

            	var combo = Ext.create('Ext.form.field.ComboBox', {
            	    renderTo: 'languages',
            	    margin: '10, 0, 0, 10',
            	    store: store,
            	    displayField: 'language',
            	    queryMode: 'local',
            	    emptyText: 'Select a language...',
            	    hideLabel: true,
            	    width: 200,
            	    listeners: {
            	        select: {
            	            fn: function(cb, records) {
            	                var record = records[0];
            	                window.location.search = Ext.urlEncode({"lang":record.get("code")});
            	            },
            	            scope: this
            	        }
            	    }
            	});
            	
            	var params = Ext.urlDecode(window.location.search.substring(1));

            	if (params.lang) {
            	    var url = Ext.util.Format.format('ext/locale/ext-lang-{0}.js', params.lang);

            	    Ext.Ajax.request({
            	        url: url,
            	        success: this.onSuccess,
            	        failure: this.onFailure,
            	        scope: this
            	    });

            	    // check if there's really a language with passed code
            	    var record = store.findRecord('code', params.lang, null, null, null, true);
            	    // if language was found in store, assign it as current value in combobox

            	    if (record) {
            	        combo.setValue(record.data.language);
            	    }
            	} else {
            	    // no language found, default to english
            	    this.setup();
            	}

            	Ext.tip.QuickTipManager.init();
            },
            onSuccess: function(response) {
                try {
                    eval(response.responseText);
                } catch (e) {
                    Ext.Msg.alert('Failure', e.toString());
                }
                this.setup();
            },
            onFailure: function() {
                Ext.Msg.alert('Failure', 'Failed to load locale file.');
                this.setup();
            },
            setup: function() {
                Ext.create('Ext.FormPanel', {
                    renderTo: 'datefield',
                    margin: '10, 0, 0, 10',
                    frame: true,
                    title: 'Date picker',
                    width: 380,
                    defaultType: 'datefield',
                    items: [{
                        fieldLabel: 'Date',
                        name: 'date'
                    }]
                });
                Ext.create('Ext.FormPanel', {
                    renderTo: 'emailfield',
                    margin: '10, 0, 0, 10',
                    labelWidth: 100,
                    frame: true,
                    title: 'E-mail Field',
                    width: 380,
                    defaults: {
                        msgTarget: 'side',
                        width: 340
                    },
                    defaultType: 'textfield',
                    items: [{
                        fieldlabel: 'Email',
                        name: 'email',
                        vtype: 'email'
                    }]
                });

                var monthArray = Ext.Array.map(Ext.Date.monthNames, function (e) { return [e]; });
                var ds = Ext.create('Ext.data.Store', {
                     fields: ['month'],
                     remoteSort: true,
                     pageSize: 6,
                     proxy: {
                         type: 'pagingmemory',
                         data: monthArray,
                         reader: {
                             type: 'array'
                         }
                     }
                 });

                Ext.create('Ext.grid.Panel', {
                    renderTo: 'grid',
                    margin: '10, 0, 0, 10',
                    width: 380,
                    height: 203,
                    title:'Month Browser',
                    columns:[{
                        text: 'Month of the year',
                        dataIndex: 'month',
                        width: 240
                    }],
                    store: ds,
                    bbar: Ext.create('Ext.toolbar.Paging', {
                        pageSize: 6,
                        store: ds,
                        displayInfo: true
                    })
                });
                // trigger the data store load
                ds.load();
            }
        };
    })();

    MultiLangDemo.init();
});

以上仅是ExtJS UI自身的本地化。

自己系统中的文字如何本地化呢?
余的做法基本是沿着ExtJS本地化的思路,将系统用到的字符串集中在单个文件中,这也有利于后期扩展更多的语言
像Date picker/Email Field/Month Browser/Month of the year就是系统自身的语言,不应该硬编码到UI中
在加载ExtJS语言文件的时候,同时也加载系统自身的语言文件进行刷新
系统语言文件也必须使用和ExtJS语言文件相同的后缀,如en/ja/zh_CN/zh_TW

效果:

演示页面:multiplelanguages.html
逻辑+UI:multiplelanguages.js
语言列表RawData:languages.js(和上面一样)
系统默认语言文件(必须):myproject-js/myproject-lang.js(余将默认语言弄成了日文)
系统的本地化语言文件(可选):(没有参数时会保留默认语言文件中的设定)
locale/myproject-lang-en.js
locale/myproject-lang-ja.js
locale/myproject-lang-zh_CN.js
locale/myproject-lang-zh_TW.js

multiplelanguages.html

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Localization example</title>
    <!-- Ext Library Files -->
    <script src="myproject-js/myproject-lang.js"></script>
    <link rel="stylesheet" type="text/css" href="ext/resources/css/ext-all.css">
    <script src="ext/ext-all-debug.js"></script>
    <!-- App Scripts -->
    <script src="languages.js"></script>
    <script src="multiplelanguages.js"></script>
</head>
<body>
    <div id="languages"></div>
    <div id="datefield"></div>
    <div id="emailfield"></div>
    <div id="grid"></div>
</body>
</html>

multiplelanguages.js
将UI的生成全部集中到setup函数里了

/**
 * by kuyur@kuyur.info
 * 2012.2.3
 */
Ext.Loader.setConfig({
    enabled: true
});
Ext.Loader.setPath('Ext.ux', 'ext/examples/ux/');
Ext.require(['Ext.data.*', 'Ext.tip.QuickTipManager', 'Ext.form.*', 'Ext.ux.data.PagingMemoryProxy', 'Ext.grid.Panel']);

Ext.onReady(function() {

    var params;
    MultiLangDemo = (function() {
        return {
            init: function() {
                // load ExtJS locale
                params = Ext.urlDecode(window.location.search.substring(1));
                if (params.lang) {
                    var url = Ext.util.Format.format('ext/locale/ext-lang-{0}.js', params.lang);
                    Ext.Ajax.request({
                        url: url,
                        success: this.onLoadExtLocaleSuccess,
                        failure: this.onLoadExtLocaleFailure,
                        scope: this
                    });
                } else {
                    // no language found, locale of ExtJS will be english as default
                    this.loadmyprojectLocale();
                }
            },
            onLoadExtLocaleSuccess: function(response) {
                try {
                    eval(response.responseText);
                } catch (e) {
                    Ext.Msg.alert('Failure', e.toString());
                }
                this.loadmyprojectLocale();
            },
            onLoadExtLocaleFailure: function() {
                Ext.Msg.alert('Failure', 'Failed to load locale file.');
                this.loadmyprojectLocale();
            },
            loadmyprojectLocale: function() {
                // load locale for myproject
                if (params.lang) {
                    var urlmyprojectLocale = Ext.util.Format.format('locale/myproject-lang-{0}.js', params.lang);
                    Ext.Ajax.request({
                        url: urlmyprojectLocale,
                        success: this.onLoadmyprojectLocaleSuccess,
                        failure: this.onLoadmyprojectLocaleFailue,
                        scope: this
                    });
                } else {
                    this.setup();
                }
            },
            onLoadmyprojectLocaleSuccess: function(response) {
                try {
                    eval(response.responseText);
                } catch (e) {
                    Ext.Msg.alert('Failure', e.toString());
                }
                this.setup();
            },
            onLoadmyprojectLocaleFailue: function() {
                Ext.Msg.alert('Failure', 'Failed to load myproject locale file.');
                this.setup();
            },
            setup: function() {
                var store = Ext.create('Ext.data.ArrayStore', {
                    fields: ['code', 'language'],
                    data: Ext.local.languages //from languages.js
                });

                var combo = Ext.create('Ext.form.field.ComboBox', {
                    renderTo: 'languages',
                    margin: '10, 0, 0, 10',
                    store: store,
                    displayField: 'language',
                    queryMode: 'local',
                    emptyText: myproject.Message.SelectALanguage,
                    hideLabel: true,
                    width: 200,
                    listeners: {
                        select: {
                            fn: function(cb, records) {
                                var record = records[0];
                                window.location.search = Ext.urlEncode({
                                    "lang": record.get("code")
                                });
                            },
                            scope: this
                        }
                    }
                });
                if (params.lang) {
                    // check if there's really a language with passed code
                    var record = store.findRecord('code', params.lang, null, null, null, true);
                    // if language was found in store, assign it as current value in combobox
                    if (record) {
                        combo.setValue(record.data.language);
                    }
                }

                Ext.create('Ext.FormPanel', {
                    renderTo: 'datefield',
                    margin: '10, 0, 0, 10',
                    frame: true,
                    title: myproject.Message.PickDate,
                    width: 380,
                    defaultType: 'datefield',
                    items: [{
                        fieldLabel: myproject.Message.Date,
                        name: 'date'
                    }]
                });
                Ext.create('Ext.FormPanel', {
                    renderTo: 'emailfield',
                    margin: '10, 0, 0, 10',
                    labelWidth: 100,
                    frame: true,
                    title: myproject.Message.EmailFieldTitle,
                    width: 380,
                    defaults: {
                        msgTarget: 'side',
                        width: 340
                    },
                    defaultType: 'textfield',
                    items: [{
                        fieldlabel: 'Email',
                        name: 'email',
                        vtype: 'email'
                    }]
                });

                var monthArray = Ext.Array.map(Ext.Date.monthNames, function(e) {
                    return [e];
                });
                var ds = Ext.create('Ext.data.Store', {
                    fields: ['month'],
                    remoteSort: true,
                    pageSize: 6,
                    proxy: {
                        type: 'pagingmemory',
                        data: monthArray,
                        reader: {
                            type: 'array'
                        }
                    }
                });

                Ext.create('Ext.grid.Panel', {
                    renderTo: 'grid',
                    margin: '10, 0, 0, 10',
                    width: 380,
                    height: 203,
                    title: myproject.Message.MonthList,
                    columns: [{
                        text: myproject.Message.MonthTitle,
                        dataIndex: 'month',
                        width: 240
                    }],
                    store: ds,
                    bbar: Ext.create('Ext.toolbar.Paging', {
                        pageSize: 6,
                        store: ds,
                        displayInfo: true
                    })
                });
                // trigger the data store load
                ds.load();
            }
        };
    })();

    MultiLangDemo.init();
});

myproject-js/myproject-lang.js

/**
 * by kuyur@kuyur.info
 * 2012.2.3
 */
var myproject = {};
myproject.Message = {};
myproject.Message.SelectALanguage = '言語を選択ください...';
myproject.Message.PickDate = '日付を選択';
myproject.Message.Date = '日付';
myproject.Message.EmailFieldTitle = 'メールアドレス';
myproject.Message.MonthList = '月の一覧';
myproject.Message.MonthTitle = '月順';

locale/myproject-lang-en.js

/**
 * by kuyur@kuyur.info
 * 2012.2.3
 */
if (myproject.Message) {
	myproject.Message.SelectALanguage = 'Select a language...';
	myproject.Message.PickDate = 'Date Picker';
	myproject.Message.Date = 'Date';
	myproject.Message.EmailFieldTitle = 'Email';
	myproject.Message.MonthList = 'Month Browser';
	myproject.Message.MonthTitle = 'Month of the year';
}

locale/myproject-lang-ja.js

/**
 * by kuyur@kuyur.info
 * 2012.2.3
 */
if (myproject.Message) {
	myproject.Message.SelectALanguage = '言語を選択ください...';
	myproject.Message.PickDate = '日付を選択';
	myproject.Message.Date = '日付';
	myproject.Message.EmailFieldTitle = 'メールアドレス';
	myproject.Message.MonthList = '月の一覧';
	myproject.Message.MonthTitle = '月順';
}

locale/myproject-lang-zh_CN.js

/**
 * by kuyur@kuyur.info
 * 2012.2.3
 */
if (myproject.Message) {
	myproject.Message.SelectALanguage = '请选择一种语言...';
	myproject.Message.PickDate = '选择日期';
	myproject.Message.Date = '日期';
	myproject.Message.EmailFieldTitle = '电子邮件地址';
	myproject.Message.MonthList = '月份一览';
	myproject.Message.MonthTitle = '月份';
}

locale/myproject-lang-zh_TW.js

/**
 * by kuyur@kuyur.info
 * 2012.2.3
 */
if (myproject.Message) {
	myproject.Message.SelectALanguage = '請選擇一種語言...';
	myproject.Message.PickDate = '選擇日期';
	myproject.Message.Date = '日期';
	myproject.Message.EmailFieldTitle = '電子郵件地址';
	myproject.Message.MonthList = '月份一覽';
	myproject.Message.MonthTitle = '月份';
}

注意事项:
1.ExtJS库的解压目录名要一致,代码中的为ext
2.由于AJAX的本地请求会因为安全问题被浏览器禁止,需要将文件放到服务器才能测试

源文件下载:localize.rar

4,197 次浏览 | 没有评论
2012年2月3日 | 归档于 技术, 程序

C81战果

早上6点半才起来,基本上和C79同样的时间去到会场排队
今年的同音摊位好少,在西2逛几圈就能觅完,西2还有一堆是卖画册和周边的
根绝风筝酱提供的摊位消息,少女病和六弦alice在同一个摊位,
余虽然觉得很狐疑,但还是上前去询问,结果被回答不是的哦(囧rzorz
有少量团队伍超长,但名字居然没听过Orz(塩と胡椒/蛇下呂,这是卖CD的嘛?
感觉kaede.org简直就卖不动啊

去企业馆又逛了一下,路上遇到淫兽卖萌(对面的兄弟对不住了,码懒得打了

一日目企业馆的贞操还在大腿上,三日目就全都掉到了地板,エロすぎる!
Cosplay没有兴趣去拍,闪人回家吃饭了(企业馆的妹子都比外面广场的Cosplay有爱得多

战果,于是基本上一部psv就木有了cry

3,634 次浏览 | 3 条评论
2011年12月31日 | 归档于 私语
标签:

Java获取网卡接口名称的乱码问题

大概文字编码处理得多了,连同事遇到这方面问题,都跑过来问余了。
今晚同事遇到获取网卡接口名称乱码,无论怎么转都无法解决。
果然最好的方法还是查看字符串的二进制内容,乱猜是无用的。

平台是日文正版xp
NetworkInterface的getDisplayName()方法返回值是String,那确定就是一个Unicode字符串了
直接打印这个名称,最后一个”-“之后的字符串都不正常

使用String的getBytes(“UTF-16”)方法取出字串的二进制内容,全部打印出来,前几个以及最后几个的值如下

-2
-1
0
73  // -> I
0
110  // -> n
0
116  // -> t
0
101  // -> e
0
108  // -> l
...
...
...
0
-125
0
80
0
-125
0
87

首两个字符是-2,-1,也即0xFE,0xFF,大尾的BOM(Java你到底有多变态),每个字符都是高位在前
ASCII部分的字符高位为0,毫无疑问开头的几个字符就是Intel

来看最后几个字符
去掉0之后,序列是-125 80 -125 87,也即0x83 0x50 0x83 0x57,酷似多字节编码
打开WinHex(D版UltraEdit是不能装的),新建一个四字节文件,多字节编码在文本中都是先高位后低位,
于是按顺序写入16进制值,保存为txt文件
用记事本打开txt,结果是”ケジ”(日文版xp)
还可以用Ansi2Unicode打开,选择其他编码查看转换结果,Shift-JIS是最正常的了

于是终于可以下结论,这个字符串的原始编码就是Shift-JIS,但被分开成一个个byte,高位填充0,暴力转换为Unicode字符串。
这样的字符串非常的不正常,难怪怎么转都不对了

还原方法:去掉头部的BOM,去掉所有的0,得到二进制数组,在new一个String的同时,指定这个二进制数组的编码是Shift-JIS

import java.io.UnsupportedEncodingException;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;

public class hello {

	public static void main(String[] args) {
		System.out.println("日本語");
		try {
			Enumeration interfaces = NetworkInterface.getNetworkInterfaces();
			while (interfaces.hasMoreElements()) {
				NetworkInterface ni = (NetworkInterface)interfaces.nextElement();
				byte[] b = ni.getDisplayName().getBytes("UTF-16");
				byte[] dst = new byte[b.length];
				for (int i=0; i<dst.length; i++) {
					dst[i] = 0;
				}
				int k=0;
				for (int i=0; i<b.length; i++) {
					if ((b[i]!=0) && (b[i]!=-1) && (b[i]!=-2)) {
						dst[k] = b[i];
						k++;
					}
				}
				String name = new String(dst, "Shift_JIS").trim();
				System.out.println(b.length);
				System.out.println(ni.getDisplayName());
				System.out.println(name);
			}
		} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

猜测是JDK调用了win32 api,却没有处理返回来的字符串编码
同事说Win7平台上没有问题,不需要转换,可能是因为win7系统的api返回的是Unicode字符串
这个修正方法和平台相关,很不通用

3,318 次浏览 | 没有评论
2011年11月17日 | 归档于 程序
标签: , ,

Chinese Converter – 简繁繁简转换程序

稍微花了一点时间写了这个简繁繁简字符转换程序。主要为了验证通用库的扩展能力,程序功能不是目的,因此以后基本不会更新。

程序界面:

特性:
–简繁繁简转换
–仅支持Unicode(LE)输入和Unicode(LE)保存转换结果
–采用维基简繁繁简一对一映射表,如有问题找维基
–不支持词组转换

使用方法很简单,拖曳单个文本到程序窗口,就会自动转换。左上的下拉框可以选择恰当的映射表。

程序下载地址:http://code.google.com/p/unicue/downloads/detail?name=ChineseConverter_1.0.zip

4,203 次浏览 | 2 条评论
2011年11月13日 | 归档于 作品, 程序

猜猜看:哪一种转换方法最快

蛋疼写了三种UTF-16到UTF-8的转换方法。其中一个不出所料果然很慢,但另外的两个测试结果让余跌了一下眼镜。

直接在内存上操作速度当然快,因此converMethod中convert2utf8毫无疑问是最快的
剩下的
convert2utf8_pushback
convert2utf8_copy
convert2utf8_allocate
都是返回STL中的string对象,哪个最快,哪个最慢,大家来猜一下吧

convertMethod.h

#include <string>

bool match(const char *src, unsigned int src_length)
{
	if (NULL == src)
		return false;
	if (src_length == 0)
		return false;
	if ((src_length&1) != 0)
		return false;

	return true;
}

unsigned int calcUtf8StringLength(const char *src, unsigned int src_length, bool is_little_endian)
{
	if (NULL == src)
		return 0;
	if (src_length == 0)
		return 0;
	// if src_length is an odd number
	if ((src_length&1) != 0)
		return 0;

	const unsigned char *unsignedSrc = (unsigned char *)src;
	wchar_t chr = 0;
	unsigned int dest_len = 0;
	for (unsigned int i=0;i<src_length;)
	{
		if (is_little_endian)
			chr = *(unsignedSrc+i) + (*(unsignedSrc+i+1))*256;
		else
			chr = *(unsignedSrc+i+1) + (*(unsignedSrc+i))*256;
		i+=2;

		if (chr <= 0x007F)  // U-0000 - U-007F
		{
			dest_len += 1;
		} else if (chr <= 0x07FF) {  // U-0080 - U-07FF
			dest_len += 2;
		} else if (chr >= 0xD800 && chr <= 0xDFFF) {  // U-D800 - U-DFFF
			dest_len += 0;
		} else {  // U-0800 - UD7FF and UE000 - UFFFF
			dest_len += 3;
		}
	}

	return dest_len;
}

std::string convert2utf8_pushback(const char *src, unsigned int src_length, bool is_little_endian)
{
	std::string utf8str;
	utf8str.clear();

	if (NULL == src)
		return utf8str;
	if (src_length == 0)
		return utf8str;
	// if src_length is an odd number
	if ((src_length&1) != 0)
		return utf8str;

	const unsigned char *unsignedSrc = (unsigned char *)src;
	wchar_t chr = 0;
	char dest_chars[3];
	memset((void*)dest_chars, 0, 3);
	unsigned int dest_len;
	for (unsigned int i=0;i<src_length;)
	{
		dest_len = 0;
		if (is_little_endian)
			chr = *(unsignedSrc+i) + (*(unsignedSrc+i+1))*256;
		else
			chr = *(unsignedSrc+i+1) + (*(unsignedSrc+i))*256;
		i+=2;

		if (chr <= 0x007F)  // U-0000 - U-007F
		{
			dest_len = 1;
			dest_chars[0] = (char)chr;
		} else if (chr <= 0x07FF) {  // U-0080 - U-07FF
			dest_len = 2;
			dest_chars[0] = (char)(0xC0 | (chr>>6));
			dest_chars[1] = (char)(0x80 | (chr&0x003F));
		} else if (chr >= 0xD800 && chr <= 0xDFFF) {  // U-D800 - U-DFFF
			// ignore this unicode character
			dest_len = 0;
		} else {  // U-0800 - UD7FF and UE000 - UFFFF
			dest_len = 3;
			dest_chars[0] = (char)(0xE0 | (chr>>12));
			dest_chars[1] = (char)(0x80 | ((chr>>6) & 0x003F));
			dest_chars[2] = (char)(0x80 | (chr & 0x003F));
		}
		for (unsigned int j=0;j<dest_len;j++)
		{
			utf8str.push_back(dest_chars[j]);
		}
	}

	return utf8str;
}

std::string convert2utf8_copy(const char *src, unsigned int src_length, bool is_little_endian)
{
	if (NULL == src)
		return std::string();
	if (src_length == 0)
		return std::string();
	// if src_length is an odd number
	if ((src_length&1) != 0)
		return std::string();

	unsigned int need_length = calcUtf8StringLength(src, src_length, is_little_endian);
	char* dest = new char[need_length+1];
	char* offset = dest;
	memset((void*)dest, 0, need_length+1);

	const unsigned char *unsignedSrc = (unsigned char *)src;
	wchar_t chr = 0;
	char dest_chars[3];
	memset((void*)dest_chars, 0, 3);
	unsigned int dest_len;
	for (unsigned int i=0;i<src_length;)
	{
		dest_len = 0;
		if (is_little_endian)
			chr = *(unsignedSrc+i) + (*(unsignedSrc+i+1))*256;
		else
			chr = *(unsignedSrc+i+1) + (*(unsignedSrc+i))*256;
		i+=2;

		if (chr <= 0x007F)  // U-0000 - U-007F
		{
			dest_len = 1;
			dest_chars[0] = (char)chr;
		} else if (chr <= 0x07FF) {  // U-0080 - U-07FF
			dest_len = 2;
			dest_chars[0] = (char)(0xC0 | (chr>>6));
			dest_chars[1] = (char)(0x80 | (chr&0x003F));
		} else if (chr >= 0xD800 && chr <= 0xDFFF) {  // U-D800 - U-DFFF
			// ignore this unicode character
			dest_len = 0;
		} else {  // U-0800 - UD7FF and UE000 - UFFFF
			dest_len = 3;
			dest_chars[0] = (char)(0xE0 | (chr>>12));
			dest_chars[1] = (char)(0x80 | ((chr>>6) & 0x003F));
			dest_chars[2] = (char)(0x80 | (chr & 0x003F));
		}
		if (dest_len>0)
		{
			memcpy(offset, dest_chars, dest_len);
			offset+=dest_len;
		}
	}

	std::string utf8str(dest);
	delete []dest;
	return utf8str;
}

std::string convert2utf8_allocate(const char *src, unsigned int src_length, bool is_little_endian)
{
	if (NULL == src)
		return std::string();
	if (src_length == 0)
		return std::string();
	// if src_length is an odd number
	if ((src_length&1) != 0)
		return std::string();

	unsigned int need_length = calcUtf8StringLength(src, src_length, is_little_endian);
	std::string utf8str(need_length, 0);
	unsigned int offset = 0;

	const unsigned char *unsignedSrc = (unsigned char *)src;
	wchar_t chr = 0;
	char dest_chars[3];
	memset((void*)dest_chars, 0, 3);
	unsigned int dest_len;
	for (unsigned int i=0;i<src_length;)
	{
		dest_len = 0;
		if (is_little_endian)
			chr = *(unsignedSrc+i) + (*(unsignedSrc+i+1))*256;
		else
			chr = *(unsignedSrc+i+1) + (*(unsignedSrc+i))*256;
		i+=2;

		if (chr <= 0x007F)  // U-0000 - U-007F
		{
			dest_len = 1;
			dest_chars[0] = (char)chr;
		} else if (chr <= 0x07FF) {  // U-0080 - U-07FF
			dest_len = 2;
			dest_chars[0] = (char)(0xC0 | (chr>>6));
			dest_chars[1] = (char)(0x80 | (chr&0x003F));
		} else if (chr >= 0xD800 && chr <= 0xDFFF) {  // U-D800 - U-DFFF
			// ignore this unicode character
			dest_len = 0;
		} else {  // U-0800 - UD7FF and UE000 - UFFFF
			dest_len = 3;
			dest_chars[0] = (char)(0xE0 | (chr>>12));
			dest_chars[1] = (char)(0x80 | ((chr>>6) & 0x003F));
			dest_chars[2] = (char)(0x80 | (chr & 0x003F));
		}
		for (unsigned int j=0;j<dest_len;j++)
			utf8str[offset++] = dest_chars[j];
	}

	return utf8str;
}

bool convert2utf8(const char *src, unsigned int src_length, char *dest, unsigned int dest_length, bool is_little_endian, bool check_dest_length=false)
{
	if (NULL == src || NULL == dest)
		return false;
	if (0 == src_length || 0 == dest_length)
		return false;
	if (!(dest>src+src_length || src>dest+dest_length))
		return false;
	if ((src_length&1) != 0)
		return false;
	if (check_dest_length)
	{
		if (dest_length < calcUtf8StringLength(src, src_length, is_little_endian))
			return false;
	}

	unsigned int offset = 0;
	const unsigned char *unsignedSrc = (unsigned char *)src;
	wchar_t chr = 0;
	memset((void*)dest, 0, dest_length);
	for (unsigned int i=0;i<src_length;)
	{
		if (is_little_endian)
			chr = *(unsignedSrc+i) + (*(unsignedSrc+i+1))*256;
		else
			chr = *(unsignedSrc+i+1) + (*(unsignedSrc+i))*256;
		i+=2;

		if (chr <= 0x007F)  // U-0000 - U-007F
		{
			*(dest + offset++) = (char)chr;
		} else if (chr <= 0x07FF) {  // U-0080 - U-07FF
			*(dest + offset++) = (char)(0xC0 | (chr>>6));
			*(dest + offset++) = (char)(0x80 | (chr&0x003F));
		} else if (chr >= 0xD800 && chr <= 0xDFFF) {  // U-D800 - U-DFFF
			// ignore this unicode character
		} else {  // U-0800 - UD7FF and UE000 - UFFFF
			*(dest + offset++) = (char)(0xE0 | (chr>>12));
			*(dest + offset++) = (char)(0x80 | ((chr>>6) & 0x003F));
			*(dest + offset++) = (char)(0x80 | (chr & 0x003F));
		}
	}

	return true;
}

测试程序:在VC新建一个工程把代码覆盖进去,并导入convertMethod.h
测试文件的路径需要更改一下,推荐用大文本测试,更能显示出性能的差异
测试文件只能是Unicode编码的文本文件
提供测试样品:bungakusyoujyo-unicode

#pragma once

#include "stdafx.h"
#include <string>
#include <fstream>
#include <iostream>
#include <time.h>
#include "convertMethod.h"

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	string filename = "../testfiles/utf-16/bungakusyoujyo-unicode.txt";
	ifstream infile(filename.c_str(), ios::in|ios::binary);
	if (!infile)
	{
		cerr<<"unable to open input file!\n";
		return -1;
	}
	ofstream outfile("../testfiles/out/bungakusyoujyo-utf-8.txt", ios::binary);
	if (!outfile)
	{
		cerr<<"unable to open output file\n";
		infile.close();
		return -1;
	}

	infile.seekg(0, ios::end);
	unsigned int rawLength = infile.tellg();
	char *rawStringBuffer = new char[rawLength+1];
	memset((void*)rawStringBuffer, 0, rawLength+1);
	infile.seekg(0, 0);
	infile.read(rawStringBuffer, rawLength);
	char *stringBuffer = rawStringBuffer + 2;
	unsigned int length = rawLength - 2;

	bool isLittleEndian=false, hasError=false;
	if (((unsigned char)rawStringBuffer[0]==0xFF)&&((unsigned char)rawStringBuffer[1]==0xFE))
		isLittleEndian = true;
	else if (((unsigned char)rawStringBuffer[1]==0xFF)&&((unsigned char)rawStringBuffer[0]==0xFE))
		isLittleEndian = false;
	else
	{
		hasError = true;
	}

	if (!hasError)
	{
		char UTF_8_BOM[3] = {'\xEF', '\xBB', '\xBF'};
		outfile.write(UTF_8_BOM,3);

		if (match(stringBuffer, length))
		{
			cout<<"Unicode matched!"<<endl;
			// output

			long beginTime, endTime;

			/*method 1*/
			beginTime = clock();
			string& resultBuffer1 = convert2utf8_pushback(stringBuffer, length, isLittleEndian);
			endTime = clock();
			cout<<"utf-8 string length: "<<resultBuffer1.length()<<endl;
			cout<<"pushback method costs time: "<<endTime - beginTime<<"ms"<<endl;

			/*method 2*/
			beginTime = clock();
			string& resultBuffer2 = convert2utf8_copy(stringBuffer, length, isLittleEndian);
			endTime = clock();
			cout<<"utf-8 string length: "<<resultBuffer2.length()<<endl;
			cout<<"copy construct method costs time: "<<endTime - beginTime<<"ms"<<endl;

			/*method 3*/
			beginTime = clock();
			string& resultBuffer3 = convert2utf8_allocate(stringBuffer, length, isLittleEndian);
			endTime = clock();
			cout<<"utf-8 string length: "<<resultBuffer3.length()<<endl;
			cout<<"allocate method costs time: "<<endTime - beginTime<<"ms"<<endl;

			/*method 4*/
			beginTime = clock();
			unsigned int dst_length = calcUtf8StringLength(stringBuffer, length, isLittleEndian);
			char *dst = new char[dst_length];
			bool result = convert2utf8(stringBuffer, length, dst, dst_length, isLittleEndian);
			endTime = clock();
			if (!result)
				cerr<<"Fail to convert to utf-8\n";
			else
			{
				cout<<"utf-8 string length: "<<dst_length<<endl;
				cout<<"memory copy method costs time: "<<endTime - beginTime<<"ms"<<endl;
				/*writing method 1*/
				beginTime = clock();
				outfile.write(dst, dst_length);
				endTime = clock();
				cout<<"writing file by pointer costs time: "<<endTime - beginTime<<"ms"<<endl;
			}
			delete []dst;
			dst = NULL;

			/*writing method 2*/
			/*
			const char *p = resultBuffer1.c_str();
			unsigned int resultLength = resultBuffer1.length();
			beginTime = clock();
			outfile.write(p, resultLength);
			endTime = clock();
			cout<<"writing file by pointer costs time: "<<endTime - beginTime<<"ms"<<endl;
			*/
		
			/*writing method 3*/
			/*
			beginTime = clock();
			outfile<<resultBuffer2;
			endTime = clock();
			cout<<"writing file by stream costs time: "<<endTime - beginTime<<"ms"<<endl;
			*/
		}
	}

	infile.close();
	outfile.close();
	delete []rawStringBuffer;
	rawStringBuffer = NULL;
	stringBuffer = NULL;

	return 0;
}
12,119 次浏览 | 2 条评论
2011年11月8日 | 归档于 技术, 程序
标签: , , ,

三訪秩父 – 龍勢祭り

第一季:https://kuyur.info/blog/archives/1738
第二季:https://kuyur.info/blog/archives/2000

TV Animeあの花中的花火取材自于秩父吉田地区的民俗,龍勢(りゅうぜい)祭り。
龍勢祭り是崎玉县立无形文化财,于每年10月第二个星期日举行。
说是花火,龍勢本质上是火箭。一般的烟花在离开炮筒之后就没有推力,只能靠初速冲到最高点。龍勢靠的自身产生的反冲。
另外在空中产生的缤纷烟雾流是一大特色,虽然也有火药的燃烧,但和一般烟花猛烈爆炸完全不同

10月9日,为了一睹あの花中的花火实景,再一次来到了秩父。这次的照片非常少

早上8点50起来,自从搬家后离池袋已经很远,应该再起早一点就好了(都怪昨晚一直在调教Google Voice)
抵达池袋10点06分,西武铁道前往秩父的特急满席!
只好乘坐普通列车,到了饭能转车的时候居然又等了将近30分钟
这已经决定余赶不上1点钟的由“超平和バスターズ”奉納的第15番火花了

约1点10分的时候,余还在前往龍勢会館的巴士上,即将抵达会馆,然后就在巴士上看着15番花火升上了空
其实30个流派里和动画最神似的不一定是15番,但没听到茅野爱衣现场卖萌,没能近距离目睹这一阿宅盛事,还是非常残念

残念归残念,收拾心情前往花火会场。
在靠近会场时堵得要死,穿过人群终于找到比较好的地点

大家围成一圈(身后是有料席)

龍勢祭り的主角们就不打码了

失败率非常高。余看到3点多,真正成功的只有三个。
像这个没怎么升起就掉到了半山腰

旁边的女孩子们还在担心,咿呀会不会引起火灾之类的

终于拍了一个成功的,也是余所看到的最壮观一个

吊装。在吊装的同时,主持人先会あいさつ,介绍流派,奉纳人和奉纳人的心愿之类。
然后是叙述人的あいさつ和由鼓声伴奏的很搞笑的吟唱。在吟唱快结束时点火

升空

抵达最高点

如同鲜花般盛开

填装在竹竿上的烟雾依次散发

大概是竹竿有降落伞吊着,下降速度比较慢

烟雾随着下降的竹竿拉出彩帘。

阅读全文…

2,695 次浏览 | 没有评论
2011年10月10日 | 归档于 私语

使用service启动tomcat服务导致的log4j输出文字异常

在Amazon的EC2上,用Amazon官方Linux AMI创建了一个实例,安装并配置tomcat,部署应用。
偶然一次使用service tomcat start启动服务,发现log4j输出的log中的日文字符统统变成了问号「?」。但使用/etc/init.d/tomcat启动服务却一切正常。

以前余就知道service方式启动服务会忽略掉很多环境变量,比如JAVA_HOME。
使用man命令查看帮助
[root]# man service

ENVIRONMENT
       LANG, TERM
              The only environment variables passed to the init scripts.

说是service会保留LANG和TERM两个环境变量。(事实上PATH环境变量也会传递进去)

仔细查看service脚本,却完全不是这回事。

[root]# which service
/sbin/service
[root]# vim /sbin/service

cd /
while [ $# -gt 0 ]; do
  case "${1}" in
    --help | -h | --h* )
       echo "${USAGE}" >&2
       exit 0
       ;;
    --version | -V )
       echo "${VERSION}" >&2
       exit 0
       ;;
    *)
       if [ -z "${SERVICE}" -a $# -eq 1 -a "${1}" = "--status-all" ]; then
          cd ${SERVICEDIR}
          for SERVICE in * ; do
            case "${SERVICE}" in
              functions | halt | killall | single| linuxconf| kudzu)
                  ;;
              *)
                if ! is_ignored_file "${SERVICE}" \
                    && [ -x "${SERVICEDIR}/${SERVICE}" ]; then
                  env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" status
                fi
                ;;
            esac
          done
          exit 0
       elif [ $# -eq 2 -a "${2}" = "--full-restart" ]; then
          SERVICE="${1}"
          if [ -x "${SERVICEDIR}/${SERVICE}" ]; then
            env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" stop
            env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" start
            exit $?
          fi
       elif [ -z "${SERVICE}" ]; then
         SERVICE="${1}"
       else
         OPTIONS="${OPTIONS} ${1}"
       fi
       shift
       ;;
   esac
done

if [ -f "${SERVICEDIR}/${SERVICE}" ]; then
   env -i PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" ${OPTIONS}
else
   echo $"${SERVICE}: unrecognized service" >&2
   exit 1
fi

本来系统的默认locale是en_US.UTF-8,也即环境变量$LANG的值为en_US.UTF-8,
/etc/init.d/tomcat start方式启动tomcat,除非在启动脚本中干啥坏事,否则全部的环境变量都会保留,log4j输出不会有错误

env -i命令是创建一个空白的运行环境,如果不主动传入参数,所有的环境变量都会被忽略。
Amazon官方Linux AMI的service脚本用env -i创建启动环境时,却删掉了LANG=”$LANG”,也即locale变成了POSIX,log4j输出时按POSIX输出,非ASCII字符全变成了问号

Amazon官方Linux AMI修改自CentOS,但人家CentOS的service脚本LANG保留得好好的
ubuntu的service脚本文件是/usr/sbin/service,LANG同样传递了进去

为了排查这个错误,花了一下午时间,狠命死抽Amazon

改正方法很简单,补回LANG环境变量即可

env -i LANG="$LANG" PATH="$PATH" TERM="$TERM" "${SERVICEDIR}/${SERVICE}" ...

一共四处地方

3,206 次浏览 | 没有评论
2011年10月3日 | 归档于 技术

动态加载字符映射表的字符转换环境方案

余去年写Ansi2Unicode的时候,不使用win32 api进行字符转换,使用了自定义转换函数。几个不同编码的转换函数,写起来大同小异。特别是GBK和Big5,可以说是完全一样。于是很早就想开发一个动态加载字符映射表的字符转换环境,当添加新的编码时,不需要改动程序。

现在考虑到的一些特征:
1.独立于平台。Ansi2Unicode的转换函数(toUnicode.h)严重依赖MFC,通用性很差,余一直不太满意
2.字符映射表的信息保存在配置文件(charmap.xml文件),由环境动态加载。环境是一个通用环境,普适各种多字节编码
3.用户制作映射表和添加新的映射表信息遵循一定的规则
4.平台即使在运行时都可以重新载入映射表
5.字符映射表使用优先顺序由在配置文件中出现的顺序决定。用户可调整顺序

这个平台的扩展性非常强。制作映射表,修改charmap.xml,一种新的编码转换方法就添加了,有别于以前需要多写一个函数。
另外,用户还可以替换映射表,随着字符编码的升级而升级。

初步的charmap.xml配置文件

<?xml version="1.0" encoding="utf-8" ?>
<charmaps>
	<charmap>
		<name>Shift-JIS</name>
		<version>Microsoft CP932</version>
		<useinautocheck>true</useinautocheck>
		<path>charmaps/jis2u-little-endian.map</path>
		<readingpolicy begin="0x00" end="0x7F" readnext="false" />
		<readingpolicy begin="0x80" end="0xA0" readnext="true" />
		<readingpolicy begin="0xA1" end="0xDF" readnext="false" />
		<readingpolicy begin="0xE0" end="0xFF" readnext="true" />
		<segment begin="0x00" end="0x7F" comparefrom="ascii" type="ascii" />
		<segment begin="0x80" end="0xA0" comparefrom="0xFFFD" type="undefine" />
		<segment begin="0xA1" end="0xDF" comparefrom="buffer" offset="0" type="JIS-X-0201" />
		<segment begin="0xE0" end="0x813F" comparefrom="0xFFFD" type="undefine" />
		<segment begin="0x8140" end="0xFFFF" comparefrom="buffer" offset="63" type="JIS-X-0208" />
	</charmap>
	<charmap>
		<name>GBK</name>
		<version>Microsoft CP936(mozilla enhance)</version>
		<useinautocheck>true</useinautocheck>
		<path>charmaps/gb2u-little-endian.map</path>
		<readingpolicy begin="0x00" end="0x7F" readnext="false" />
		<readingpolicy begin="0x80" end="0xFF" readnext="true" />
		<segment begin="0x00" end="0x7F" comparefrom="ascii" type="ascii" />
		<segment begin="0x80" end="0x813F" comparefrom="0xFFFD" type="undefine" />
		<segment begin="0x8140" end="0xFFFF" comparefrom="buffer" offset="0" type="gbk-multiple-char" />
	</charmap>
	<charmap>
		<name>BIG5</name>
		<version>Unicode-at-on (UAO) 2.50</version>
		<useinautocheck>true</useinautocheck>
		<path>charmaps/b2u-little-endian.map</path>
		<readingpolicy begin="0x00" end="0x7F" readnext="false" />
		<readingpolicy begin="0x80" end="0xFF" readnext="true" />
		<segment begin="0x00" end="0x7F" comparefrom="ascii" type="ascii" />
		<segment begin="0x80" end="0x813F" comparefrom="0xFFFD" type="undefine" />
		<segment begin="0x8140" end="0xFFFF" comparefrom="buffer" offset="0" type="big5-multiple-char" />
	</charmap>
</charmaps>
4,690 次浏览 | 1 条评论
2011年7月29日 | 归档于 技术, 程序
标签:

口胡论文一篇

这次回家休假,翻了下家里的旧电脑,发现研一时候写的一篇论文。重新读了一遍,发现很多文句有不严密之处,但时过3年,观点还是没有发生太大的变化,故贴上来。余早已经不是忧国忧民的FQ了,倘若你读得不顺眼开喷也请口下留情。此文是首次在余blog发布,转载是拒绝的。抄袭神马的也是可耻的

自然辩证法期末论文

科学技术的尽头

关键字:热力学第二定律 熵 可持续发展 物质混乱

摘要:人类的发展是否能随科学技术的进步无限进行下去?本文将利用热力学第二定律分析人类目前面临的发展困境,进行力所能及的探讨。

一、引言

小时候,我一直是科学家的崇拜者。那段时候,憧憬的对象一直在变化,有时候是宇航员,有时候是居里夫人式的科学家,虽然连科学家的概念都弄不清楚,但这种崇拜之情的对象说成科学家总不会有错。可能是作为小孩子时读的科普读物太多,不过现在细想回来,那些科普读物的主观导向性非常强,基本来说就是让你崇拜上科学技术,崇拜上科学家。我想,大概有两点:一、科学家都是崇高的,无私的,胸怀广阔为全人类服务的;二、科学技术是强大的,它改善我们的生活,几乎无所不能就算现在不能将来还是能而且是无害的。如今我当科学家估计是没戏了,但技术算是被附上身了,总算对小时候的理想有了点交代。

科普读物教给我的两个观点,估计现在都被我否定了,也可谓是教育的一种失败,怎么说都是书作者的一种失败。因此在这里给个建议现在写科普的作家,不要尽拍科学的马屁,好歹也写些反面例子吧,虽然我心理承受能力强,但难免有些单纯的孩子日后接触到残酷的现实不会精神分裂。

话归正题,这两个观点,第一个现在估计也就是能拿来哄哄小孩而已了,本文要探讨的是第二个观点。这世界上大体可以分为两类人——悲观者和乐观者,长了这么大,对科学技术也有过质疑,心中也有过思考,但我想我还是一位乐观者。我以前相信科学技术带来的问题可以随着科学技术的发展而得到解决,这也是很多乐观者的看法,不过现在我更倾向于认为它是一个神话。但我仍然是个乐观者,我善意地希望,人类不会自己毁灭自己。

下面介绍一些必要的理论。

二、热力学第二定律

最早被热力学第二定律2吸引是在高中时代,那时候语文改版,新增加了一系列课文,其中一篇是介绍热力学方面有关熵的可以说成是科普读物的文章。不过后来读大学后和物理的关系就像一点出发的两条射线,而读研究生时简直就是背道而驰了。热力学第二定律(简称热二律)实在有意思,不管是谁提出,也不管那些复杂烦人的公式,它说:熵(entropy)是正增加的。熵是什么?熵是系统无序度的度量。最早的无序度只是指能量,要不这个定理也不会叫热力学第二定律了,但后来人们意识到物质也是从有序向无序转化,这两者都是不可逆过程。都说人类一思考,上帝便发笑,热力学定律对宇宙来说无关重要,到底在什么条件下能被推翻对宇宙来说也无关重要,重要的只是它对人类的影响。对于能源和资源,人类的眼里除了开发和利用,别无其他,就算是保护也是为了更好的开发,有用和无用,热二律正是为人类而生。为了简单的介绍热二律的影响,牵扯到系统之类就太过学院化,通俗的说,从人类的角度出发,能量或物质被使用后,由低熵态变为高熵态,由有序的组织状态转变为无序的组织状态,虽然能量和物质不灭,但可用的数量却越来越少。当所有的物质和能量都变得无序,这便是热寂,但功利的人类自然不必去担心宇宙的命运。

高中时我就思考时光能不能倒流的问题,想想我头脑也算是不错的了,接触到热二律居然给我想出了证明过程。现实的世界熵只会增加,虽然你没有意识到,但一秒后的世界比现在要无序。时光要倒流,哪怕是只倒流一秒,也要将无序的世界变得更有序一点。这违反了热二律。好了,要时光倒流,你先去推翻热二律。后来接触到混沌理论,我又将时光隧道给推翻了。混沌理论说,你不能预测未来。一个确定的现在,就算将所有的变量都找出来,将所有的变化趋势用任意阶的偏微分方程(当然我那时候不会懂得偏微分方程)去描述,还是不能计算出未来,坚持能计算出来的,那是机械唯物主义。也即是说一个确定的现在对应着无数个可能的未来。穿越时空隧道回到现在,意味着未来也已经确定。这违反了混沌理论。曾今一度为自己的推理兴奋,过早的洞察使我少了很多迷惘,但也较同龄失去了许多幻想。

但理论的制高点未必使你更有说服力。我父亲曾经相信他自己能做出永动机,我发现我怎么都说不服他,他不会理睬你的理论。唯一的办法是让他自己做,也即是实验。写本文时,想不到还能搜到一条相关新闻3

热二律告诉我们,有用的物质和能量只会越用越少,同时无用的物质和能量越来越多。无用的物质被人类称之为垃圾。

三、乐观论和悲观论

 

中国从来都不缺乏一些异想天开的专家。他们提出一些骇人听闻的构思,如炸喜马拉雅山脉,引印度洋水气上青藏高原以缓解中国水资源压力;又如断山东半岛开凿运河沟通渤黄两海以缓解渤海水污染。这些人是科技狂热派,他们相信科技无所不能,比乐观者还乐观。正在实施的南水北调工程虽说不是异想天开,但仍然可以作为科技无所不能的一个代表。

对于人类面临的发展困境,乐观者通常认为是可以随着科技的进步得以解决。不管能不能得到解决,首先我认为这是一个非常不负责的观点。我们到底给后人留下了什么?污染的空气、水、土地,如山的废弃物,几乎被采空的能源储备,全球变暖。人类发展困境,这是又一个切尔诺贝利核电站,当代人类无能力去处理,只好用厚厚的水泥封住,将难题推给了后人,这是一个多么不负责任的态度!我们应当做些什么而不是全部留给后人。

这时悲观论者站了出来。放弃科技,回到过去的刀耕火种、小国寡民的时代吧。古代的发展方式能够可持续发展,这是历史做的实验。虽然热二律告诉我们热寂最终会来临,但那是以亿年为量级的时间后的事。小国寡民,就这样经营地球能持续个数十万年就已经心满意足。

小国寡民为什么行得通?人类是负熵的物体。熵是物质混乱的标志,而负熵则标志着这个物体高度有序,比平均的有序程度还要有序得多。如果要制造一个这样的物体,必须输入大量的有用能量和物质,也即以其他物质和能量变无序为代价。要制造人类这样一个负熵物体,消耗有序物质和能量最低的方法是生一个小孩然后抚养他长大,一次一个同学异想天开说制造一个微粒组装机器,按照物质的结构一个个把微粒组装起来就可以生成人类和其他物体,我说算了吧,造点简单的东西如金块啊可能还算可以,造人类你还比得过地球花了四十六亿年自然进化才得来的方法。负熵是自然选择的结果,物种必须更负熵才更有竞争力才能生存下去。但刀耕火种的人类未必比动物更负熵多少。就算生存一百年,只管吃和喝,也不过作为生物链的顶端输出多一点无序物质罢了。地球作为一个半封闭系统,通常认为只和外界交换能量而不交换物质。地球从太阳接受有序的能量(太阳能),向宇宙辐射无序的能量(微波辐射),维持了地球的负熵状态。人类低当量的活动带来的熵很快就会被太阳能的负熵抵消,人类砍伐树木,捕猎动物,开垦荒地种取粮食,只要不突破一个界限,破坏很快就会被自然修补。这是古代的农耕文明所论述的天人合一,也正是小国寡民能维持下去的原因。田松在《第三类永动机》1里论述了只和外界交换能量的半封闭系统也不可能可以维持下去,但无论如何,正如我上文所述,没有宇宙天灾,人类要小国寡民地经营地球几十万年总不会是难事。

但从高度发达的工业文明重返农耕文明,这是个倒退。只要这个世界不被极端的环保主义者悲观论者所把持,这就不可能发生,而我受过的十几年的马克思主义教育也告诉我这不可能发生。我们的科学技术难道做不了什么了吗?

四、论可持续发展

世界提出可持续发展已经很多年,很多人拿可持续发展作为救命稻草。就算在中国,我印象中九十年代读小学时就听过这个概念。但如今看来,也越发像个神话。

我曾经一段时间很崇拜欧洲,因为我觉得他们基本实现了可持续发展。看过新闻报道鱼类重返泰晤士河,看过德国鲁尔重工业区的成功改造案例,还有数不胜数的标志欧洲的自然环境得到恢复的照片。伴随着的是中国逐渐成为世界的制造中心,中国的污染日益严重,中国的治污治不胜治。然而,深思后发现,这两者其实存在着必然联系,欧洲的可持续发展是伪可持续发展,他们的环境恢复是以中国的环境破坏为代价。在欧洲乃至日本美国,他们所需的很多生活用品工业初级品都是从中国进口,他们为什么不制造?代价太巨大了,工人的工资只是很小的一部分原因;制作这些商品的环境成本太高,由于法律的高度完善,破坏环境的道路已经行不通,而从技术角度上,不影响环境地生产也是个神话。所以并非欧洲人觉悟有多高,他们也只是从商业利益的角度看待问题。因此制造业转移到了中国,用一句话说,中国成为了世界的烟囱。中国的人力成本低,另外一个利好消息时,政府会和你同穿一条裤子,民众投诉环境问题,有政府帮你压下去。西方人的可持续发展实质是将高熵转移到中国,以实现自己的负熵,他们没有解决熵的问题,只是将问题转移。但中国的环境崩溃了,中国排放的熵全球化(最典型的例子,温室气体排放)了,他们还能继续逍遥自在吗?因此他们的发展是伪可持续发展。

远古时候,地球一片荒凉,我们以此为参考点,将熵值定为零。经过太阳四十六亿年的照射,直到人类出现,地球越发变得有序。整个地球表面被生物圈所覆盖,生机盈然。这是一个高度负熵状态。太阳的恩泽不但实现了地球的负熵,而且还有盈余,多余的负熵被储存了起来,成为人类日后称之为煤炭石油的东西。人类出现,只不过多了一个生物物种,人类工业社会的出现,对地球而言才真正是一个灾难。

人类工业社会又是一个超负熵的存在。它比我们全人类自身生存仅需要的负熵高上几个数量级。自从有了工业社会,我们已经不是只考虑吃的问题。从生下的那一天,总得有个地方住吧,因此有了房子;到长大成人能够自行独立前,要学会能生存乃至发展的能力吧,因此有了教育;要出行呀,因此有了道路和交通工具;交通工具制造要金属啊,跑起来要烧油啊,因此有了冶金工业炼油工业。人类还不满足,身体歇了,脑子也想歇会,因此有了计算机;还要朝闻天下,有了电视电台网络。人类永远都得不到满足,他们津津乐道于一个概念:GDP。人类社会是一个超负熵的锅炉,它处于一个严谨有序的状态,社会学家说比自然界都要有序得多了。要维持这样一个状态,必须输入大量的负熵物质和能量,排出高熵的废弃物。负熵的物质来自哪里?森林、淡水、空气、土地、矿石、能源、生物物种……人类想尽一切办法利用能利用的东西,将它们统统投入锅炉。同时排出的高熵废弃物谁帮我们解决?农业社会的人类是全交给了自然,工业社会的人类是一半交给了自然,一半交给了上帝。好了,我们看看自然的负担有多重,她不仅要维持自己的负熵状态,她还要替人类社会维持负熵状态。我们将自然能给我们解决的部分称之为可再生资源,连自然都不能解决的部分,也即是上帝那部分,我们称之为危机。

自然能够给我们提供的负熵和能够给我们处理的熵都是有限度的,这个限度最大的标志是她维持自身负熵的最低状态,突破这个状态,自然就崩溃了。如果未来人类还能存在,从热力学的角度来看,他们所做的并没有比刀耕火种小国寡民更先进,他们不过也是同样维持了自身的负熵和自然的负熵,更完美点,还略有盈余。

五、我们能做些什么

 

前文说过,工业社会的人类是将不能处理的高熵交给了上帝。谁是上帝?我们其实期待着我们的后代是上帝,不能解决的问题期待他们都能解决。但未来他们存在吗?他们的未来存在吗?

其实我们更应当是上帝,将目前所有的问题都解决,留给他们一个轻松的未来。这是创世。

回到科学技术上来。生产同样的物质,如果消耗的负熵(有序)更低,则我们称之为更有效率,这也是科技更发达的一种形式。假设存在初始状态一模一样的两个地球,假设他们最终都要毁灭,他们单位时间产出的GDP都相同,而且都保持恒定的产出率,但一个地球科技更昌明,产生的熵更少一点,可以预见,这个地球的毁灭更晚一点。这时有人看到了希望的火花,就像牛顿对第一宇宙速度的设想,站在高山顶架一门大炮,炮弹速度越大飞的越远,当速度达到宇宙第一速度就能绕圈圈了,他希望科技能无限昌明下去,按照原来的技术水平还能发展一百年,昌明一点点就能延长一点时间,昌明到一定程度不就解决人类发展问题了吗?问题是,延长的时间可能是随着昌明的程度趋向于一个极限值而非无限值,或者说科技不可能无限昌明下去。但是我们还是看到了科学技术的一些正面作用。

我更倾向于延长时间存在极限值。就算能解决能量问题能成功用核聚变发电,科技还是有怎么都无法解决的问题,那就是物质混乱4。地球不能和外界交换物质,终有一天,地球所有的物质都无法再利用,退一步而言,只要有一类必需品混乱到无法再利用,人类如果不能移民外星球,就只能灭绝。很多人以为物质能无限循环使用,只需以能量为代价。但事实并非如此。我们使用的物品,很多都来自矿物。这些矿物呈富集状态,否则无法利用。以铁为例,古代的农业文明从铁器时代起耕种普遍使用了铁耙,他们要把铁矿石开采出来然后冶炼成铁再铸造成铁耙,在使用中铁原子会磨损,散失到土地当中,变为不可用的物质,但铁的需求量总体不大,可供古代人有足够长的开采时间。到了现代工业社会,人类对铁需求量之大以致目前的探明储量已不足开采150年,但人类的浪费相比古人要惊人得多。技术的进步可以使得熵降低,但不能无限降低到零,可以提高回收率,但不能提高到100%。说到提高回收率,这又是一个神话。要回收铁原子,无论技术怎么进步,回收成本都是随着回收率的增长而急剧增大,而伴随着回收过程,就算忽略了能量损耗但又添加了回收设备的物质损耗,正所谓得不偿失。科技的进步大大促进了物质混乱的速度,古代人类不用去担心这个,他们没必要,这只是隐藏在科技带给现代人类力量的背后的灾难。另一方面,人类到目前为止并没有完全将技术应用到降低熵值上,更多的是拿来增加产值扩大规模,这无疑是一种自杀行为。

科学技术救不了人类,只有人类能救自己。未来生存第一要义,寡民。没出现人类之前,看自然界,哪有一种物种像人类一样扩张。处于食物链顶层的动物,数量总是稀少,因为它们只懂得打猎,不懂得开荒。它们的负熵来自低一层的动物,数量固定,自然界不会多给一分。人类自从懂得了从土地获取负熵,开始了扩张之旅,时至今日,地球人口六十几亿,已将能用作耕地的土地全部变成了耕地。未来的人类只有小规模才能发展下去,因为如果他们不放弃科学技术,他们的单位产出熵实在太高了。未来生存第二要义,发展低熵产业。古代的农业比现代农业要低熵,为什么?古人种下了种子,输入太阳能便产出了粮食,多么简洁的过程!现代农业耕种收割有机械,施肥有化肥,除草有农药,折腾一番,以钢铁、能源损耗,土地、水污染,物种减少为代价才收获一点粮食。我并不是鼓吹重返刀耕火种的年代,我相信存在第三种更低熵的耕种方式。未来生存第三要义,缩小生产规模。人类每进行一分生产,地球就多一分混乱。这个生产规模到底要缩小到什么程度?应当维持着一个足够低的限度直到人类有能力移民外星球或能和外星球交换物质为止。

我们目前能为我们的后代做的就是沿着这上述的方向前进,仅且而已。

(全文完)

参考文献

1、  第三类永动机,田松,博览群书2007年第九期。http://shc2000.sjtu.edu.cn/0710/disanlei.htm

2、  物理学-热力学第二定律。

http://eduinfo.hust.edu.cn/old/kjzz/gjcbs/06/p03/ch09/sec09/index.htm

3、  新闻报道:温州科学爱好者声称他推翻了热力学第二定律。

http://news.xinhuanet.com/st/2005-03/12/content_2686441.htm

4、  有限地球时代的怀疑论自序,田松。http://shc2000.sjtu.edu.cn/0710/youxiandi.htm

2,842 次浏览 | 没有评论
2011年7月23日 | 归档于 私语