Posted: September 17, 2009 at 10:49 am | Tags: linux, php, python, windows
最近在国内没办法再用 spotify 听歌了,迫于无奈只好转向 google music 。
因为 firefox 尴尬的内存占用,并且考虑把歌同步到 iPhone 上来听,开始着手找下载 google music 的小工具,发现骨头做的 gmbox 不错,可惜现在是只支持 windows 和 linux ,只好用 gmusic.py 这个脚本。
运行 gmusic.py 首先要安装 python3 环境,去 python 官方网站载下 python3.1 然后 configure –enable-shared && make && sudo make install && ln -s /usr/local/bin/python3 /usr/bin/python3 既可
用 gmusic.py 下载速度在我的 istat 里显示大概是 800K 左右,非常不错。
Posted: November 28, 2006 at 12:22 am | Tags: blog, class, html, java, php, python, ror, ruby, web
不知道明年今日,会不会出现一队只会用Ruby On Rails的毕业生,像当年的asp、jsp、php迅速剿了C++/perl的CGI那样,把我们给剿了。同好们劝我,根据大公司经济学,这基本不会发生。
在茫茫的框架之海认出一个Rails框架,基本上靠四个特征
1.一门动态语言
2.一个extreme simple to use的ORM框架
3.一个extreme simple to use的MVC框架
4.一些自动生成代码的命令、模版
其余ajax、web service、i18n等特性自由扩展
1.Ruby: Ruby on Rails
Rails系的旗手,一己之力搞得J2EE阵营鸡飞蛋打。
旗手的作用表现在:
*最接近1.0的版本,目前已出到最后一个RC版 1.0 rc4(0.14.3)
*拥有一本amazon超级畅销的《Agile Web Development with Rails》,而且这本书的组织也好,part I是一个渐进的sample application 让你快速入门,partII是几个重要领域的深入介绍。
*拥有自己的IDE: RadRails ,基于Eclipse但独立成军,目前出到0.5。(是不是怕惨了Eclipse的多变,现在这些IDE的发行版本开始反包含了Eclipse在内。)
2.Python: DJango、turbogears
认识不深,所以DJango请看limodou的blog,turbogears看xlp123的。
3.Groovy: grails
如果能顺产,绝对是J2EE阵营里感情分最高涨的项目。因为它语言用Groovy,ORM用annotation版Hibernate,MVC用Spring。不用担心它换汤不换药,因为经过extreme的封装,再结合Groovy,绝对不再是原来的Spring+Hibernate,而是和RoR差不多的一样东西了。不信可以看看他的Sample。
不过这个项目的源码目录树非一般的乱,也还没有0.1版释出。还有一样奇怪的事情,这个Groovy项目,大多数的class代码都是Java写的。
4.Php: symfony ,cake
据说cake比symfony弱非常多,所以没看。
symfonys是基于php5的项目,成功整合了Propel(ORM)、Mojavi3(MVC),再配合自动生成的脚本打造而成。看他的sample,最后整合出来的东西也很Rails了,除了ORM层的xml文件。
还有一个发现是Php项目现在可以用pear来安装,很像Ruby的gem,Java的Maven要努力了。
C: />pear channel-discover pear.symfony-project.com
C: />pear install symfony/symfony
这样就装完了symfonys和propel&croel, mojavi3,还有用来运行脚本命令的pake(php make),PHing(php ant?),一大堆东西。
from :http://www.blogjava.net/calvin/archive/2005/11/22/20938.html
author:江南白衣
Posted: November 16, 2006 at 3:44 pm | Tags: python
http://www.miex.org/python/
Posted: November 15, 2006 at 3:54 pm | Tags: cache, class, debug, html, java, linux, php, python, ruby, server, web, 优化, 测试, 类, 缓存
一、memcached 简介
在很多场合,我们都会听到 memcached 这个名字,但很多同学只是听过,并没有用过或实际了解过,只知道它是一个很不错的东东。这里简单介绍一下,memcached 是高效、快速的分布式内存对象缓存系统,主要用于加速 WEB 动态应用程序。
二、memcached 安装
首先是下载 memcached 了,目前最新版本是 1.1.12,直接从官方网站即可下载到 memcached-1.1.12.tar.gz。除此之外,memcached 用到了 libevent,我下载的是 libevent-1.1a.tar.gz。
接下来是分别将 libevent-1.1a.tar.gz 和 memcached-1.1.12.tar.gz 解开包、编译、安装:
# tar -xzf libevent-1.1a.tar.gz# cd libevent-1.1a# ./configure --prefix=/usr# make# make install# cd ..# tar -xzf memcached-1.1.12.tar.gz# cd memcached-1.1.12# ./configure --prefix=/usr# make# make install
安装完成之后,memcached 应该在 /usr/bin/memcached。
三、运行 memcached 守护程序
运行 memcached 守护程序很简单,只需一个命令行即可,不需要修改任何配置文件(也没有配置文件给你修改
):
/usr/bin/memcached -d -m 128 -l 192.168.1.1 -p 11211 -u httpd
参数解释:
-d 以守护程序(daemon)方式运行 memcached;-m 设置 memcached 可以使用的内存大小,单位为 M;-l 设置监听的 IP 地址,如果是本机的话,通常可以不设置此参数;-p 设置监听的端口,默认为 11211,所以也可以不设置此参数;-u 指定用户,如果当前为 root 的话,需要使用此参数指定用户。
当然,还有其它参数可以用,man memcached 一下就可以看到了。
四、memcached 的工作原理
首先 memcached 是以守护程序方式运行于一个或多个服务器中,随时接受客户端的连接操作,客户端可以由各种语言编写,目前已知的客户端 API 包括 Perl/PHP/Python/Ruby/Java/C#/C 等等。PHP 等客户端在与 memcached 服务建立连接之后,接下来的事情就是存取对象了,每个被存取的对象都有一个唯一的标识符 key,存取操作均通过这个 key 进行,保存到 memcached 中的对象实际上是放置内存中的,并不是保存在 cache 文件中的,这也是为什么 memcached 能够如此高效快速的原因。注意,这些对象并不是持久的,服务停止之后,里边的数据就会丢失。
三、PHP 如何作为 memcached 客户端
有两种方法可以使 PHP 作为 memcached 客户端,调用 memcached 的服务进行对象存取操作。
第一种,PHP 有一个叫做 memcache 的扩展,Linux 下编译时需要带上 –enable-memcache[=DIR] 选项,Window 下则在 php.ini 中去掉 php_memcache.dll 前边的注释符,使其可用。
除此之外,还有一种方法,可以避开扩展、重新编译所带来的麻烦,那就是直接使用 php-memcached-client。
本文选用第二种方式,虽然效率会比扩展库稍差一些,但问题不大。
四、PHP memcached 应用示例
首先 下载 memcached-client.php,在下载了 memcached-client.php 之后,就可以通过这个文件中的类“memcached”对 memcached 服务进行操作了。其实代码调用非常简单,主要会用到的方法有 add()、get()、replace() 和 delete(),方法说明如下:
add ($key, $val, $exp = 0)
往 memcached 中写入对象,$key 是对象的唯一标识符,$val 是写入的对象数据,$exp 为过期时间,单位为秒,默认为不限时间;
get ($key)
从 memcached 中获取对象数据,通过对象的唯一标识符 $key 获取;
replace ($key, $value, $exp=0)
使用 $value 替换 memcached 中标识符为 $key 的对象内容,参数与 add() 方法一样,只有 $key 对象存在的情况下才会起作用;
delete ($key, $time = 0)
删除 memcached 中标识符为 $key 的对象,$time 为可选参数,表示删除之前需要等待多长时间。
下面是一段简单的测试代码,代码中对标识符为 ‘mykey’ 的对象数据进行存取操作:
<pre><?php// 包含 memcached 类文件require_once(‘memcached-client.php’);// 选项设置$options = array( ’servers’ => array(‘192.168.1.1:11211′), //memcached 服务的地址、端口,可用多个数组元素表示多个 memcached 服务 ‘debug’ => true, //是否打开 debug ‘compress_threshold’ => 10240, //超过多少字节的数据时进行压缩 ‘persistant’ => false //是否使用持久连接 );// 创建 memcached 对象实例$mc = new memcached($options);// 设置此脚本使用的唯一标识符$key = ‘mykey’;// 往 memcached 中写入对象$mc->add($key, ’some random strings’);$val = $mc->get($key);echo “n”.str_pad(‘$mc->add() ’, 60, ‘_’).“n”;var_dump($val);// 替换已写入的对象数据值$mc->replace($key, array(’some’=>‘haha’, ‘array’=>‘xxx’));$val = $mc->get($key);echo “n”.str_pad(‘$mc->replace() ’, 60, ‘_’).“n”;var_dump($val);// 删除 memcached 中的对象$mc->delete($key);$val = $mc->get($key);echo “n”.str_pad(‘$mc->delete() ’, 60, ‘_’).“n”;var_dump($val);?></pre>
是不是很简单,在实际应用中,通常会把数据库查询的结果集保存到 memcached 中,下次访问时直接从 memcached 中获取,而不再做数据库查询操作,这样可以在很大程度上减轻数据库的负担。通常会将 SQL 语句 md5() 之后的值作为唯一标识符 key。下边是一个利用 memcached 来缓存数据库查询结果集的示例(此代码片段紧接上边的示例代码):
<?php$sql = ‘SELECT * FROM users’;$key = md5($sql); //memcached 对象标识符if ( !($datas = $mc->get($key)) ) { // 在 memcached 中未获取到缓存数据,则使用数据库查询获取记录集。 echo “n”.str_pad(‘Read datas from MySQL.’, 60, ‘_’).“n”; $conn = mysql_connect(‘localhost’, ‘test’, ‘test’); mysql_select_db(‘test’); $result = mysql_query($sql); while ($row = mysql_fetch_object($result)) $datas[] = $row; // 将数据库中获取到的结果集数据保存到 memcached 中,以供下次访问时使用。 $mc->add($key, $datas);} else { echo “n”.str_pad(‘Read datas from memcached.’, 60, ‘_’).“n”;}var_dump($datas);?>
可以看出,使用 memcached 之后,可以减少数据库连接、查询操作,数据库负载下来了,脚本的运行速度也提高了。
之前我曾经写过一篇名为《PHP 实现多服务器共享 SESSION 数据》文章,文中的 SESSION 是使用数据库保存的,在并发访问量大的时候,服务器的负载会很大,经常会超出 MySQL 最大连接数,利用 memcached,我们可以很好地解决这个问题,工作原理如下:
- 用户访问网页时,查看 memcached 中是否有当前用户的 SESSION 数据,使用 session_id() 作为唯一标识符;如果数据存在,则直接返回,如果不存在,再进行数据库连接,获取 SESSION 数据,并将此数据保存到 memcached 中,供下次使用;
- 当前的 PHP 运行结束(或使用了 session_write_close())时,会调用 My_Sess::write() 方法,将数据写入数据库,这样的话,每次仍然会有数据库操作,对于这个方法,也需要进行优化。使用一个全局变量,记录用户进入页面时的 SESSION 数据,然后在 write() 方法内比较此数据与想要写入的 SESSION 数据是否相同,不同才进行数据库连接、写入数据库,同时将 memcached 中对应的对象删除,如果相同的话,则表示 SESSION 数据未改变,那么就可以不做任何操作,直接返回了;
- 那么用户 SESSION 过期时间怎么解决呢?记得 memcached 的 add() 方法有个过期时间参数 $exp 吗?把这个参数值设置成小于 SESSION 最大存活时间即可。另外别忘了给那些一直在线的用户延续 SESSION 时长,这个可以在 write() 方法中解决,通过判断时间,符合条件则更新数据库数据。
五、相关资源
肖理达 (KrazyNio AT hotmail.com), 2006.04. 06, 转载请注明出处
转载自:http://nio.infor96.com/php-memcached/
Posted: October 27, 2006 at 12:48 pm | Tags: class, java, php, python, server, unix, web, 平台, 开发, 技术, 测试, 类
SOAP(Simple Object Access Protocal,简单对象访问协议) 技术有助于实现大量异构程序和平台之间的互操作性,从而使存在的应用能够被广泛的用户所访问。SOAP是把成熟的基于HTTP的WEB技术与XML的灵活性和可扩展性组合在了一起。
SOAP由MS和IBM共同制定
用于规范WEB服务标准 实现异构程序与平台间的数据交换
它是基于XML的协议,包括三个部分: 封套(envelope)定义了消息内容和处理的框架、一套编码规则用来表达应用定义数据类型的实例以及表达远程过程调用和响应的协定。
与已定义的中间件不同 SOAP只是定义了一种基于XML的文本格式 而没有定义什么ORB代理或是SOAP API 因此用户可以方便的开发自己的应用而不必担心兼容性(corba与dcom间的兼容性在soap中不会再出现)
下面是一篇简介 更多介绍在动网先锋中可以找到
http://www.aspsky.net/article/show.aspx?id=2001
简单对象协议(SOAP)简介
作者:何杭军
简单对象访问协议-CNXML标准教程
2000-9-25 作者:何杭军
"SOAP是在非集中、分布环境中交换信息的轻量级协议。它是基于XML的协议,包括三个部分: 封套(envelope)定义了消息内容和处理的框架、一套编码规则用来表达应用定义数据类型的实例以及表达远程过程调用和响应的协定。"
——SOAP 1.1规范
第一节 SOAP简介
SOAP(Simple Object Access Protocal,简单对象访问协议) 技术有助于实现大量异构程序和平台之间的互操作性,从而使存在的应用能够被广泛的用户所访问。SOAP是把成熟的基于HTTP的WEB技术与XML的灵活性和可扩展性组合在了一起。
SOAP的一个主要目标是使存在的应用能被更广泛的用户所使用。为了实现这个目的,没有任何SOAP API或SOAP 对象请求代理(SOAP ORB),SOAP是假设你将使用尽可能多的存在的技术。几个主要的CORBA厂商已经承诺在他们的ORB产品中支持SOAP协议。微软也承诺在将来的 COM版本中支持SOAP。DevelopMentor已经开发了参考实现,它使得在任何平台上的任何Java或Perl程序员都可以使用SOAP。而且 IBM和Sun也陆续支持了SOAP协议,和MS合作共同开发SOAP规范和应用。目前SOAP已经成为了W3C和IETF的参考标准之一。
SOAP的指导理念是“它是第一个没有发明任何新技术的技术”。它采用了已经广泛使用的两个协议:HTTP和XML。HTTP用于实现SOAP的RPC风格的传输,而XML是它的编码模式。采用几行代码和一个XML解析器,HTTP服务器(如MS的IIS或Apache)立刻成为了SOAP的ORBs。因为目前超过一半的Web服务器采用IIS或Apache, SOAP将会从这两个产品的广泛而可靠的使用中获取利益。这并不意味着所有的SOAP请求必须通过Web服务器来路由,传统的Web 服务器只是分派SOAP请求的一种方式。因此Web服务如IIS或Apache对建立SOAP性能的应用是充分的,但决不是必要的。
SOAP把XML的使用代码化为请求和响应参数编码模式,并用HTTP作传输。这似乎有点抽象。具体地讲,一个SOAP方法可以简单地看作遵循SOAP编码规则的HTTP请求和响应。一个SOAP终端则可以看作一个基于HTTP的URL,它用来识别方法调用的目标。象CORBA/IIOP一样,SOAP不需要具体的对象被绑定到一个给定的终端,而是由具体实现程序来决定怎样把对象终端标识符映射到服务器端的对象。
SOAP请求是一个HTTP POST请求。SOAP请求的content-type必须用text/xml。而且它必须包含一个请求-URI。服务器怎样解释这个请求-URI是与实现相关的,但是许多实现中可能用它来映射到一个类或者一个对象。一个SOAP请求也必须用SOAPMethodName HTTP头来指明将被调用的方法。简单地讲,SOAPMethodName头是被URI指定范围的应用相关的方法名,它是用#符作为分隔符将方法名与 URI分割开:
SOAPMethodName: urn:strings-com:IString#reverse
这个头表明方法名是reverse,范围URI是urn:strings-com:Istring。 在SOAP中,规定方法名范围的名域URI在功能上等同于在DCOM 或 IIOP中规定方法名范围的接口ID。
简单的说,一个SOAP请求的HTTP体是一个XML文档,它包含方法中[in]和[in,out]参数的值。这些值被编码成为一个显著的调用元素的子元素,这个调用元素具有SOAPMethodName HTTP头的方法名和名域URI。调用元素必须出现在标准的SOAP <Envelope>;和<Body>;元素内(后面会更多讨论这两个元素)。下面是一个最简单的SOAP方法请求:
POST /string_server/Object17 HTTP/1.1
Host: 209.110.197.2
Content-Type: text/xml
Content-Length: 152
SOAPMethodName: urn:strings-com:IString#reverse
<Envelope>;
<Body>;
<m:reverse xmlns:m=”urn:strings-com:IString”>;
<theString>;Hello, World</theString>;
</m:reverse>;
</Body>;
</Envelope>;
SOAPMethodName头必须与<Body>;下的第一个子元素相匹配,否则调用将被拒绝。这允许防火墙管理员在不解析XML的情况下有效地过滤对一个具体方法的调用。
SOAP响应的格式类似于请求格式。响应体包含方法的[out]和 [in,out]参数,这个方法被编码为一个显著的响应元素的子元素。这个元素的名字与请求的调用元素的名字相同,但以Response后缀来连接。下面是对前面的SOAP请求的SOAP响应:
200 OK Content-Type: text/xml
Content-Length: 162
<Envelope>;
<Body>;
<m:reverseResponse xmlns:m=”urn:strings-com:IString”>;
<result>;dlroW ,olleH</result>;
</m:reverseResponse>;
</Body>;
</Envelope>;
这里响应元素被命名为reverseResponse,它是方法名紧跟Response后缀。要注意的是这里是没有SOAPMethodName HTTP头的。这个头只在请求消息中需要,在响应消息中并不需要。
第二节 SOAP体的核心
SOAP的XML特性是为把数据类型的实例序列化成XML的编码模式。为了达到这个目的,SOAP不要求使用传统的RPC风格的代理。而是一个SOAP方法调用包含至少两个数据类型:请求和响应。考虑这下面个COM IDL代码:
[ uuid(DEADF00D-BEAD-BEAD-BEAD-BAABAABAABAA) ]
interface IBank : IUnknown {
HRESULT withdraw([in] long account,
[out] float *newBalance,
[in, out] float *amount
[out, retval] VARIANT_BOOL *overdrawn);
}
在任何RPC协议下,account和amount参数的值将出现在请求消息中,newBalance、overdrawn参数的值,还有amount参数的更新值将出现在响应消息中。
SOAP把方法请求和方法响应提升到了一流状态。在SOAP中,请求和响应实际上类型的实例。为了理解一个方法比如IBank::withdraw怎样映射一个SOAP请求和响应类型,考虑下列的数据类型:
struct withdraw {
long account;
float amount;
};
这时所有的请求参数被打包成为单一的结构类型。同样下面的数据表示打包所有响应参数到单一的数据类型。
struct withdrawResponse {
float newBalance;
float amount;
VARIANT_BOOL overdrawn;
};
再给出下面的简单的Visual Basic程序,它使用了以前定义的Ibank接口:
Dim bank as IBank
Dim amount as Single
Dim newBal as Single
Dim overdrawn as Boolean
amount = 100
Set bank = GetObject("soap:http://bofsoap.com/am"
overdrawn = bank.withdraw(3512, amount, newBal)
这里,在发送请求消息之前,参数被序列化成为一个请求对象。同样被响应消息接收到的响应对象被反序列化为参数。一个类似的转变同样发生在调用的服务器端。
当通过SOAP调用方法时,请求对象和响应对象被序列化成一种已知的格式。每个SOAP体是一个XML文档,它具有一个显著的称为< Envelope>;的根元素。标记名<Envelope>;由SOAP URI (urn:schemas-xmlsoap-org:soap.v1)来划定范围,所有SOAP专用的元素和属性都是由这个URI来划定范围的。SOAP Envelope包含一个可选的<Header>;元素,紧跟一个必须的<Body>;元素。<Body>;元素也有一个显著的根元素,它或者是一个请求对象或者是一个响应对象。下面是一个IBank::withdraw请求的编码:
<soap:Envelope xmlns:soap=”urn:schemas-xmlsoap-org:soap.v1”>;
<soap:Body>;
<IBank:withdraw xmlns:IBank=”urn:uuid
EADF00D-BEAD-BEAD-BEAD-BAABAABAABAA”>;
<account>;3512</account>;
<amount>;100</amount>;
</IBank:withdraw>;
</soap:Body>;
</soap:Envelope>;
下列响应消息被编码为:
<soap:Envelope xmlns:soap=”urn:schemas-xmlsoap-org:soap.v1”>;
<soap:Body>;
<IBank:withdrawResponse xmlns:IBank=”urn:uuid
EADF00D-BEAD-BEAD-BEAD-BAABAABAABAA”>;
<newBalance>;0</newBalance>;
<amount>;5</amount>;
<overdrawn>;true</overdrawn>;
</IBank:withdrawResponse>;
</soap:Body>;
</soap:Envelope>;
注意[in, out]参数出现在两个消息中。在检查了请求和响应对象的格式后,你可能已经注意到序列化格式通常是:
<t:typename xmlns:t=”namespaceuri”>;
<fieldname1>;field1value</fieldname1>;
<fieldname2>;field2value</fieldname2>;
……
</t:typename>;
在请求的情况下,类型是隐式的C风格的结构,它由对应方法中的[in]和[in, out]参数组成。对响应来说,类型也是隐式的C风格的结构,它由对应方法中的[out]和[in, out]参数组成。这种每个域对应一个子元素的风格有时被称为元素正规格式(ENF)。一般情况下,SOAP只用XML特性来传达描述包含在元素内容中信息的注释。
象DCOM和IIOP一样,SOAP支持协议头扩展。SOAP用可选的<Header>;元素来传载被协议扩展所使用的信息。如果客户端的 SOAP软件包含要发送头信息,原始的请求将可能如图9所示。在这种情况下命名causality的头将与请求一起序列化。收到请求后,服务器端软件能查看头的名域URI,并处理它识别出的头扩展。这个头扩展被http://comstuff.com URI识别,并期待一个如下的对象:
struct causality {
UUID id;
};
在这种情况下的请求,如果头元素的URI不能被识别,头元素可以被安全地忽略。
但你不能安全的忽略所有的SOAP体中的头元素。如果一个特定的SOAP头对正确处理消息是很关键的,这个头元素能被用SOAP属性 mustUnderstand=’true’标记为必须的。这个属性告诉接收者头元素必须被识别并被处理以确保正确的使用。为了强迫前面 causality头成为一个必须的头,消息将被写成如下形式:
<soap:Envelope xmlns:soap=”urn:schemas-xmlsoap-org:soap.v1”>;
<soap:Header>;
<causality soap:mustUnderstand=”true”xmlns="http://comstuff.com">;
<id>;362099cc-aa46-bae2-5110-99aac9823bff</id>;
</causality>;
</soap:Header>;
</soap:Envelope>;
SOAP软件遇到不能识别必须的头元素情况时,必须拒绝这个消息并出示一个错误。如果服务器在一个SOAP请求中发现一个不能识别的必须的头元素,它必须返回一个错误响应并且不发送任何调用到目标对象。如果客户端在一个SOAP请求中发现一个不能识别出的必须的头元素,它必须向调用者返回一个运行时错误。在COM情况下,这将映射为一个明显的HRESULT。
第三节 SOAP数据类型
在SOAP消息中,每个元素可能是一个SOAP结构元素、根元素、存取元素或一个独立的元素。在SOAP中,soap:Envelope、soap:Body和soap:Header是唯一的组成元素。它们的基本关系由下列XML Schema所描述:
<schema targetNamespace=”urn:schemas-xmlsoap-org:soap.v1”>;
<element name=”Envelope”>;
<type>;
<element name=”Header” type=”Header” minOccurs=”0” />;
<element name=”Body” type=”Body”minOccurs=”1” />;
</type>;
</element>;
</schema>;
在SOAP元素的四种类型中,除了结构元素外都被用作表达类型的实例或对一个类型实例的引用。
根元素是显著的元素,它是soap:Body 或是 soap:Header的直接的子元素。其中soap: Body只有一个根元素,它表达调用、响应或错误对象。这个根元素必须是soap:Body的第一个子元素,它的标记名和域名URI必须与HTTP SOAPMethodName头或在错误消息情况下的soap:Fault相对应。而soap:Header元素有多个根元素,与消息相联系的每个头扩展对应一个。这些根元素必须是soap:Header的直接子元素,它们的标记名和名域URI表示当前存在扩展数据的类型。
存取元素被用作表达类型的域、属性或数据成员。一个给定类型的域在它的SOAP表达将只有一个存取元素。存取元素的标记名对应于类型的域名。考虑下列Java 类定义:
package com.bofsoap.IBank;
public class adjustment {
public int account ;
public float amount ;
}
在一个SOAP消息中被序列化的实例如下所示:
<t:adjustment xmlns:t=”urn:develop-com:java:com.bofsoap.IBank”>;
<account>;3514</account>;
<amount>;100.0</amount>;
</t:adjustment>;
在这个例子中,存取元素account和amount被称着简单存取元素。对引用简单类型的存取元素,元素值被简单地编码为直接在存取元素下的字符数据,如上所示。对引用组合类型的存取元素(就是那些自身用子存取元素来构造的存取元素),有两个技术来对存取元素进行编码。最简单的方法是把被结构化的值直接嵌入在存取元素下。考虑下面的Java类定义:
package com.bofsoap.IBank;
public class transfer {
public adjustment from;
public adjustment to;
}
如果用嵌入值编码存取元素,在SOAP中一个序列化的transfer对象如下所示:
<t:transfer xmlns:t=”urn:develop-com:java:com.bofsoap.IBank”>;
<from>;
<account>;3514</account>;
<amount>;-100.0</amount>;
</from>;
<to>;
<account>;3518</account>;
<amount>;100.0</amount>;
</to>;
</t:transfer>;
在这种情况下,adjustment对象的值被直接编码在它们的存取元素下。在考虑组合存取元素时,需要说明几个问题。先考虑上面的transfer类。类的from和to的域是对象引用,它可能为空。SOAP用XML Schemas的null属性来表示空值或引用。下面例子表示一个序列化的transfer对象,它的from域是空的:
<t:transfer xmlns:t=”urn:develop-com:java:com.bofsoap.IBank”
xmlns
sd=”http://www.w3.org/1999/XMLSchema/instance”>;
<from xsd:null=”true” />;
<to>;
<account>;3518</account>;
<amount>;100.0</amount>;
</to>;
</t:transfer>;
在不存在的情况下, xsd:null属性的隐含值是false。给定元素的能否为空的属性是由XML Schema定义来控制的。例如下列XML Schema将只允许from存取元素为空:
<type name=”transfer” >;
<element name=”from” type=”adjustment” nullable=”true” />;
<element name=”to” type=”adjustment” nullable=”false”/>;
</type>;
在一个元素的Schema声明中如果没有nullable属性,就意味着在一个XML文档中的元素是不能为空的。Null存取元素的精确格式当前还在修订中�要了解用更多信息参考最新版本的SOAP规范。
与存取元素相关的另一个问题是由于类型关系引起的可代换性。由于前面的adjustment类不是一个final类型的类,transfer对象的 from和to域实际引用继承类型的实例是可能的。为了支持这种类型兼容的替换,SOAP使用一个名域限定的类型属性的XML Schema约定。这种类型属性的值是一个对元素具体的类型的限制的名字。考虑下面的adjustment扩展类:
package com.bofsoap.IBank;
public class auditedadjustment extends adjustment {
public int auditlevel;
}
给出下面Java语言:
transfer xfer = new transfer();
xfer.from = new auditedadjustment();
xfer.from.account = 3514;
xfer.from.amount = -100;
xfer.from.auditlevel = 3;
xfer.to = new adjustment();
xfer.to.account = 3518;
xfer.from.amount = 100;
在SOAP中transfer对象的序列化形式如下所示:
<t:transfer xmlns
sd=”http://www.w3.org/1999/XMLSchema”
xmlns:t=”urn:develop-com:java:com.bofsoap.IBank”>;
<from xsd:type=”t:auditedadjustment” >;
<account>;3514</account>;
<amount>;-100.0</amount>;
<auditlevel>;3</auditlevel >;
</from>;
<to>;
<account>;3518</account>;
<amount>;100.0</amount>;
</to>;
</t:transfer>;
在这里xsd:type属性引用一个名域限定的类型名,它能被反序列化程序用于实例化对象的正确类型。因为to存取元素引用到一个被预料的类型的实例(而不是一个可代替的继承类型),xsd:type属性是不需要的。
刚才的transfer类设法回避了一个关键问题。如果正被序列化的transfer对象用下面这种方式初始化将会发生什么情况:
transfer xfer = new transfer();
xfer.from = new adjustment();
xfer.from.account = 3514; xfer.from.amount = -100;
xfer.to = xfer.from;
基于以前的议论,在SOAP 中transfer对象的序列化形式如下所示:
<t:transfer xmlns:t=”urn:develop-com:java:com.bofsoap.IBank”>;
<from>;
<account>;3514</account>;
<amount>;-100.0</amount>;
</from>;
<to>;
<account>;3514</account>;
<amount>;-100.0</amount>;
</to>;
</t:transfer>;
这个表达有两个问题。首先最容易理解的问题是同样的信息被发送了两次,这导致了一个比实际所需要消息的更大的消息。一个更微妙的但是更重要的问题是由于反序列化程序不能分辨两个带有同样值的adjustment对象与在两个地方被引用的一个单一的adjustment对象的区别,两个存取元素间的身份关系就被丢失。如果这个消息接收者已经在结果对象上执行了下面的测试,(xfer.to == xfer.from)将不会返回true。
void processTransfer(transfer xfer) {
if (xfer.to == xfer.from)
handleDoubleAdjustment(xfer.to);
else
handleAdjustments(xfer.to, xfer.from);
}
为了支持必须保持身份关系的类型的序列化,SOAP支持多引用存取元素。目前我们接触到的存取元素是单引用存取元素,也就是说,元素值是嵌入在存取元素下面的,而且其它存取元素被允许引用那个值(这很类似于在NDR中的[unique]的概念)。多引用存取元素总是被编码为只包含已知的soap:href 属性的空元素。soap:href属性总是包含一个代码片段标识符,它对应于存取元素引用到的实例。如果to和from存取元素已经被编码为多引用存取元素,序列化的transfer对象如下所示:
<t:transfer xmlns:t=”urn:develop-com:java:com.bofsoap.IBank”>;
<from soap:href=”#id1” />;
<to soap:href=”#id1” />;
</t:transfer>;
这个编码假设与adjustment类兼容的一个类型的实例已经在envelope中的其它地方被序列化,而且这个实例已经被用soap:id属性标记,如下所示:
<t:adjustment soap:id=”id1”xmlns:t=”urn:develop-com:java:com.bofsoap.IBank”>;
<account>;3514</account>;
<amount>;-100.0</amount>;
</t:adjustment>;
第四节 结语
一个遗留的HTTP问题还需要进一步阐明。SOAP支持(但不需要)HTTP扩展框架约定来指定必须的HTTP头扩展。这些约定主要有两个目的。首先,它们允许任意的URI被用于限定给定的HTTP头的范围(类似XML名域)。第二,这些约定允许把必须的头与可选的头区分开来(象soap: mustUnderstand)。下面是一个使用HTTP扩展框架来把SOAPMethodName头定义成为一个必须的头扩展:
M-POST /foobar HTTP/1.1
Host: 209.110.197.2
Man: "urn:schemas-xmlsoap-org:soap.v1; ns=42"
42-SOAPMethodName: urn:bobnsid:IFoo#DoIt
Man头映射SOAP URI到前缀为42的头,并表示没有认出SOAP的服务器必须返回一个HTTP错误,状态代码为501 (没有被实现) 或 510 (没有被扩展)。HTTP方法必须是M-POST,表明目前是必须的头扩展。SOAP是一个被类型化的序列化格式,它恰巧用HTTP 作为请求/响应消息传输协议。SOAP被设计为与正将出现的XML Schema规范密切配合,并支持在Internet的任何地方运行的COM、CORBA、Perl、Tcl、和Java、C、Python或 PHP等程序间的互操作性。
Posted: October 26, 2006 at 11:38 am | Tags: blog, class, flash, html, php, python, web
今天查看 Webmasters 时发现原来搜索 FFMPEG 过来的人是最多的……看来大家最近对视频很感兴趣嘛,不知是不是因为 youtube 被收购的原因?
Posted: October 26, 2006 at 11:06 am | Tags: blog, class, html, java, linux, python, unix, windows, 优化, 平台, 开发, 技术, 类
声明:
.本文2004年5月首发于《CSDN开发高手》,版权归该杂志与《程序员》杂志社所有。杂志限于篇幅部分内容有所删节,此处版本为相对完整版本。
.本文为介绍性文章,会随笔者学习C++语言不断更新。
库
在C++中,库的地位是非常高的。C++之父 Bjarne Stroustrup先生多次表示了设计库来扩充功能要好过设计更多的语法的言论。现实中,C++的库门类繁多,解决的问题也是极其广泛,库从轻量级到重量级的都有。不少都是让人眼界大开,亦或是望而生叹的思维杰作。由于库的数量非常庞大,而且限于笔者水平,其中很多并不了解。所以文中所提的一些库都是比较著名的大型库。
标准库
标准库中提供了C++程序的基本设施。虽然C++标准库随着C++标准折腾了许多年,直到标准的出台才正式定型,但是在标准库的实现上却很令人欣慰得看到多种实现,并且已被实践证明为有工业级别强度的佳作。
1、 Dinkumware C++ Library
参考站点:http://www.dinkumware.com/
P.J. Plauger编写的高品质的标准库。P.J. Plauger博士是Dr. Dobb’s程序设计杰出奖的获得者。其编写的库长期被Microsoft采用,并且最近Borland也取得了其OEM的license,在其C/C++的产品中采用Dinkumware的库。
2、 RogueWave Standard C++ Library
参考站点:http://www.roguewave.com/
这个库在Borland C++ Builder的早期版本中曾经被采用,后来被其他的库给替换了。笔者不推荐使用。
3、SGI STL
参考站点:http://www.roguewave.com/
SGI公司的C++标准模版库。
4、STLport
参考站点:http://www.stlport.org/
SGI STL库的跨平台可移植版本。
准标准库——Boost
Boost库是一个经过千锤百炼、可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的发动机之一。 Boost库由C++标准委员会库工作组成员发起,在C++社区中影响甚大,其成员已近2000人。 Boost库为我们带来了最新、最酷、最实用的技术,是不折不扣的“准”标准库。
Boost中比较有名气的有这么几个库:
Regex
正则表达式库
Spirit
LL parser framework,用C++代码直接表达EBNF
Graph
图组件和算法
Lambda
在调用的地方定义短小匿名的函数对象,很实用的functional功能
concept check
检查泛型编程中的concept
Mpl
用模板实现的元编程框架
Thread
可移植的C++多线程库
Python
把C++类和函数映射到Python之中
Pool
内存池管理
smart_ptr
5个智能指针,学习智能指针必读,一份不错的参考是来自CUJ的文章:
Smart Pointers in Boost,哦,这篇文章可以查到,CUJ是提供在线浏览的。中文版见笔者在《Dr. Dobb’s Journal软件研发杂志》第7辑上的译文。
Boost总体来说是实用价值很高,质量很高的库。并且由于其对跨平台的强调,对标准C++的强调,是编写平台无关,现代C++的开发者必备的工具。但是Boost中也有很多是实验性质的东西,在实际的开发中实用需要谨慎。并且很多Boost中的库功能堪称对语言功能的扩展,其构造用尽精巧的手法,不要贸然的花费时间研读。Boost另外一面,比如Graph这样的库则是具有工业强度,结构良好,非常值得研读的精品代码,并且也可以放心的在产品代码中多多利用。
参考站点:http://www.boost.org(国内镜像:http://www.c-view.org/tech/lib/boost/index.htm)
GUI
在众多C++的库中,GUI部分的库算是比较繁荣,也比较引人注目的。在实际开发中,GUI库的选择也是非常重要的一件事情,下面我们综述一下可选择的GUI库,各自的特点以及相关工具的支持。
1、 MFC
大名鼎鼎的微软基础类库(Microsoft Foundation Class)。大凡学过VC++的人都应该知道这个库。虽然从技术角度讲,MFC是不大漂亮的,但是它构建于Windows API 之上,能够使程序员的工作更容易,编程效率高,减少了大量在建立 Windows 程序时必须编写的代码,同时它还提供了所有一般 C++ 编程的优点,例如继承和封装。MFC 编写的程序在各个版本的Windows操作系统上是可移植的,例如,在 Windows 3.1下编写的代码可以很容易地移植到 Windows NT 或 Windows 95 上。但是在最近发展以及官方支持上日渐势微。
2、 QT
参考网站:http://www.trolltech.com/
Qt是Trolltech公司的一个多平台的C++图形用户界面应用程序框架。它提供给应用程序开发者建立艺术级的图形用户界面所需的所用功能。Qt是完全面向对象的很容易扩展,并且允许真正地组件编程。自从1996年早些时候,Qt进入商业领域,它已经成为全世界范围内数千种成功的应用程序的基础。Qt也是流行的Linux桌面环境KDE 的基础,同时它还支持Windows、Macintosh、Unix/X11等多种平台。
3、WxWindows
参考网站:http://www.wxwindows.org/
跨平台的GUI库。因为其类层次极像MFC,所以有文章介绍从MFC到WxWindows的代码移植以实现跨平台的功能。通过多年的开发也是一个日趋完善的GUI库,支持同样不弱于前面两个库。并且是完全开放源代码的。新近的C++ Builder X的GUI设计器就是基于这个库的。
4、Fox
开放源代码的GUI库。作者从自己亲身的开发经验中得出了一个理想的GUI库应该是什么样子的感受出发,从而开始了对这个库的开发。有兴趣的可以尝试一下。
参考网站:http://www.fox-toolkit.org/
5、 WTL
基于ATL的一个库。因为使用了大量ATL的轻量级手法,模板等技术,在代码尺寸,以及速度优化方面做得非常到位。主要面向的使用群体是开发COM轻量级供网络下载的可视化控件的开发者。
6、 GTK
参考网站:http://gtkmm.sourceforge.net/
GTK是一个大名鼎鼎的C的开源GUI库。在Linux世界中有Gnome这样的杀手应用。而GTK就是这个库的C++封装版本。
网络通信
ACE
参考网站:http://www.cs.wustl.edu/~schmidt/ACE.html
C++库的代表,超重量级的网络通信开发框架。ACE自适配通信环境(Adaptive Communication Environment)是可以自由使用、开放源代码的面向对象框架,在其中实现了许多用于并发通信软件的核心模式。ACE提供了一组丰富的可复用C++包装外观(Wrapper Facade)和框架组件,可跨越多种平台完成通用的通信软件任务,其中包括:事件多路分离和事件处理器分派、信号处理、服务初始化、进程间通信、共享内存管理、消息路由、分布式服务动态(重)配置、并发执行和同步,等等。
StreamModule
参考网站:http://www.omnifarious.org/StrMod/
设计用于简化编写分布式程序的库。尝试着使得编写处理异步行为的程序更容易,而不是用同步的外壳包起异步的本质。
SimpleSocket
参考网站:http://home.hetnet.nl/~lcbokkers/simsock.htm
这个类库让编写基于socket的客户/服务器程序更加容易。
A Stream Socket API for C++
参考网站:http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.html
又一个对Socket的封装库。
XML
Xerces
参考网站:http://xml.apache.org/xerces-c/
Xerces-C++ 是一个非常健壮的XML解析器,它提供了验证,以及SAX和DOM API。XML验证在文档类型定义(Document Type Definition,DTD)方面有很好的支持,并且在2001年12月增加了支持W3C XML Schema 的基本完整的开放标准。
XMLBooster
参考网站:http://www.xmlbooster.com/
这个库通过产生特制的parser的办法极大的提高了XML解析的速度,并且能够产生相应的GUI程序来修改这个parser。在DOM和SAX两大主流XML解析办法之外提供了另外一个可行的解决方案。
Pull Parser
参考网站:http://www.extreme.indiana.edu/xgws/xsoap/xpp/
这个库采用pull方法的parser。在每个SAX的parser底层都有一个pull的parser,这个xpp把这层暴露出来直接给大家使用。在要充分考虑速度的时候值得尝试。
Xalan
参考网站:http://xml.apache.org/xalan-c/
Xalan是一个用于把XML文档转换为HTML,纯文本或者其他XML类型文档的XSLT处理器。
CMarkup
参考网站:http://www.firstobject.com/xml.htm
这是一种使用EDOM的XML解析器。在很多思路上面非常灵活实用。值得大家在DOM和SAX之外寻求一点灵感。
libxml++
http://libxmlplusplus.sourceforge.net/
libxml++是对著名的libxml XML解析器的C++封装版本
科学计算
Blitz++
参考网站:http://www.oonumerics.org/blitz/
Blitz++ 是一个高效率的数值计算函数库,它的设计目的是希望建立一套既具像C++ 一样方便,同时又比Fortran速度更快的数值计算环境。通常,用C++所写出的数值程序,比 Fortran慢20%左右,因此Blitz++正是要改掉这个缺点。方法是利用C++的template技术,程序执行甚至可以比Fortran更快。Blitz++目前仍在发展中,对于常见的SVD,FFTs,QMRES等常见的线性代数方法并不提供,不过使用者可以很容易地利用Blitz++所提供的函数来构建。
POOMA
参考网站:http://www.codesourcery.com/pooma/pooma
POOMA是一个免费的高性能的C++库,用于处理并行式科学计算。POOMA的面向对象设计方便了快速的程序开发,对并行机器进行了优化以达到最高的效率,方便在工业和研究环境中使用。
MTL
参考网站:http://www.osl.iu.edu/research/mtl/
Matrix Template Library(MTL)是一个高性能的泛型组件库,提供了各种格式矩阵的大量线性代数方面的功能。在某些应用使用高性能编译器的情况下,比如Intel的编译器,从产生的汇编代码可以看出其与手写几乎没有两样的效能。
CGAL
参考网站:www.cgal.org
Computational Geometry Algorithms Library的目的是把在计算几何方面的大部分重要的解决方案和方法以C++库的形式提供给工业和学术界的用户。
游戏开发
Audio/Video 3D C++ Programming Library
参考网站:http://www.galacticasoftware.com/products/av/
AV3D是一个跨平台,高性能的C++库。主要的特性是提供3D图形,声效支持(SB,以及S3M),控制接口(键盘,鼠标和遥感),XMS。
KlayGE
参考网站:http://home.g365.net/enginedev/
国内游戏开发高手自己用C++开发的游戏引擎。KlayGE是一个开放源代码、跨平台的游戏引擎,并使用Python作脚本语言。KlayGE在LGPL协议下发行。感谢龚敏敏先生为中国游戏开发事业所做出的贡献。
OGRE
参考网站:http://www.ogre3d.org
OGRE(面向对象的图形渲染引擎)是用C++开发的,使用灵活的面向对象3D引擎。它的目的是让开发者能更方便和直接地开发基于3D硬件设备的应用程序或游戏。引擎中的类库对更底层的系统库(如:Direct3D和OpenGL)的全部使用细节进行了抽象,并提供了基于现实世界对象的接口和其它类。
线程
C++ Threads
参考网站:http://threads.sourceforge.net/
这个库的目标是给程序员提供易于使用的类,这些类被继承以提供在Linux环境中很难看到的大量的线程方面的功能。
ZThreads
参考网站:http://zthread.sourceforge.net/
一个先进的面向对象,跨平台的C++线程和同步库。
序列化
s11n
参考网站:http://s11n.net/
一个基于STL的C++库,用于序列化POD,STL容器以及用户定义的类型。
Simple XML Persistence Library
参考网站:http://sxp.sourceforge.net/
这是一个把对象序列化为XML的轻量级的C++库。
字符串
C++ Str Library
参考网站:http://www.utilitycode.com/str/
操作字符串和字符的库,支持Windows和支持gcc的多种平台。提供高度优化的代码,并且支持多线程环境和Unicode,同时还有正则表达式的支持。
Common Text Transformation Library
参考网站:http://cttl.sourceforge.net/
这是一个解析和修改STL字符串的库。CTTL substring类可以用来比较,插入,替换以及用EBNF的语法进行解析。
GRETA
参考网站:http://research.microsoft.com/projects/greta/
这是由微软研究院的研究人员开发的处理正则表达式的库。在小型匹配的情况下有非常优秀的表现。
综合
P::Classes
参考网站:http://pclasses.com/
一个高度可移植的C++应用程序框架。当前关注类型和线程安全的signal/slot机制,i/o系统包括基于插件的网络协议透明的i/o架构,基于插件的应用程序消息日志框架,访问sql数据库的类等等。
ACDK – Artefaktur Component Development Kit
参考网站:http://acdk.sourceforge.net/
这是一个平台无关的C++组件框架,类似于Java或者.NET中的框架(反射机制,线程,Unicode,废料收集,I/O,网络,实用工具,XML,等等),以及对Java, Perl, Python, TCL, Lisp, COM 和 CORBA的集成。
dlib C++ library
参考网站:http://www.cis.ohio-state.edu/~kingd/dlib/
各种各样的类的一个综合。大整数,Socket,线程,GUI,容器类,以及浏览目录的API等等。
Chilkat C++ Libraries
参考网站:http://www.chilkatsoft.com/cpp_libraries.asp
这是提供zip,e-mail,编码,S/MIME,XML等方面的库。
C++ Portable Types Library (PTypes)
参考网站:http://www.melikyan.com/ptypes/
这是STL的比较简单的替代品,以及可移植的多线程和网络库。
LFC
参考网站:http://lfc.sourceforge.net/
哦,这又是一个尝试提供一切的C++库
其他库
Loki
参考网站:http://www.moderncppdesign.com/
哦,你可能抱怨我早该和Boost一起介绍它,一个实验性质的库。作者在loki中把C++模板的功能发挥到了极致。并且尝试把类似设计模式这样思想层面的东西通过库来提供。同时还提供了智能指针这样比较实用的功能。
ATL
ATL(Active Template Library)是一组小巧、高效、灵活的类,这些类为创建可互操作的COM组件提供了基本的设施。
FC++: The Functional C++ Library
这个库提供了一些函数式语言中才有的要素。属于用库来扩充语言的一个代表作。如果想要在OOP之外寻找另一分的乐趣,可以去看看函数式程序设计的世界。大师Peter Norvig在 “Teach Yourself Programming in Ten Years”一文中就将函数式语言列为至少应当学习的6类编程语言之一。
FACT!
参考网站:http://www.kfa-juelich.de/zam/FACT/start/index.html
另外一个实现函数式语言特性的库
Crypto++
提供处理密码,消息验证,单向hash,公匙加密系统等功能的免费库。
还有很多非常激动人心或者是极其实用的C++库,限于我们的水平以及文章的篇幅不能包括进来。在对于这些已经包含近来的库的介绍中,由于并不是每一个我们都使用过,所以难免有偏颇之处,请读者见谅。
书籍
以前熊节先生曾撰文评论相对于Java程序设计语言,C++的好书多如牛毛。荣耀先生在《程序员》杂志上撰文《C++程序设计之四书五经》也将本领域内几乎所有的经典书籍作了全面的介绍,任何关于书的评论此时看来便是很多余的了。个人浅见,除非你打算以C++作为唯一兴趣或者生存之本,一般读者确实没有足够的时间和必要将20余本书籍全部阅读。更有参考价值的是荣耀先生的另一篇文章:《至少应该阅读的九本C++著作》,可以从下面的地址浏览到此文:
http://www.royaloo.com/articles/articles_2003/9CppBooks.htm
下面几本书对于走在C++初学之路上的读者是我们最愿意推荐给大家的:
《C++ Primer》
哦,也许你会抱怨我们为什么不先介绍TCPL,但对于走在学习之路上的入门者,本书内容更为全面,更为详细易懂,我们称它为“C++的超级宝典”并不过分。配有一本不错的习题解答《C++ Primer Answer Book》可以辅助你的学习之路。
《Essential C++》
如果说《C++ Primer》是C++领域的超级宝典,那么此书作为掌握C++的大局观当之无愧。正如《.NET大局观》一书能够让读者全揽.NET,本书讲述了C++中最核心的全部主题。书虽不厚,内容精炼,不失为《C++ Primer》读者茶余饭后的主题回顾之作。
《The C++ Programming Language》
Bjarne为你带来的C++教程,真正能够告诉你怎么用才叫真正的C++的唯一一本书。虽然如同“某某程序设计语言”这样的书籍会给大家一个内容全揽,入门到精通的感觉,但本书确实不太适合初学者阅读。如果你自认为是一名很有经验的C++程序员,那至少也要反复咀嚼Bjarne先生所强调的若干内容。
《Effective C++》,《More Effective C++》
是的,正如一些C++爱好者经常以读过与没有读过上述两本作品来区分你是否是C++高手。我们也极力推崇这两本著作。在各种介绍C++专家经验的书籍里面,这两本是最贴近语言本质,看后最能够有脱胎换骨感觉的书,读此书你需每日三省汝身。
技术书籍仁者见仁,过多的评论反无太多意义,由读者喜好选择最适合自己的书方为上策。
资源网站
正如我们可以通过计算机历史上的重要人物了解计算机史的发展,C++相关人物的网站也可以使我们得到最有价值的参考与借鉴,下面的人物我们认为没有介绍的必要,只因下面的人物在C++领域的地位众所周知,我们只将相关的资源进行罗列以供读者学习,他们有的工作于贝尔实验室,有的工作于知名编译器厂商,有的在不断推进语言的标准化,有的为读者撰写了多部千古奇作……
Bjarne Stroustrup http://www.research.att.com/~bs/
Stanley B. Lippman
http://blogs.msdn.com/slippman/(中文版http://www.zengyihome.net/slippman/index.htm)
Scott Meyers http://www.aristeia.com/
David Musser http://www.cs.rpi.edu/~musser/
Bruce Eckel http://www.bruceeckel.com
Nicolai M. Josuttis http://www.josuttis.com/
Herb Sutter http://www.gotw.ca/
Andrei Alexandrescu http://www.moderncppdesign.com/
侯捷先生 http://www.jjhou.com
孟岩先生 先生繁忙于工作,痴迷于技术,暂无个人主页,关于先生的作品可以通过CSDN的专栏和侯先生的主页访问到。
荣耀先生 http://www.royaloo.com/
潘爱民先生 http://www.icst.pku.edu.cn/panaimin/pam_homepage.htm
除了上述大师的主页外,以下的综合类C++学习参考站点是我们非常愿意向大家推荐的:
CodeProject http://www.codeproject.com
CodeGuru http://www.codeguru.com
Dr. Dobb’s Journal http://www.ddj.com
C/C++ Users Journal http://www.cuj.com
C维视点 http://www.c-view.org
allaboutprogram http://www.allaboutprogram.com
其他资料
ISO IEC JTC1/SC22/WG21 – C++:标准C++的权威参考
http://anubis.dkuug.dk/jtc1/sc22/wg21/
C++ FAQ LITE — Frequently Asked Questions: 最为全面的C++FAQ
http://www.sunistudio.com/cppfaq/index.html
C/C++ 新闻组:
你不妨尝试从这里提问和回答问题,很多不错的Q&A资源……
.alt.comp.lang.learn.c-c++
这个简单些,如果你和我一样是个菜鸟
.comp.lang.c++.moderated
嗯,这个显然水平高一些
.comp.std.c++
如果你需要讨论标准C++相关话题的话
不得不写的结束语
结束的时候也是总结现状,展望未来的时候。虽然C++从脱胎于C开始,一路艰难坎坷的走过来,但是无论如何C++已经取得了工业基础的地位。文章列举的大量相关资源就是最好的证明,而业界的大量用C++写成的产品代码以及大量的C++职业工程师则是最直接的证明。同时,我们可以看到各个高校的计算机专业都开设有C++这门课程,网络上对于C++的学习讨论也从来都没有停过。但是,在Java和.NET两大企业开发平台的围攻下,给人的感觉是C++越来越“不行”了。
C++在面向企业的软件开发中,在开发便捷性等方面的确要比Java和C#差很多,其中一个问题是C++语言本身比较复杂,学习曲线比较陡峭,另外一个问题是C++标准化的时间太长,丧失了很多的壮大机会,耗费了很多精力在厂商的之间的斗争上,而C++的标准库离一个完善的程序开发框架还缺少太多太多的内容,各个第三方的类库和框架又在一致性和完整性上没法和随平台提供的框架相提并论。难道C++真的要退出历史舞台了?
从C++目前的活跃程度,以及应用现状来说是完全能够肯定C++仍然是软件工业的基础,也不会退出历史舞台的。另外从Boost,Loki这些库中我们也能够看到C++的发展非常活跃,对于新技术新思维非常激进,C++仍然广泛受到关注。从ACE在高性能通信领域的应用,以及MTL这样的库在数值计算领域的出色表现,我们可以看到C++在高性能应用场合下的不可替代的作用,而嵌入式系统这样的内存受限开发平台,比如Symbian OS上,C++已经发挥着并且将发挥更大的作用。可以预见的是以后的软件无论上层的应用怎么变,它的底层核心都会是由C/C++这样的系统级软件编写的,比如Java虚拟机,.NET Framwork。因为只有这样的系统级软件才能完全彻底的发挥机器的功能。
需要看到的是两个趋势,一个趋势是C++变得更加复杂,更加学院派,通过模板等有潜力的语法因素构造越来越精巧的库成为了现代C++的热点,虽然在利用库实现新的编程范式,乃至设计模式等方面很有开创意义,也确实产生了一些能够便捷开发的工具,但是更多的是把C++变得更加强大,更加复杂,也更加难懂,似乎也更加学院派,不得不说它正在向边缘化道路发展。另一个趋势是C++在主流的企业应用开发中已经逐渐退出了,ERP这样的企业软件开发中基本上不会考虑C++,除非需要考虑性能或者和遗留代码的集成这些因素。C++退守到系统级别语言,成为软件工业的基础是大势所趋。然而反思一下,真的是退守么?自从STL出现,无数的人风起云涌的开始支持C++,他们狂呼“我看到深夜消失了,目标软件工程的出现。我看到了可维护的代码。”是的,STL在可维护性下做得如此出色。但是又怎样呢?STL为C++铺平了现代软件工程的道路,而在上层应用程序软件开发领域这块场地早不单独属于C++,很多程序设计语言都做得很出色,疯狂的支持者会毫不犹豫地说我们应当支持C++,因为它是世界上最棒的语言。而坦率地说,你的腰杆真的那么硬么?也许只是在逃避一些事实。C++是优秀的,这不可否认,STL的出现让C++一度走上了最辉煌的时刻,然而现在看来……我的一位恩师曾言:真正能够将STL应用得淋漓尽致的人很保守地说国内也不超过200人,或许不加入STL能够使C++向着它应当发展的方向发展的更好,而现在看来,C++也应当回首到真正属于他的那一片圣地上……
Posted: October 18, 2006 at 12:52 pm | Tags: linux, python, ror, server, unix, web, 优化, 技术, 测试, 类
对系统管理员来说,平时的工作重心应该集中在维护系统正常运转,能够正常提供服务上,这里往往牵涉到一个数据备份的问题,在我所了解
的情况中,有80%的系统管理员不是太关心自己服务器的安全性,但往往对备分镜像的技术相当感兴趣,但由于商业产品的软硬件价格都相当高
昂,因此往往会选择自由软件。这里准备介绍的rsync就是这样的软件,它可以满足绝大多数要求不是特别高的备份需求。
一、特性简介
rsync是类unix系统下的数据镜像备份工具,从软件的命名上就可以看出来了——remote sync。它的特性如下:
1、可以镜像保存整个目录树和文件系统。
2、可以很容易做到保持原来文件的权限、时间、软硬链接等等。
3、无须特殊权限即可安装。
4、优化的流程,文件传输效率高。
5、可以使用rcp、ssh等方式来传输文件,当然也可以通过直接的socket连接。
6、支持匿名传输。
二、使用方法
rsync的使用方法很简单,我就举自己使用的例子来说明吧。
1、系统环境
rsync支持大多数的类unix系统,无论是Linux、Solaris还是BSD上都经过了良好的测试。我的系统环境为:
server: FreeBSD 4.3 ip: 192.168.168.52
client: Solaris 8 ip: 192.168.168.137
rsync 版本 2.4.6(可以从http://rsync.samba.org/rsync/获得最新版本)
2、配置server端的/etc/rsyncd.conf文件
bash-2.03# cat /etc/rsyncd.conf
uid = nobody
gid = nobody
use chroot = no # 不使用chroot
max connections = 4 # 最大连接数为4
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsync.lock
log file = /var/log/rsyncd.log # 日志记录文件
[inburst] # 这里是认证的模块名,在client端需要指定
path = /home/inburst/python/ # 需要做镜像的目录
comment = BACKUP CLIENT IS SOLARIS 8 E250
ignore errors # 可以忽略一些无关的IO错误
read only = yes # 只读
list = no # 不允许列文件
auth users = inburst # 认证的用户名,如果没有这行,则表明是匿名
secrets file = /etc/inburst.pas # 认证文件名
[web]
path = /usr/local/apache/htdocs/
comment = inburst.org web server
3、在server端生成一个密码文件/etc/inburst.pas
bash-2.03# cat /etc/inburst.pas
inburst:hack
出于安全目的,文件的属性必需是只有属主可读。
4、在server端将rsync以守护进程形式启动
bash-2.03# rsync –daemon
如果要在启动时把服务起来,有几种不同的方法,比如:
a、加入inetd.conf
编辑/etc/services,加入rsync 873/tcp,指定rsync的服务端口是873
编加/etc/inetd.conf,加入rsync stream tcp nowait root /bin/rsync rsync –daemon
b、加入rc.local
在各种操作系统中,rc文件存放位置不尽相同,可以修改使系统启动时rsync –daemon加载进去。
5、从client端进行测试
下面这个命令行中-vzrtopg里的v是verbose,z是压缩,r是recursive,topg都是保持文件原有属性如属主、时间的参数。–progress是指显示
出详细的进度情况,–delete是指如果服务器端删除了这一文件,那么客户端也相应把文件删除,保持真正的一致。后面的inburst@ip中,
inburst是指定密码文件中的用户名,之后的::inburst这一inburst是模块名,也就是在/etc/rsyncd.conf中自定义的名称。最后的/tmp是备份
到本地的目录名。
在这里面,还可以用-e ssh的参数建立起加密的连接。可以用–password-file=/password/path/file来指定密码文件,这样就可以在脚本中使
用而无需交互式地输入验证密码了,这里需要注意的是这份密码文件权限属性要设得只有属主可读。
bash-2.03# rsync -vzrtopg –progress –delete inburst@192.168.168.52::inburst /tmp/
Password:
receiving file list … done
./
1
785 (100%)
1.py
4086 (100%)
2.py
10680 (100%)
a
0 (100%)
ip
3956 (100%)
./
wrote 190 bytes read 5499 bytes 758.53 bytes/sec
total size is 19507 speedup is 3.43
6、创建更新脚本
如果有比较复杂的工作,利用一些常见的脚本语言可以有帮助。比如:
bash-2.03# cat /usr/local/bin/rsync.sh
#!/bin/sh
DATE=`date +%w`
rsync -vzrtopg –progress –delete inburst@192.168.168.52::inburst /home/quack/backup/$DATE –password-file=/etc/rsync.pass >
/var/log/rsync.$DATE
7、修改/etc/crontab做好定时
比如:
bash-2.03# echo "15 4 * * 6 root rsync.sh">>/etc/crontab
三、FAQ
Q:如何通过ssh进行rsync,而且无须输入密码?
A:可以通过以下几个步骤
1. 通过ssh-keygen在server A上建立SSH keys,不要指定密码,你会在~/.ssh下看到identity和identity.pub文件
2. 在server B上的home目录建立子目录.ssh
3. 将A的identity.pub拷贝到server B上
4. 将identity.pub加到~[user b]/.ssh/authorized_keys
5. 于是server A上的A用户,可通过下面命令以用户B ssh到server B上了
e.g. ssh -l userB serverB
这样就使server A上的用户A就可以ssh以用户B的身份无需密码登陆到server B上了。
Q:如何通过在不危害安全的情况下通过防火墙使用rsync?
A:解答如下:
这通常有两种情况,一种是服务器在防火墙内,一种是服务器在防火墙外。
无论哪种情况,通常还是使用ssh,这时最好新建一个备份用户,并且配置sshd仅允许这个用户通过RSA认证方式进入。
如果服务器在防火墙内,则最好限定客户端的IP地址,拒绝其它所有连接。
如果客户机在防火墙内,则可以简单允许防火墙打开TCP端口22的ssh外发连接就ok了。
Q:我能将更改过或者删除的文件也备份上来吗?
A:当然可以:
你可以使用如:rsync -other -options -backupdir = ./backup-2000-2-13 …这样的命令来实现。
这样如果源文件:/path/to/some/file.c改变了,那么旧的文件就会被移到./backup-2000-2-13/path/to/some/file.c,这里这个目录需要自己
手工建立起来
Q:我需要在防火墙上开放哪些端口以适应rsync?
A:视情况而定
rsync可以直接通过873端口的tcp连接传文件,也可以通过22端口的ssh来进行文件传递,但你也可以通过下列命令改变它的端口:
rsync –port 8730 otherhost::
或者
rsync -e ‘ssh -p 2002′ otherhost:
Q:我如何通过rsync只复制目录结构,忽略掉文件呢?
A:rsync -av –include ‘*/’ –exclude ‘*’ source-dir dest-dir
Q:为什么我总会出现"Read-only file system"的错误呢?
A:看看是否忘了设"read only = no"了
Q:为什么我会出现’@ERROR: invalid gid’的错误呢?
A:rsync使用时默认是用uid=nobody;gid=nobody来运行的,如果你的系统不存在nobody组的话,就会出现这样的错误,可以试试gid =
nogroup或者其它
Q:绑定端口873失败是怎么回事?
A:如果你不是以root权限运行这一守护进程的话,因为1024端口以下是特权端口,会出现这样的错误。你可以用–port参数来改变。
Q:为什么我认证失败?
A:从你的命令行看来:
你用的是:
> bash$ rsync -a 144.16.251.213::test test
> Password:
> @ERROR: auth failed on module test
>
> I dont understand this. Can somebody explain as to how to acomplish this.
> All suggestions are welcome.
应该是没有以你的用户名登陆导致的问题,试试rsync -a max@144.16.251.213::test test
四、一些可借鉴的脚本
这里这些脚本都是rsync网站上的例子:
1、每隔七天将数据往中心服务器做增量备份
#!/bin/sh
# This script does personal backups to a rsync backup server. You will end up
# with a 7 day rotating incremental backup. The incrementals will go
# into subdirectories named after the day of the week, and the current
# full backup goes into a directory called "current"
# tridge@linuxcare.com
# directory to backup
BDIR=/home/$USER
# excludes file – this contains a wildcard pattern per line of files to exclude
EXCLUDES=$HOME/cron/excludes
# the name of the backup machine
BSERVER=owl
# your password on the backup server
export RSYNC_PASSWORD=XXXXXX
########################################################################
BACKUPDIR=`date +%A`
OPTS="–force –ignore-errors –delete-excluded –exclude-from=$EXCLUDES
–delete –backup –backup-dir=/$BACKUPDIR -a"
export PATH=$PATH:/bin:/usr/bin:/usr/local/bin
# the following line clears the last weeks incremental directory
[ -d $HOME/emptydir ] || mkdir $HOME/emptydir
rsync –delete -a $HOME/emptydir/ $BSERVER:USER/$BACKUPDIR/
rmdir $HOME/emptydir
# now the actual transfer
rsync $OPTS $BDIR $BSERVER:USER/current
2、备份至一个空闲的硬盘
#!/bin/sh
export PATH=/usr/local/bin:/usr/bin:/bin
LIST="rootfs usr data data2"
for d in $LIST; do
mount /backup/$d
rsync -ax –exclude fstab –delete /$d/ /backup/$d/
umount /backup/$d
done
DAY=`date "+%A"`
rsync -a –delete /usr/local/apache /data2/backups/$DAY
rsync -a –delete /data/solid /data2/backups/$DAY
3、对vger.rutgers.edu的cvs树进行镜像
#!/bin/bash
cd /var/www/cvs/vger/
PATH=/usr/local/bin:/usr/freeware/bin:/usr/bin:/bin
RUN=`lps x | grep rsync | grep -v grep | wc -l`
if [ "$RUN" -gt 0 ]; then
echo already running
exit 1
fi
rsync -az vger.rutgers.edu::cvs/CVSROOT/ChangeLog $HOME/ChangeLog
sum1=`sum $HOME/ChangeLog`
sum2=`sum /var/www/cvs/vger/CVSROOT/ChangeLog`
if [ "$sum1" = "$sum2" ]; then
echo nothing to do
exit 0
fi
rsync -az –delete –force vger.rutgers.edu::cvs/ /var/www/cvs/vger/
exit 0
4、利用find的一种巧妙方式
rsync -avR remote:’`find /home -name "*.[ch]"`’ /tmp/
可以用这种方法列出需要备份的文件列表——这种方法似乎比较少人用到。
五、参考资料:
1、http://rsync.samba.org/
2、rsync examples
3、rsync FAQ
文章来源:http://xfocus.org/
Posted: October 11, 2006 at 11:04 am | Tags: cache, class, html, java, linux, python, ror, server, unix, web, windows, 优化, 平台, 开发, 技术, 测试, 类
本文适合初学编程的程序员阅读,它对比了几种编程语言在解决同一问题的时候的运效率。并通过具体的例子进行了量化分析。主要目的是帮助初学者认识各种编程语言的特质,并且能够理性的选择适合的编程语言来进行工作。
事发
我无聊的翻着散落案头的书籍,这些都是五花八门的关于编程和系统管理的著作。干了这么多年程序员,大大小小的软件和项目也做了无数。每每有新入行的朋友问我这个所谓的"老前辈":哪种语言最好之类的问题,我总会作出一副知识渊博的样子,复述着从更老的老前辈那里听来的或者某些名著上看来的"知识"。就好比我们从学习编程的第一天起,就被计算机老师告知,COBOL语言是擅长处理商务事务、FOTRAN语言是用于科学计算一样。类似的知识还有"汇编语言比C语言快得多"以及"JAVA是一种效率很低的语言环境"在一代又一代的程序员中口耳相传,几乎成为了毋庸置疑的真理。
我产生了一个想法,能不能对于同一个应用用几种编程语言分别实现,来比较一下看看到底哪种语言效率最高?
老实说我自己都觉得这个想法很无聊,想想谁会反复用不同的语言写同一个程序呢?下雨天打孩子,闲着也是闲着。再说,对于某种语言的弱点和优势有一个量化的分析,对于我们今后在做项目的时候面临工具选择也少许有一点指导意义。另外,觉得好玩才是我做这件事情的真正原因。
选题
选择一个什么样的程序问题进行这样的测试呢?这是一个很关键的问题,也最容易影响测试的公平性。另外的,对于每种语言,各自的优势都是不同的。程序员的偏爱也是各不相同的。在网上和现实中,对于什么语言更好一些的争论从来就没有停止过。甚至的,各门各派的程序员所构成的各种阵营,把某种语言奉若神明的也不在少数。不信,你在CSDN的JAVA论坛说一句"JAVA执行效率太低了云云"试试?立刻会被铺天盖地的板砖掀翻在地。类似的,还有管理员对于操作系统的偏好和争论:在Linux论坛你要是表扬Windows,其惨烈程度简直是难以言状。因此,从这个意义上来说,程序员们对于编程语言的偏好,类似于战士之喜爱枪械,赛手之喜爱赛车,已经上升为一种精神层面的东西了。蔡学镛先生说得好:有人逢微软必反,有人逢微软必捧。这是一种纯粹的精神上的爱,但它可能会影响正常的、科学的思考。
可以预料的,我这篇文章一定会遭到各路豪杰的迎头痛击。
好了,让我们言归正转吧。首先的,我们的选题中要使用的各种程序语言的最常用的要素。什么是最常用的要素呢?当然了,大家都有的就是赋值、数组操作、循环、判断等。另外,对IO的操作也是编程语言重要的内容。其次的,操作时间一定要长,否则,对于解释性的语言来说是极不公平的:解释器还没调入内存呢,人家编译派的已经运行完了。最后,就是程序不能太复杂。除了我没有那么大的毅力用各种语言完成一个复杂算法的决心外,程序过于复杂,算法在测试中起的作用就越来越大,影响运行效率的原因也就增加了。算法过于复杂,开发工具的扩展部分用得也就越多。于是就成了语言附加库之间的竞赛了,这是我不愿意看到的。
考虑上述因素,我设计了一个简单的选题:从指定文本文件中搜索指定字符串,计算个数。并且打印出搜索到的个数作为结果输出。作为程序员的你粗粗过一下脑子,马上会想到这个算法里面包含了条件判断、循环、数组操作等基本的程序语言因素。这满足了上面第一个条件。另外的,为了满足第二个条件,我准备了一个多达2G的文本文件,总共有文本1500万行多。这保怔了足够的运行时间(但应该不会太长),而决不会一眨眼就执行完了。最后的,我们都知道,在文本串里面搜索子串的算法是数据结构课本中的一个典型的例子(考试也经常被考到的),也满足算法简单的要求。同时,为了让每个程序的环境都一样,我得每测试一次就重新启动一次机器,避免CACHE的影响。
准备
比赛嘛,就需要公平。首先的,硬件平台要统一。我找了一台看起来还不错的机器(服务器):两颗PIII800,1G内存。操作系统嘛,原来的机器上有新装的Windows2000Server版本。几乎没装什么别的应用。我偷懒了一下,没有重新安装OS,就这样用吧。
第一个选手:PERL
如果别人交给我这个题目,我会马上决定用PERL语言来做这件事。这个题目是完全的文本处理问题,还有比用PERL来做更合适的吗?因为PERL是专门为了文本处理而编制的语言。事实上也是这样,我用了2分钟,写了几行代码,就轻松实现了这个问题。这也说明了,选择适用的编程语言工具,比选择喜爱的工具更重要。
#!/usr/bin/perl
$filename="d:\access.log_";
$count = 0;
open(FILE , "<$filename");
while(<FILE>)
{
@match_list = ($_ =~ /HIT/g);
$count=$count+@match_list;
}
close(FILE);
print "Count = $count ";
exit
PERL是一位语言学家Larry Wall发明的,事实上,早期这种语言是专门用于在UNIX平台处理文字文件的(Perl=Practical Extraction Report Language:实用报表析取语言)。后来人们发现有大量文本构成的HTML页面用PERL来做CGI程序生成动态页面再合适不过了。因为互联网的兴起,PERL跟着发大了起来。这种语言的语法和C语言基本类似,因此比较好掌握,并且的,其关于"正则表达式"处理的强大功能目前基本上无人能够望其项背。事实上,类似于"过滤出含有TOM或者ABC的、并且后者的第一个和第三个字母大写,前者最少出现2次,后者出现5次、而且中间间隔8个或4个字母或空格的文本行"。我猜你正在反复的揣摩这句话,事实上,这就是所谓正则表达式,这样的问题,在PERL只需要一行语句就可以完成。在C语言中需要多少语句才能实现呢。
我略略解释一下上面的程序,让没有用过PERL语言的程序员也有个感性认识。
第一行是在UNIX中才用得到,因为PERL是一种基于解释的脚本语言。
第四行是打开文件
下面的循环是一行一行的读文件的内容。循环中间的第一句话是把凡是文本行中含有的HIT全部放到一个数组中;循环中中的第二句话是统计一下刚才的数组中有几个HIT,然后累加起来。循环完成了,我们的任务也就完成了。怎么样,很简单吧?"/HIT/g"就是最简单的正则表达式。
现在的PERL语言早已经不是原来的脚本语言形象了,现代PERL几乎具备了其特语言的所有特性,并且的在模块的功能帮助下,可以实现很大的应用。而且还增加了一些面向对象的特点。尽管大多数人仍然在用它处理大量的文本,但也有使用PERL完成大型应用的,尤其是在WEB方面。值得一提的是PERL也是一个跨平台语言。
我的这个程序在测试平台上,使用PERL5.8解释器,用了8分18秒08完成了1500万行文本的扫描,并得出了正确的结果。
第二个选手:纯C
也许年龄大了,但是我真的很喜欢C语言。而且我最喜欢的就是使用指针和强制类型转换来任意操作数据。我甚至会在程序里通过指针手工拼凑一个长整性的数据。说句可能引起争议的话,我觉得JAVA语言抛弃可爱的指针的做法基本上就是逃避。因为掌握不好就不用,到头来就是牺牲了效率。
本文这个题目,用C语言来实现应该还是比较不错的选择。下面的代码就是在VC下面实现的纯C代码的字符串搜索程序(为了避免图形界面的干扰,一律做成控制台程序)。编译的时候使用速度优先编译选项。
#include <stdio.h>
#include <string.h>
void main()
{
int len=2048;
char filename[20];//文件名
char buff[10000];//文件缓冲区
char hit[5];
FILE *fd;
int i,j,flag=0,over=0;
int max,readed;
int count=0;//最后的结果
strcpy(&filename[0] , "d:\access.log_");
strcpy(&hit[0] , "HIT");
buff[0]=0×0;
buff[1]=0×0;
//打开文件:
if((fd = fopen(&filename[0] , "rb"))==NULL)
{
printf("Error : Can not open file %s ",&filename[0]);
}
//读取文件内容
while(over != 1)
{
readed = fread(&buff[2] , 1 , len , fd);
if(readed < len)
{
over=1;
max=readed;
}
else
{
max=len;
}
for(i=0;i<max;i++)
{
for(j=0;j<3;j++)
{
if(hit[j] != buff[i+j])
{
flag=0;//一旦有一个不相同就退出并且标志为0
break;
}
else
{
flag=1;//一个相同为1,如果连续都相同最后结果定是1
}
}
if(flag==1)
{
count++;
i+=j-1;
}
else
{
if(j==0)
{
i+=(j);
}
else
{
i+=(j-1);
}
}
}
//把最后两个字符转移到前面两个字节以防止切断搜索串.
buff[0]=buff[max];
buff[1]=buff[max+1];
}
fclose(fd);
printf("count:%d ",count);
}
程序很好懂,用的也是教科书上面的标准字符串搜索算法,但是比前面的PERL程序长多了吧?那是因为人家PERL已经帮你完成了大部分工作。但是看到上面这段程序的运行结果你可能会高兴起来,它最快一次只用了2分10秒52,最慢也只用了2分20秒59就完成了1500万行文本的搜索任务。平均2分15秒多。为什么每次时间不一样呢?我不清楚具体原因,但学过操作系统的朋友会明白,只有在单道单任务的系统中,代码才能有执行上的可再现性。
有经验的朋友可能会说,你的缓冲区只用了2048字节,加大它速度还会增加呢。是的,而且我相信还有高手能作出更快的程序来,但这不重要,重要的是我们要考察的是不同语言完成同一件工作的效率。而且你能够明白,在程序中,改进什么能够提高效率,这就足够了。因为C语言程序中,这些都是自由可控的。
第三个选手:C++
C++和前面的C是亲戚。我简单的把前面的C代码移植过来,然后把文件输入部分改成了流类对象。至于算法部分嘛。跟前面的C是一模一样的。最后在编译的时候,除了使用速度最佳编译选项外,当然还用了C++的编译参数,因此执行文件的长度比前面的C要长一些,这说明我加的流类代码比标准C库要复杂。是的,C++应该说是目前流行的计算机编程语言中复杂度排名靠前的。其复杂的类和继承关系,以及各种初始化的次序和构造函数执行顺序等都需要考虑。还有多态以及动态联编技术等。C++也是我非常喜欢的语言,提供了面向对象的代码重用特性和足够的安全型,但是在效率上的确比纯C略逊一筹。你知道吗,大部分的操作系统核心几乎都是用纯C写成的,尽管很复杂,但很少有使用面向对象技术的。为什么,不是面向对象技术不好,也不是操作系统核心不够复杂(那什么复杂?),主要的考虑就是效率问题。
#include <stdio.h>
#include <string.h>
#include <fstream.h>
void main()
{
int len=2048;
char filename[20];//文件名
char buff[10000];//文件缓冲区
char hit[5];
int i,j,flag=0;
int max;
int count=0;//最后的结果
strcpy(&filename[0] , "d:\access.log_");
strcpy(&hit[0] , "HIT");
buff[0]=0×0;
buff[1]=0×0;
//用输入流打开文件:
ifstream input(&filename[0]);
//读取文件内容
while(input)
{
input.getline(&buff[2] , len);
max = strlen(&buff[2]);
for(i=0;i<max;i++)
{
for(j=0;j<3;j++)
{
if(hit[j] != buff[i+j])
{
flag=0;//一旦有一个不相同就退出并且标志为0
break;
}
else
{
flag=1;//一个相同为1,如果连续都相同最后结果定是1
}
}
if(flag==1)
{
count++;
i+=j-1;
}
else
{
if(j==0)
{
i+=(j);
}
else
{
i+=(j-1);
}
}
}
}
printf("count:%d ",count);
}
这段C++程序在测试平台上用了最快4分25秒95 到最慢5分40秒68的时间完成1500万行的文本检索,并在2G的文件中检索出10951968个"HIT"字符串。这结果是正确的。
第四个选手:汇编
本以为汇编程序能够达到前所未有的高速,把前面的选手远远抛在身后而笑傲江湖。这一想法支撑我完成了艰涩的代码。可事实上测试的结果缺让我大失所望,完全用机器指令书写的程序,去掉缓冲区才几百字节,算法和前面的C程序一模一样,扫描1500万行文本竟然最快也要2分14秒56!这甚至还比不过C语言的最快纪录。而平均下来,汇编程序的速度竟然和前面的C程序在伯仲之间。恐怕这样的结果也出乎大部分人的意外。因为我们从入行的那一天起,就被告知汇编是你所能够掌握的最快的语言!尽管代码坚涩难懂,但性能的代价是值得的。而从这里的测试看,你觉得向下面这样的代码,实现和C语言一样的速度和功能值得吗?
;堆栈段
STSG SEGMENT STACK ‘S’
DW 64 DUP(?)
STSG ENDS
;数据段
DATA SEGMENT
rlength EQU 2048
fname DB ‘access.log_’,0
hit DB ‘HIT$’
fd DW ? ;文件句柄
resault DB ‘count : $’ ;结果提示
count DD 0 ;存放结果
disflag DB 0 ;显示标志
buff DB 5000 dup(0) ;缓冲区
DATA ENDS
;代码段
CODE SEGMENT
MAIN PROC FAR
ASSUME CS:CODE,DS:DATA,SS:STSG,ES:NOTHING
MOV AX,DATA
MOV DS,AX
;我的代码开始:
mov ah,3dh ;打开文件
lea dx,fname
mov al,00h ;文件打开方式
int 21h ;开始操作
;这里就不作错误处理了,偷懒喽!
;CF=0表示正确,CF=1表示错误,AX是文件句柄或者是错误代码
mov fd,ax ;保存文件句柄
READ: mov ah,3fh ;读文件
mov bx,fd ;文件句柄
mov cx,rlength ;要读length字节
lea dx,buff ;给出读缓冲区指针
add dx,2 ;缓冲区指针向后错两个(目的是解决边界问题:有一个HIT正好横跨rlength界限)
int 21h ;开始读
;AX里面是实际读出的字节数
;读完了以后,扫描缓冲区
push ax ;保存AX字节数
cmp ax,0
jz ALLEND ;文件读完了就退出
sub dx,2 ;指针向前错2个,
mov si,dx
add dx,2 ;把指针回到原来的位置
add dx,ax ;计算结尾
LOD3: cmp si,dx ;到头了就重新读一次文件
jz OVR
lods buff
lea bx,HIT
cmp al,[bx]
jnz LOD3 ;读第一个字节不相等就重新读一个
cmp si,dx
jz OVR
lods buff
cmp al,[bx+1]
jnz LOD3 ;如果第一个字节相等,就读第2个字节,不行等就从第一个字节再重比较。
cmp si,dx ;如果第二个字节也相等的话,就比较第三个字节。
jz OVR
lods buff
cmp al,[bx+2]
jnz LOD3 ;第三个字节不相等再从头开始
;有一个HIT匹配
push bx
lea bx,count
add WORD ptr [bx],1 ;计数器增加一个
adc WORD ptr [bx+2],0 ;进位
pop bx
jmp LOD3
OVR: mov ah,[si-1]
mov BYTE ptr buff+1 , ah
mov ah,[si-2]
mov BYTE ptr buff , ah
pop ax ;恢复这次总共读出的字节数
cmp ax,rlength ;看看是不是最后一次(剩余的零头)
jz READ
;如果是最后一次读文件,
ALLEND: mov ah,3eh ;关闭文件
mov bx,fd ;文件句柄
int 21h ;关闭文件
mov ah,9 ;显示结果字符串
lea dx,resault
int 21h
;转换2进制结果到10进制ACSII形式
mov bx, WORD ptr count
call TERN
mov ax,4c00h ;返回DOS
int 21h
;结束代码,最大的数字已经排到了最前面
MAIN ENDP
TERN PROC ;这个子程序是转换并显示2进制数字的
mov cx,10000
call DEC_DIV
mov cx,1000
call DEC_DIV
mov cx,100
call DEC_DIV
mov cx,10
call DEC_DIV
mov cx,1
call DEC_DIV
ret
TERN ENDP
DEC_DIV PROC
mov ax,bx
mov dx,0
div cx
mov bx,dx
mov dl,al
add dl,30H
mov ah,disflag ;read flag
cmp ah,0
jnz DISP ;已经显示过有效数字了
cmp dl,30H
jz NODISP
mov disflag,1 ;作用是第一个有效数字出现前不显示0
DISP: mov ah,2
int 21H
NODISP: ret
DEC_DIV ENDP
CODE ENDS
END MAIN
上面这段代码我猜你也懒得仔细阅读。其实他不能"显示结果"。因为最后这段负责把最终结果转换成可显示ASCII码的程序实际上只能转换二进制十六位的数据,而最终的结果高达1000万挂零,显示会出错。由于这最终结果的显示已经和程序的运行没有大关系了,因此,我也就懒得去写一个32位的ASCII转换程序了。就这样吧。
第五个选手:JAVA
JAVA是一个不能不参加比赛的选手。有如此多的人热爱他,他们中的一半人是因为JAVA的面向对象特性以及良好的跨平台特性。而另一半人纯粹就是因为JAVA不姓"微(软)",这就是意识形态在程序员头脑中对某种语言的注释。单纯从语言元素上来说,我还是比较喜欢JAVA的。因为他的语法干净、简洁。环境也好。虽然用虚拟机系统(JVM)的做法来实现跨平台特性并非什么了不得的创意(像不像30年前的BASIC解释器?别跟我说什么中间代码?几乎所有的解释器都是把语言因素翻译成中间代码的,JVM不过是分成2步来实现罢了,但从运行机制上应该是差不多的。),但JVM仍然将JAVA的跨平台特性做到了前所未有的地步。而且JVM是一个很干净的系统,让人用起来赏心悦目。说到这里我忍不住想提一下J2EE企业应用框架了。不知道有多少人能够看懂SUN出的J2EE的"理论著作"?满纸充斥着各种生造的概念,洋溢着溢美之词。JAVA的企业应用框架实在是比较复杂的东西,虽然赶不上后来的.NET框架,但足以让大多数初学者望而却步。一句话,东西太多了。事实上JAVA的企业级应用并没有想象的成功,iPlanet就随着电子商务概念的全面垮台而渐渐淡出。现在换了个名叫“SUNONE”――SUN公司员工原话。
我们回到JAVA的语言元素上来说,实际上JAVA可以被理解为被纯化的C++。JAVA去除了C++为了兼容C而增加的一些"非面向对象特质",用其他的一些变通办法实现C++直接实现的功能,比如:多继承。在实现机制上,JAVA的程序会先编译成.CLASS文件,然后这种跨平台的中间代码就可以"一次编译,到处运行"了。当然必须运行在有JVM虚拟机的环境中,连图形什么的都可以照搬。换句话说,你用JAVA程序在PC屏幕上画一个圆,在JAVA-PDA上它还是圆的。
我在本次测试中,写了下面的代码,用JAVA做了同样的测试,测试中实际上用到了:JAVA的文件流类,运行了循环、条件判断、数组操作等基本的语言因素。环境是J2SE1.3.1-06。JAVA程序做1500万行的文本扫描用了8分21秒18。应该说是几种语言中最慢的,基本上和纯解释的PERL是在同一水准。J2EE的JVM环境还是经过优化的所谓HOTSPOT。
import java.io.*;
public class langtest
{
public static void main(String[] args)
{
String filename = "d:\access.log_";
try
{
count(filename);
}
catch (IOException e)
{
System.err.println(e.getMessage());
};
}
public static void count(String filename) throws IOException
{
long count=0;
long len;
String strline = "";
char hit[] = {‘H’,'I’,'T’};//要搜索的字符串
char buff[] = new char[2100];
Reader in = new FileReader(filename);//用FileReader类构造产生一个Reader类对象
LineNumberReader line = null;//生成一个空指针
try
{
line = new LineNumberReader(in);//建立LineNumberReader类对象
while((strline = line.readLine()) != null)
{
//到这里已经读出一行了,用下面的代码分析这行有几个HIT
int i=0,j=0,max=0,flag=0;
buff = strline.toCharArray();//转换成字符数组
max = strline.length();
for(i=0;i<max;i++)
{
for(j=0;j<3;j++)
{
if(hit[j] != buff[i+j])
{
flag=0;//一旦有一个不相同就退出并且标志为0
break;
}
else
{
flag=1;//一个相同为1,如果连续都相同最后结果定是1
}
}
if(flag==1)
{
count++;
i+=j-1;
}
else
{
if(j==0)
{
i+=(j);
}
else
{
i+=(j-1);
}
}
}
}
System.out.println("Count : "+count);
}
catch (IOException e)
{
System.err.println(e.getMessage());
}
finally
{
try
{
if(in != null) in.close();
}
catch (IOException e)
{
}
}
}
}
候捷先生翻译的宏篇巨著《JAVA编程思想》一书中第67页说到:"使用最原始的JAVA解释器,JAVA大概比C慢上20到50倍"之说法我在阅读的时候就心存疑虑,心想要是这样,JAVA完全没有存或与世间的必要了。在亲自动手试验过后,我觉得说JAVA在J2EE环境下,比C慢上2-3倍还是比较可靠的说法的。况且,目前越来越多的硬件JVM的诞生,也给JAVA越来越多的机会。不过我担心的正是这点,JVM的多厂家多样化很可能会造成某些兼容性方面的问题。例如我见过一篇文章就是讨论某种JAVA程序在IBM-JVM可用而在SUN-JVM上不可用之事例。但愿的,JAVA能健康成长。
总结
事实上,本文有两个基本的意义传递给初做程序员的读者:
一、 抛开你的意识形态好恶,选择最合适的编程语言来完成你的工作。每种流行的语言都有自己存在的意义。
二、 在编程中,有想法就自己做一做,你会得出自己的结论。
至此,你应该明白,前面的所有测试结果其实并不重要,重要的是你了解了这些语言的特质,也许在今后的编程生涯中会因此增加一点点"经验"呢。
后记
本来笔者还打算继续测试一下另外的一种颇为流行的解释语言Python和新贵C#以及在Linux平台完成这些测试,但终究还是被懒惰瓦解了斗志。好在的,Python和Perl比较相似,而C#和JAVA有异曲同工之妙。也可以略略做一点参考。
事实上,本文测试中有一个大大的不公平之处,相信仔细的读者已经发现了:其中C和ASM都是使用缓冲区直读的办法,不管三七二十一就进行判断(最后用指针检查缓冲区边界)。而C++等其他的语言虽然用了非常方便的流按行读出,但是多做了很多事情:每一个字符都要判断其是不是回车换行符,而按行读近来,每次缓冲的也要少很多。因此其他几种语言就大大的吃亏了。不过这并不影响结论性的东西,因为测试本身就说明越方便就效率越低。事情总是要有人做,不是吗?
Posted: September 1, 2006 at 6:33 pm | Tags: cache, linux, python, 优化, 平台, 类
1.1. 新的,改进的及不再赞成使用的模块
象以前一样, Python 标准库变得更强更健壮. 这里列出了大部分值得注意的变化, 按模块名字母序排列. 请参考源代码树中的 Misc/NEWS 文件以了解更完整的变化列表, 或者通过 SVN 日志了解所有的细节.
gc 模块新增了一个 get_count() 函数. 它返回一个 3-元素 tuple: 内容是当前三个 GC 生成器的垃圾收集数目. 这是垃圾收集器的统计信息,当这个数值到达一个指定值, 就会执行一个清扫动作.原有的 gc.collect() 函数现在接受一个可选的 生成器 参数 ( 0, 1, 或 2 )以指定由哪个 生成器 进行收集.
heapq 模块中的 nsmallest() 和 nlargest() 函数现在支持一个新的关键字参数以提供类似 min()/max() 和 sort() 的功能. 这里有一个例子:
>>> import heapq
>>> L = ["short", 'medium', 'longest', 'longer still']
>>> heapq.nsmallest(2, L) # Return two lowest elements, lexicographically
['longer still', 'longest']
>>> heapq.nsmallest(2, L, key=len) # Return two shortest elements
['short', 'medium']
(贡献者: Raymond Hettinger .)
itertools.islice() 函数现在接受 None 作为 start 和 step 参数. 这使得它与 slice 对象更兼容. 你现在能够象下面这样写代码了.:
s = slice(5) # 创建一个 slice 对象 itertools.islice(iterable, s.start, s.stop, s.step) (贡献者: Raymond Hettinger .)
operator 模块的 itemgetter() 和 attrgetter() 函数现在支持多个域了. 类似 operator.attrgetter(‘a’, ‘b’) 会返回一个拥有 a 和 b 属性的函数经.结合这个新特性及 sort() 方法的 key 参数,你能够很容易的使用多个域对一个列表进行排序.
os 模块有了很多变化. stat_float_times 变量的默认值变成了 true, 这意味着 os.stat() 将以浮点数的格式返回时间值.(这并不是说 os.stat() 就一定返回带有小数点的时间数:因为不是所有的平台支持这样的精确度.)
增加了os.SEEK_SET, os.SEEK_CUR, 和 os.SEEK_END 常量; 这些都是 os.lseek() 函数的参数. 用于 locking 的两个新的常量是 os.O_SHLOCK 和 os.O_EXLOCK.
增加了两个新函数: wait3() 和 wait4(). 它们的行为类似 waitpid() 函数: 等待一个子进程退出并返回一个元素为进程的 ID 及 退出状态的 tuple.不同之处在于 wait3() 和 wait4() 返回更多的信息. wait3() 不接受进程 ID 参数, 它等待任何子进程退出并返回一个 3-元素(进程id,退出状态,资源使用)的tuple. 类似 resource.getrusage() 函数的返回值. wait4(pid) 接受一个进程 ID 作为参数. (XXX 贡献.) 在FreeBSD上, os.stat() 函数现在返回精确到十亿分之一秒的时间, 并且返回的对象现在拥有 st_gen 和 st_birthtime 属性. 只要平台支持,st_flags 成员也是可用的.
自 Python 2.0 以来就不再推荐使用的陈旧的 regex 和 regsub 模块, 终于从标准库中删除了. 其它删除的模块还有: statcache, tzparse, whrandom.
以前用来包含那些类似 dircmp 和 ni 等古老模块lib-old 目录,也被删除. 除非你的代码显式的添加了这个目录到 sys.path, lib-old 不再位于默认 sys.path 内.这应该不支影响到你的代码.
socket 模块现在在 Linux 上 支持 AF_NETLINK 了, 感谢 Philippe Biondi 的patch. Netlink sockets 是 Linux平台上的用于用户空间进程与内核的通信的专有机制. http://www.linuxjournal.com/article/7356 是一篇它的介绍文章. 在 Python 代码中, netlink addresses 被表示一个由两个整数组成的 tuple(pid, group_mask).
getfamily(), gettype(), 和 getproto() 方法用来得到 Socket对象的 family, type, 和 protocol 值.
新模块: spwd 提供了访问 shadow 口令库的一系列函数(在支持的平台上).
tarfile 模块中的 TarFile 类现在有了一个 extractall() 方法以释放出 tar 包中所有的文件到当前目录.当然你也可以指定一个不同的目标目录, 及指定解包哪些特定文件.
一个 tarfile 的压缩格式可以通过使用模式 ‘r|*’ 自动检测. (Lars Gustäbel 贡献.)
unicodedata 模块做了更新,现在使用 4.1.0 版本的 Unicode 字符数据库了. 某些平台需要版本 3.2.0, 因此可以用 unicodedata.db_3_2_0 使用这个老的版本.
xmlrpclib 模块现在支持XML-RPC 日期类型返回datetime对象. 需要提供 use_datetime=True 参数给loads()函数或Unmarshaller类以允许这个特性.
1.1.1. 13.1 ctypes 包
ctypes 包的作者是 Thomas Heller, 这个包被加入到标准库中. ctypes 使你能够调用共享库或DLL中的任意函数.
将来发行 Python 2.5(正式版或BETA版?)时 , 我会添加一个简短的关于如何使用这个模块的介绍.
1.1.2. 13.2 ElementTree 包
用于处理 XML的 ElementTree 库(作者:Fredrik Lundh )的子集被添加到标准库中,名字为 xml.etree. 可用的模块有 ElementTree, ElementPath, 和 ElementInclude (ElementTree版本 1.2.6).
将来发行 Python 2.5(正式版或BETA版?)时 , 我会添加一个简短的关于如何使用这个模块的介绍(约一页长). 完整的 ElementTree 文档在http://effbot.org/zone/element-index.htm.
1.1.3. 13.3 hashlib 包
添加了一个新的 hashlib 模块以替换掉 md5 和 sha 模块. hashlib 添加了更多的安全 hashes (SHA-224, SHA-256, SHA-384, and SHA-512)支持. 只要可能,这个模块就会使用 OpenSSL 进行快速的平台优化的算法实现.
旧的 md5 和 sha 模块仍然以 hashlib 封装器的形式存在,以提供向后兼容性.新模块的接口非常接近旧模块,但不是一模一样.最大的不同在于创建新哈希对象的构造函数 的命名不同.
# Old versions h = md5.md5() h = md5.new()
# New version h = hashlib.md5()
# Old versions h = sha.sha() h = sha.new()
# New version h = hashlib.sha1()
# Hash that weren’t previously available h = hashlib.sha224() h = hashlib.sha256() h = hashlib.sha384() h = hashlib.sha512()
# Alternative form h = hashlib.new(‘md5′) # Provide algorithm as a string
一旦创建了一个哈希对象, 它的方法就和以前相同: update(string) hashes指定的字符串到当前的 digest 状态, digest() 和 hexdigest() 以一个二进制字符串或16进制字符串的形式返回 digest 值, copy() 返回一个同样digest 状态的新的哈希对象.
贡献者: Gregory P. SmithThis
1.1.4. 13.4 sqlite3 包
pysqlite 模块(http://www.pysqlite.org), 一个嵌入式数据库 SQLite 的封装器, 被加入到标准库中, 包的名字是 sqlite3. SQLite 是一个 C 库,在不需要一个独立服务器进程的情况下实现了一个支持 SQL的全功能数据库. 它将数据保存为一个单一的磁盘文件.pysqlite 由 Gerhard Häring 完成, 提供了一个 兼容 DB-API 2.0 的SQL 接口.这意味着你可以用 SQLite 来书写你的应用程序的第一个版本, 在必要时将数据库切换到一个类似postgreSQL或Oracle的大的数据库, 这种切换将是相当的轻松.
如果你是自己编译 Python , 要注意源码树中并不包含 SQLite 代码, 而只有封装模块. 你必须在编译 Python 之前安装 SQLite 库和头文件, 之后 build 进程才会编译这个模块.
将来发行 Python 2.5(正式版或BETA版?)时 , 我会添加一个简短的关于如何使用这个模块的介绍.
转至http://www.woodpecker.org.cn
Previous Page