konhon

忘掉過去,展望未來。找回自我,超越自我。
逃避不一定躲的过, 面对不一定最难过, 孤单不一定不快乐, 得到不一定能长久, 失去不一定不再拥有, 可能因为某个理由而伤心难过, 但我却能找个理由让自己快乐.

Google

BlogJava 首页 新随笔 联系 聚合 管理
  203 Posts :: 0 Stories :: 61 Comments :: 0 Trackbacks

2007年3月8日 #

   很多股民常常感到孤独无助,买入的股票总是被套,这里面折射出一个问题,那就是你没有真正的掌握好买入点,今天我就在这里给大家讲一讲如何把握买入点。

  由于股票变化无常,主力更是绞尽脑汁和散户斗智斗勇,所以买入法也没有固定格式,我在这里只简单的介绍几种很有效的方法,仅供你借鉴。

     1、量缩法。顾名思义,就是当股票在一定“趋势和条件”下,成交量开始萎缩到不能再萎缩时,说明下跌能量消耗已尽,此时就是最好的买入点。所谓的“趋势和条件”是讲——这只股票运行趋势必须是在上升通道或低位震荡中,当它连续下跌两到三天时,成交量已经萎缩很多,股价波动范围已经很小,盘中也不再见有大的买单,特别是遇到大盘下跌时,它也不再下跌,这说明这只股票到了“谷”底,随时有反弹和新的行情发动,此时就是最价买入点。例如600297在1月30号到2月2号,连续下跌了四天,切成交量逐步萎缩,2月2号就是个最价买点。

     2、前高突破法。当一只股票经过一段时间的上涨,之后开始进入回调,当经过一段时间的调整,再次开始放量攻击前期高点时,此时也是买入点。但是这里最难把握的就是主力的假突破,很容易上当,这需要你自己正确判断。比方说600983在1月23达到一个高点后,2月8号开始放量攻击前期高点,那么2月8号就是最价买点。而600717在3月8号就是一个假突破。我特别强调一点,最好是出现三次冲击前期高点的股票,把握性最大。

     3、长阳之后等待法。就是当某只股票出现长阳甚至是涨停时,第二天它很可能不再连续拉升,而是出现小幅的调整,此时也是买入点,例如600723在1月18日就是一个买入点。

     4、诱空识破法。很多主力在进入下一波的大行情中,往往做最后的诱空洗盘。此时也是最价买入法,这需要你高超的识盘技巧。例如,600176在1月31号,出现了一条放量的长阴,其实那是诱空,做出这样的判断是要对这只股票的基本面和技术面都非常熟悉才行。

     5、大换手率的等待法。当某只股票在低位(特别强调是低位)出现大换手率时,说明主力杀入,你随时可以跟进。例如600178在1月19和22连续两天出现超过12%的还手率,之后再出现低点时,你就可以杀入了。
posted @ 2007-04-04 06:52 konhon 优华 阅读(1410) | 评论 (0)编辑 收藏

http://www.openitpower.com/wenzhang/97/10846_1.html

  与TTable、TQuery一样,TClientDataSet也是从TDataSet继承下来的,它通常用于多层体系结构的客户端。TClientDataSet最大的特点是它不依赖于BDE(Borland Database Engine),但它需要一个动态链接库的支持,这个动态链接库叫DBCLIENT.DLL。在客户端,也不需要用TDatabase构件,因为客户端并不直接连接数据库。
  由于TClientDataSet是从TDataSet继承下来的,所以,它支持诸如编辑、搜索、浏览、纠错、过滤等功能。由于TClientDataSet在内存中建立了数据的本地副本,上述操作的执行速度很快。也正是由于TClientDataSet并不直接连接数据库,因此,客户程序必须提供获取数据的机制。在Delphi 4中,TClientDataSet有三种途径获取数据:
.从文件中存取数据。
.从本地的另一个数据集中获取数据。
.通过IProvider接口从远程数据库服务器获取数据。
  在一个客户程序中,可以同时运用上述三种机制获取数据。
11.1 浏览和编辑数据
  和其他数据集构件一样,可以用标准的数据控件显示由TClientDataSet引入的数据集,当然,这需要借助于TDataSource构件。
  由于TClientDataSet是从TDataSet继承下来的,所以,凡是其他数据集构件支持的功能,TClientDataSet构件也大致具备。不同的是,TClientDataSet能够在内存中建立数据的副本,因此,TClientDataSet比其他数据集构件增加了一些特殊的功能。
11.1.1 浏览数据
  可以用标准的数据控件显示由TClientDataSet引入的数据集。在运行期,可以调用诸如First、GotoKey、Last、Next和Prior等函数来浏览数据。
  TClientDataSet也支持书签功能,可以用书签来标记某条记录,以后就可以方便地找到这条记录。
  对于TTable、TQuery等数据集构件来说,只能读RecNo属性来判断当前记录的序号。对于TClientDataSet构件来说,还可以写RecNo属性,使某一序号的记录成为当前记录。
11.1.2 CanModify属性
  TDataSet的CanModify属性用于判断数据集中的数据是否可以修改。CanModify属性本身是只读的,也就是说,数据是否能够修改不取决于应用程序。
  不过,TClientDataSet构件有其特殊性,因为TClientDataSet已经把数据在内存中建立了副本,因此,应用程序可以决定是否允许修改数据。如果不允许用户修改数据,只要把ReadOnly属性设为True,此时,CanModify属性肯定返回False。
  与其他数据集构件不同,修改TClientDataSet构件的ReadOnly属性时,不需要事先把Active属性设为True。
11.1.3 取消修改
  TClientDataSet传输数据的基本单位称为数据包,当前的数据包可以由Data属性来访问。不过,用户对数据的修改并不直接反映到Data属性中,而是临时写到一个日志即Delta属性中,这样做的好处是以后随时可以取消修改。
  不过,这里要说明一点,尽管用户的修改并没有反映到Data,当用户在数据控件中看到的却是最新修改的数据。如果一条记录被反复修改了多次,用户看到的只是最新的数据,但日志中却记载了多次。
  要取消上一次的修改,调用UndoLastChange函数。UndoLastChange需要传递一个布尔类型的参数叫FollowChange,如果FollowChange参数设为True,光标就移到被恢复的记录上,如果FollowChange参数设为False,光标仍然在当前记录上。
  ChangeCount属性返回日志中记载的修改次数。如果一条记录被反复修改了多次,每调用一次UndoLastChange能够逐级取消上一次的修改。
  UndoLastChange只能取消上一次的修改,如果想一下子取消所有的修改,首先要选择一个记录,然后调用RevertRecord。RevertRecord将从日志中取消所有对当前记录的修改。
  TClientDataSet还有一个SavePoint属性,它能把当前的编辑状态保存起来,以后随时可以返回当时的状态。例如,可以这样保存当前的状态:
  BeforeChanges := ClientDataSet1.SavePoint;
  以后,可以这样来恢复当时的状态:
  ClientDataSet1.SavePoint := BeforeChanges;
  应用程序可以保存多处状态,可以恢复其中一个状态,不过,一旦某个状态被恢复,在其之后的状态就无效。
  如果要一下子取消日志中记载的所有修改,可以调用CancelUpdates函数。CancelUpdates将把日志清空,取消所有的修改。
  如果LogChanges属性设为False,用户对数据的修改就会直接反映到Data属性中。
11.1.4 合并修改
  要把日志中记载的修改合并到Data属性中,有两种方式,具体使用哪一种方式,取决于应用程序获取数据的机制。不过,不管是哪种机制,合并后,日志自动被清空。
  对于一个从文件中获取数据的程序来说,只要调用MergeChangeLog函数,就把日志中记载的修改合并到Data属性中。不用担心其他用户同时修改了数据。
  对于一个从应用服务器获取数据的程序来说,就不能调用MergeChangeLog来合并数据,而要调用ApplyUpdates函数,ApplyUpdates会把日志中记载的修改传递给应用服务器,待应用服务器成功地把数据更新了数据库服务器后,才会合并到Data属性中。
11.1.5 纠错
  TClientDataSet支持纠错功能。一般情况下,需要自己建立纠错规则,以便对用户输入的数据进行纠错。
  此外,如果获得了IProvider接口的话,还可以从远程服务器引入纠错规则。
  有时候,客户端可能需要暂时禁止纠错,因为客户端从应用服务器检索数据是分阶段进行的,在所有的数据检索完毕之前,有些纠错规则很可能会报错。
要暂时禁止纠错,可以调用DisableConstraints,要重新允许纠错,可以调用EnableConstraints函数。DisableConstraints和EnableConstraints实际上都是作用于一个内部的计数。
11.2 索 引
  使用索引有这么几个好处:
.在数据集中定位记录比较快。
.能够在两个数据集之间建立Lookup或Master/Detail关系。
.可以对记录排序。
  在多层体系结构中,当客户程序从应用服务器检索数据时,它同时获得了默认的索引。默认的索引叫DEFAULT_ORDER,可以使用这个索引排序,但不能修改或删除这个索引。
  除了默认的索引外,TClientDataSet还对日志中记载的记录自动建立了一个副索引叫CHANGEINDEX。与DEFAULT_ORDER一样,不能修改或删除这个副索引。
  另外,还可以使用数据集中已建立的其他索引,或者自己建立索引。
11.2.1 创建一个新的索引
  要创建一个新的索引,可以调用AddIndex。AddIndex需要传递若干个参数:
  一是Name参数,用于指定索引名。在运行期切换索引时需要用到索引的名称。
  二是Fields参数,它是一个字符串,用于指定索引中的字段名,彼此之间用分号隔开。
  三是Options参数,用于设置索引的选项,包含ixDescending元素表示按降序排列,包含ixCaseInsensitive元素表示大小写不敏感。
  四是DescFields参数,它也是一个字符串,用于指定若干个字段名,这些字段将按照降序排列。
  五是CaseInsFields参数,它的作用与DescFields参数类似,包含在CaseInsFields参数中的字段将对大小写不敏感。
  六是GroupingLevel参数,用于指定分组级别,其值不能超过索引中的字段数。
  下面的代码创建了一个索引:
If Edit1.Text <> '' and ClientDataSet1.Fields.FindField(Edit1.Text) then
Begin
ClientDataSet1.AddIndex(Edit1.Text+'Index',Edit1.Text,  
  [ixCaseInsensitive],'','',0);
ClientDataSet1.IndexName := Edit1.Text + 'Index';
End;
为了避免创建一个索引,可以临时用IndexFieldNames属性来指定若干个字段,让数据集按这些字段排序。
11.2.2 删除和切换索引
  要删除一个先前创建的索引,可以调用DeleteIndex并指定要删除的索引名称。注意:DEFAULT_ORDER和CHANGEINDEX不能删除。
  如果建立了多个索引,可以任意选择其中的一个索引,这就要用到IndexName属性。
11.2.3 用索引把数据分组
  选择了一个索引后,数据集将自动按其中的字段进行排序。这样,临近的记录往往在关键字段上含有相同的值。例如,假设有一个表是这样的:
SalesRep Customer OrderNo Amount
1      1     5    100
1      1     2    50
1      2     3    200
1       2     6    75
2      1     1    10
2      3     4    200
  可以看出,SalesRep字段的值有重复的。对于SalesRep字段的值为1的来说,Customer字段的值也有重复的。这就是说,可以按SalesRep字段分组,进而再按Customer字段分组。显然,这里的分组级别是不同的,按SalesRep字段建立的分组属于第一级,按Customer字段建立的分组属于第二级。实际上,分组级别取决于字段在索引中的顺序。
  TClientDataSet可以决定是否按照分组级别来显示记录的值。例如,也许想以下面这种形式显示数据:
SalesRep Customer OrderNo Amount
1      1    5    100
           2    50
       2    3    200
           6    75
2      1    1    10
2      3    4    200
  要判断当前记录某一级的什么位置,可以调用GetGroupState函数。GetGroupState函数需要传递一个参数,用于指定分组级别。
11.3 计 算 字 段
  与其他数据集一样,也可以在TClientDataSet建立的数据集中增加计算字段。计算字段的值是基于同一个记录中的其他字段计算出来的。
  在其他数据集中,只要用户修改了数据或当前记录发生改变,就会触发OnCalcFields事件,换句话说,计算字段的值就被计算一次。
  TClientDataSet引入了“内部计算字段”的概念。与一般的计算字段不同的是,内部计算字段的值将随其他字段的值一起存取,这样,只有当用户修改了数据才会触发OnCalcFields事件,如果仅仅改变了当前记录,不会触发OnCalcFields事件。也就是说,内部计算字段的值需要重新计算的机会大大减少。
  在处理OnCalcFields事件的句柄中,首先要判断State属性。如果State属性返回dsInternalCalc,此时需要计算内部计算字段的值。如果State属性返回dsCalcFields,此时需要计算一般的计算字段的值。
11.4 统 计 值
  TClientDataSet增加了统计的功能,它可以基于分组自动计算总和、平均、计数、最大、最小值。当用户编辑数据时,这些统计值会自动跟着变化。
11.4.1 指定统计方式
  要指定怎样进行统计,就要用到Aggregates属性。这个属性是一个TAggregates对象,它用于管理一组TAggregate对象。
  在设计期,可以单击Aggregates属性边上的省略号按钮打开如图11.1所示
的编辑器。
  图11.1 管理一组TAggregate对象
  单击按钮可以增加一个TAggregate对象,单击按钮可以删减一个TAggregate对象,单击按钮可以把TAggregate对象前移,单击按钮可以把TAggregate对象后移。
  可以用字段编辑器专门创建一个用于表达统计值的字段,该字段的类型必须是“Aggregate”。Delphi 4会自动创建一个TAggregate对象,并加到Aggregates属性中。选择一个TAggregate对象,Object Inpector将显示该对象的属性。
  其中,Expression属性用于指定统计表达式,例如:
Sum(Field1)
  也可以是比较复杂的表达式:
Sum(Qty * Price) - Sum(AmountPaid)
  在表达式中,可以使用下列统计运算符:
.Sum计算一组数据的总和。
.Avg计算一组数据的平均值。
.Count计算一组数据中的非空值的个数。
.Min计算一组数据的最小值。
.Max计算一组数据的最大值。
  除了上述几个统计运算符外,还可以使用过滤条件中所能使用的运算符,但不能嵌套。在一个表达式中,可以混合出现几个统计值或常量,但不能混合出现统计值和字段。
  Sum(Qty * Price){合法}
  Max(Field1) - Max(Field2){合法}
  Avg(DiscountRate) * 100{合法}
  Min(Sum(Field1)){非法,不能嵌套}
  Count(Field1) - Field2{非法,统计值和字段不能混合出现在一个表达式中}
11.4.2 指定分组
  默认情况下,统计值是基于数据集中所有的记录计算出来的。不过,也可以针对一部分记录计算统计值,这就需要事先建立分组。
  前面在介绍索引时已经提到分组的概念。可以通过IndexName属性和GroupingLevel属性来选择使用哪个索引以及最大的分组级别。
  例如,假设有一个表是这样的:
SalesRep Customer OrderNo Amount
1      1     5    100
1      1     2    50
1      2     3    200
1       2     6    75
2      1     1    10
2      3     4    200
  如果要按SalesRep字段分组,并且指定其中的第一级,程序代码应当这样写:
Agg.Expression := 'Sum(Amount)';
Agg.IndexName := 'SalesCust';
Agg.GroupingLevel := 1;
Agg.AggregateName := 'Total for Rep';
11.4.3 怎样获取统计值
  要获取统计值,可以调用TAggregate对象的Value函数。如果统计值是基于数据集中所有的记录计算出来的,随时可以调用Value函数。如果统计值是基于分组计算出来的,必须保证当前记录正好位于该分组内。因此,在调用Value之前,最好先调用GetGroupState函数看看当前记录是否位于该分组内。
  要在数据控件中显示统计值,必须事先在字段编辑器中创建一个永久字段对象,该字段的类型必须是Aggregate。
11.5 数 据 包
  通过Data属性可以访问客户程序从应用服务器检索到的数据。程序示例如下:
Procedure TForm1.Button1Click(Sender: TObject);
Begin
ClientDataSet1.Data := ClientDataSet1.Provider.DataRequest(FilterEdit.Text);
End;
11.5.1 直接对Data属性赋值
  前面讲过,客户程序既可以通过IProvider接口获取数据,也可以从另一个数据集获取数据,后者就是通过Data属性赋值的。程序示例如下:
  ClientDataSet1.Data := ClientDataSet2.Data;
  一旦Data被赋值,就可以用标准的数据控件显示这些数据。
  注意:当从另一个数据集获取数据时,另一个数据集的日志也将被复制过来,但不包括原来的范围和过滤条件。
  如果要从另一个基于BDE的数据集中获取数据,可以通过数据集构件的Provider属性,程序示例如下:
  ClientDataSet1.Data := Table1.Provider.Data;
  如果要从一个自定义的数据集获取数据,首先要创建一个临时的TProvider构件,然后设置其DataSet属性指定这个自定义的数据集。程序示例如下:
TempProvider := TDataSetProvider.Create(Form1);
TempProvider.DataSet := SourceDataSet;
ClientDataSet1.Data := TempProvider.Data;
TempProvider.Free;
11.5.2 在数据包中加入自定义的信息
  可以把自定义的信息加到数据包中。当把数据保存到文件或流中时,这些自定义的信息也将保存到文件或流中。如果把数据包直接赋值给另一个数据集的话,这些自定义的信息也将被复制。
  要把自定义的信息加到数据包中,可以调用SetOptionalParam函数。要从数据包中检索自定义的信息,可以调用GetOptionalParam。程序示例如下:
Procedure TAppServer.Provider1UpdateData(Sender: TObject; DataSet: TClientDataSet);
var
WhenProvided: TDateTime;
Begin
WhenProvided := DataSet.GetOptionalParam('TimeProvided');
...
End;
11.5.3 克隆另一个数据集
  调用TClientDataSet的CloneCursor函数可以获得一个数据集的完全相同的副本。它与直接通过Data属性赋值是有区别的。
  区别之一:数据在两个数据集之间是共享的,修改其中一个将同时修改另一个。
  区别之二:除了数据外,CloneCursor函数还复制了一些属性和事件,这取决于Reset和KeepSettings参数怎样设置。
   CloneCursor函数需要传递三个参数,其中,Source参数指定源数据集,Reset参数和KeepSettings参数用于设置除了数据外是否还要复制下列属性和事件:Filter、Filtered、FilterOptions、OnFilterRecord、IndexName、MasterSource、MasterFields、ReadOnly、RemoteServer、ProviderName、Provider。
  如果Reset和KeepSettings参数都设为False,源数据集的上述属性和事件都将被复制给目标数据集。如果Reset参数设为True,目标数据集的上述属性和事件都将被清空。如果Reset参数设为False,而KeepSettings参数设为True,目标数据集的上述属性和事件不变,不过,必须保证这些属性和事件与克隆后的数据相容。
11.6 与应用服务器通讯
  在多层体系结构中,客户程序通过IProvider接口与应用服务器交换数据。这一章介绍怎样在客户端获得IProvider接口、怎样向应用服务器传递参数、怎样向应用服务器请求数据、怎样把用户对数据的修改写到数据库中。
11.6.1 怎样在客户端获得IProvider接口
  在单层应用程序以及工作在“公文包”模式下的多层应用程序中,不需要用到IProvider接口。而在多层体系结构中,客户程序要与应用服务器交换数据,首先必须获得IProvider接口,这就要用到RemoteServer属性和ProviderName属性。
  RemoteServer属性用于指定客户端的MIDAS连接构件。MIDAS连接构件又称Data Broker,用于建立和维护与应用服务器的连接。
  在设计期,正确设置了RemoteServer属性后,就可以在对象观察器中为ProviderName属性选择一个值,实际上就是选择应用服务器上的一个TProvider构件。
11.6.2 向应用服务器传递参数
  客户程序可以向应用服务器传递参数,这些参数实际上是传递给应用服务器上的TQuery构件或TStoredProc构件。既可以在设计期也可以在运行期设置参数。
  在设计期,可以单击Params属性边上的省略号按钮,打开一个如图11.2所示的编辑器。
  图11.2 设置参数
  单击按钮可以增加一个参数,单击按钮可以删减一个参数,单击按钮可以把一个参数前移,单击按钮可以把一个参数后移。
  选择一个参数,对象观察器将显示该参数(TParam对象)的属性。
  在运行期可以调用TParams的CreateParam函数来创建一个参数。例如,下面的代码创建了一个参数叫CustNo,它的使用类型是ptInput,数据类型是ftInteger,它的值设为605。
With ClientDataSet1.Params.CreateParam(ftInteger, 'CustNo', ptInput) Do
AsInteger := 605;
  设置好参数以后,如果TClientDataset的Active属性是False,只要把Active属性设为True,这些参数将被自动传递给应用服务器。如果Active属性已经为True,就要调用SendParams函数把参数传递给应用服务器。
  注意:传递给应用服务器的参数必须与TQuery构件或TStoredProc构件的参数匹配,包括名称、数据类型和参数类型。
11.6.3 怎样向应用服务器请求数据
  TClientDataSet提供了两个属性和三个方法,用于怎样向应用服务器请求数据:
  一是FetchOnDemand属性。如果这个属性设为True,TClientDataSet会根据需要自动检索附加的数据包,例如BLOB字段的值或者嵌套表的内容。如果这个属性设为False,程序需要显式地调用GetNextPacket才能获得这些附加的数据包。
  二是PacketRecords属性,用于设置一个数据包中最多可容纳的记录数,设为-1表示一个数据包可以容纳数据集的所有记录。
  三是GetNextPacket函数,用于向应用服务器检索下一个数据包,并把检索到的数据包添加到前一次检索到的数据包的后面。这个函数返回实际检索到的记录数。
  四是FetchBlobs过程,用于从应用服务器检索BLOB字段的值。如果FetchOnDemand属性设为True,就没必要调用FetchBlobs函数。
  五是FetchDetails过程,用于检索嵌套表中的数据。如果FetchOnDemand属性设为True,就没必要调用FetchDetails函数。
11.6.4 更新数据库
  在多层体系结构中,用户在客户端修改了数据后,需要把最新的数据写到数据库中,这就要调用TClientDataSet的ApplyUpdates函数。
  ApplyUpdates只需要传递一个参数叫MaxErrors,用于指定一个整数,当遇到无法更新的记录超过这个数时,此次更新就中止。如果MaxErrors参数设为0,表示只要遇到一个错误更新就中止,客户端的日志保持不变。如果MaxErrors参数设为-1,当应用服务器发现有错误的记录,就尝试更新下一个记录,等所有的记录都尝试过以后才返回。
  ApplyUpdates会自动调用Reconcile函数,进而调用应用服务器上的TProvider构件的ApplyUpdates函数去更新远程的数据库服务器。没有被DBMS服务器认可的记录通过Reconcile返回给客户端,此时将在客户端触发OnReconcileError事件让您更正错误。最后,ApplyUpdates函数返回仍然没有被认可的记录数。
11.7 在文件中存取数据
  要从文件中读取数据,可以调用LoadFromFile函数。LoadFromFile函数需要传递一个参数,用于指定文件名。文件名应包含完整的路径。如果客户程序总是从一个固定的文件中读取数据,可以设置FileName属性指定一个文件名,以后,当TClientDataSet引入的数据集打开时,就自动从这个文件中读取数据,不需要调用LoadFromFile。
  要从流中读取数据,可以调用LoadFromStream。LoadFromStream需要传递一个参数,用于指定一个流对象。
  注意:LoadFromFile(LoadFromStream)只能从先前用SaveToFile(SaveToStream)保存的文件中读取数据。
  要把数据保存到文件中,可以调用SaveToFile函数。SaveToFile需要传递一个参数,用于指定文件名。如果指定的文件已存在,文件中的数据将被覆盖。如果客户程序总是把数据保存到一个固定的文件中,可以设置FileName属性指定一个文件名,当TClientDataSet引入的数据集关闭时,就自动把数据保存到这个文件中,不需要调用SaveToFile。
  要把数据保存到流中,可以调用SaveToStream。SaveToStream需要传递一个参数,指定一个流对象。
  注意:当把数据保存到文件或流中时,日志中记载的修改仍然保留。这样,当下次调用LoadFromFile或LoadFromStream读取数据时,仍然可以恢复原来的数据。
posted @ 2007-03-29 05:57 konhon 优华 阅读(1613) | 评论 (0)编辑 收藏

  近日(呵呵,这篇文章是去年写的)用了两个月开发了一个物流信息系统,这个系统是两层、三层相结合,C/SB/S相结合的系统。虽然限于时间的紧张和人手的原因,系统规模不是很大,但是其中涉及的技术却很全面。在这个《开发技术篇》中我们将讲解我在开发系统中遇到的技术问题及解决方案,希望对大家有帮助。对于物流信息系统的分析设计问题,我将在另一篇文章《物流信息系统开发手记――系统构架篇》中讲解。

 

一、Midas的安全问题。

    Midas技术是Delphi中进行三层开发的首选技术,它不仅有纯DCOM/COM+(COM+技术是.NET技术的基础)的优点,而且也结合了Delphi的快速开发特性,可以快速开发出想要的系统,其开发速度是用VC,PB等开发DCOM的数十倍,把程序员从烦杂的代码中解脱出来,从而将更多的精力投入到业务逻辑的设计中去。

    但是Midas技术的一个最令人担忧的就是它的安全问题:

远端只要知道应用服务器的端口号即可访问到应用服务器,而一旦访问到应用服务器,TClientDataSet即可获得ProviderNames列表。一旦知道了ProviderNames列表,这就相当于将数据库暴露在外了。

关于可轻易获得ProviderNames列表的问题,我使用下面的方法解决:

 在服务器端定义一个

LoginMTS(const AUserId, APassword: WideString): WordBool;

方法。初始状态下,所有的DataSetProvider和数据集的连接断开。用户必须调用LoginMTS并传递用户名和密码,登陆成功才将DataSetProvider和数据集的连接打开。这样如果用户验证没有通过,即使它获得了ProviderNames列表也没法调用接口中的方法对数据库进行操作。

二、Midas中主从表的实现

主从表的应用在信息系统中应用很广。在两层开发中我们可以通过直接建立两个数据集之间为主从关系来实现主从表;在三层中虽然我们仍然可以通过直接建立两个数据集之间为主从关系来实现主从表,但是这样就要求把数据库中所有相关的数据行都下载到本地,丧失了三层开发的优势。我在实际中使用下面的方法实现。这里我以实现入库单查询、添加、修改、删除(CRUD)为例来讲解:

1)新建一个MTS Data Module,命名为TmtsStockInListBiz,增加如下方法:

    function QueryStockInListMasterById(const AId: WideString;

      var ADatas: OleVariant): WordBool; safecall;

    function QueryStockInListSlaveByMasterId(const AId: WideString;

      var ADatas: OleVariant): WordBool; safecall;

    procedure UpdataStockInListMaster(var ADatas: OleVariant); safecall;

    procedure UpdataStockInListSlave(var ADatas: OleVariant); safecall;

    function GenerateStockInListId: WideString; safecall;

 

QueryStockInListMasterById作用是根据入库单单号查询入库单的基本信息(入库日期、负责人等),Aid为入库单单号,Adatas为返回值,其格式就是Midas的数据包,可以将其附给ClientDatSetData属性。

QueryStockInListSlaveByMasterId作用是根据入库单单号查询入库单的详细信息(商品条码,数量)

UpdataStockInListMaster是对入库单主表进行删除、添加、修改操作。只要将ClientDataSetDelta属性做为传递即可。

UpdataStockInListSlave是对入库单从表进行删除、添加、修改操作。

GenerateStockInListId是产生一个唯一的入库单号。

下面是几个方法的代码,都很简单,就不多解释了,可以查看Delphi的帮助。

function TmtsStockInListBiz.QueryStockInListMasterById(

  const AId: WideString; var ADatas: OleVariant): WordBool;

begin

  result := false;

  ADatas := null;

  try

    cdsQuery.Close;

    cdsQuery.CommandText := 'select * from t_StockInListMaster where Id=:Id';

    cdsQuery.Params.ParamByName('Id').AsString := AId;

    cdsQuery.Open;

    if cdsQuery.RecordCount > 0 then

    begin

      result := true;

      ADatas := cdsQuery.Data;

    end;

  finally

    cdsQuery.Close;

  end;

end;

 

procedure TmtsStockInListBiz.UpdataStockInListMaster(

  var ADatas: OleVariant);

var

  eCount: Integer;

  OwnerData: OleVariant;

begin

  DCOMConStockInList.GetServer.AS_ApplyUpdates('dspStockInListMaster',

    ADatas, -1, eCount, OwnerData);

end;

 

function TmtsStockInListBiz.GenerateStockInListId: WideString;

var

  LPrior: string;

  i: Integer;

begin

  cdsQuery.Close;

  cdsQuery.CommandText := 'select top 1 id from t_StockInListMaster order by id desc';

  cdsQuery.Open;

  LPrior := cdsQuery.FieldByName('Id').AsString;

  i := StrToIntDef(RightStr(LPrior,8),0);

  Inc(i);

  result := 'RK' + FormatFloat('00000000',i);

  cdsQuery.Close;

end;

 

2)、新建一个应用程序,通过DCOMConnectionSocketConnection等连接到MTS组件,然后就可以调用MTS的相应的方法实现客户端功能了。

放入cdsStockInListMastercdsStockInListSlave两个ClientDataSet控件,在控件上点击右键,选择“FieldsEditor”新建于服务器中的字段同样的字段,然后再次在控件上单击右键,选择“CreateDataSet”,建立一个本地数据库。

3

根据入库单号查询入库单的方法实现:

procedure TFormStockInList.BtnFindClick(Sender: TObject);

var

  v,vs: OleVariant;

begin

  if SocketConStockInList.AppServer.QueryStockInListMasterById(Trim(LEdtId.Text), v) then

  begin

    cdsStockInListMaster.Data := v;//显示入库单主表(主要信息)

 

    if SocketConStockInList.AppServer.QueryStockInListSlaveByMasterId(Trim(LEdtId.Text), vs) then

      cdsStockInListSlave.Data := vs; ;//显示入库单从表(明细信息)

  end

  else

    ShowMessage('此单不存在!');

end;

4)新建入库单的实现

procedure TFormStockInList.BtnNewClick(Sender: TObject);

var

  LId: string;

begin

  ClearCDSRecord;

  cdsStockInListMaster.Open;

  cdsStockInListMaster.Insert;

  LId := SocketConStockInList.AppServer.GenerateStockInListId;

  LEdtId.Text := LId;

  cdsStockInListMaster.FieldByName('Id').AsString := LId;

  cdsStockInListMaster.FieldByName('GenerateDate').AsDateTime := Now();

end;

5)提交功能的实现

procedure TFormStockInList.BtnPostClick(Sender: TObject);

var

  LQuerymts: ImtsQueryObjDisp;

  LBar: string;

begin

  SetSocketConnectionConnect(SocketConQuery);

  LQuerymts := ImtsQueryObjDisp(SocketConQuery.GetServer);

 

  SocketConQuery.Close;

 

  if cdsStockInListMaster.RecordCount > 0 then

    SocketConStockInList.AppServer.UpdataStockInListMaster(cdsStockInListMaster.Delta);

  if cdsStockInListSlave.RecordCount > 0 then

  SocketConStockInList.AppServer.UpdataStockInListSlave(cdsStockInListSlave.Delta);

end;

注:本文中ClientDataSet控件的名称开头一般为cdsTsocketConnection控件的名称开头一般为SocketCon

三、动态设置TsimpleObjectBroker的服务器列表

procedure SetSocketConnectionConnect(AValue: TSocketConnection);

  procedure FillAppServerList(ABroker: TSimpleObjectBroker);

  var

    sl: TStringList;

    i, n: Integer;

  begin

    sl := TStringList.Create;

    从配置文件中读取服务器列表,并保存到sl;

    n := sl.Count - 1;

    ABroker.ServerData := null;

    for i := 0 to n do

    begin

      ABroker.Servers.Add;

      ABroker.Servers[i].ComputerName := sl.Strings[i]

    end;

    sl.Free;

  end;

var

  LBroker: TSimpleObjectBroker;

begin

  LBroker := TSimpleObjectBroker.Create(nil);

    FillAppServerList(LBroker);

    AValue.ObjectBroker := LBroker;

    try

      AValue.Connected := true;

    except

      raise Exception.Create('应用服务器连接错误!');

    end;

    LBroker.Free;

end;

posted @ 2007-03-28 05:07 konhon 优华 阅读(2543) | 评论 (0)编辑 收藏


  因为需要处理大量的音、视频数据,所以一套强有力的计算机系统及外围硬件配置是必要的。另外,由于我们要直接输出的是高质量的MPG文件(而非体积庞大的AVI文件),因此,强烈建议使用视频压缩卡进行硬件高速压缩,本文以较常见的MPEGator 3.1压缩卡为例,介绍通过Premiere利用压缩卡直接输出MPG的办法:

  1、制作VCD电子相册应使用怎样的系统配置?

  答:推荐的系统配置如下---

  CPU: PIII 500E以上

  内存: 256M SDRAM或更大

  硬盘: 20G高速

  压缩卡:MPEGator 3.1 刻录机:所有能被Video Pack 4.0所支持的型号

  操作系统:Windows 95 OSR2或Windows 98 制作软件:Adobe Premiere 5.0/4.2、Video Pack 4.0

  辅助软件:Paint shop Pro 6.0、ACDsee3.0、Audio Grabber 1.61 以上推荐的系统配置中,我们主要是采用了著名的视频编辑软件Adobe Premiere 5.0与性能价格比较高的MPEGator 3.0压缩卡配合制作VCD相册。为什么要采用这样的配合呢?还是先让我们来了解一下它们各自的特性吧:

  2、为什么推荐使用Premiere 5.0数字影视编辑软件?

  答:Adobe Premiere 5.0是图形图像软件巨人---Adobe公司继广受称赞的Premiere 4.2后,推出的新一代桌面数字影视编辑系统。它具有比Premiere 4.2更为强大的图像编辑能力,通过把动画、位图、数字音频文件按照需要进行剪辑、组合,让你可以设计出近乎随心所欲的影视特效;在专为编辑影像片段而设计的Timeline里,利用鼠标就可完成所有的编辑操作;它的即时播放功能还使我们在发生编辑错误时,能够轻松地进行修改,以节省时间与精力。更重要的是,Premiere 5.0可以提供高品质影像输出质量!这一点着实令人心动。此外,它还支持制作输出有声有色的VCD电子相册呢。

  3、为什么推荐使用MPEGator压缩卡?

  答:压缩卡方面,韩国DARIM公司生产的MPEGator压缩卡(图02)可谓是具有较高性能价格比的视频压缩卡:不仅带有S-video、复合、及立体声音频输入接口,可以通过摄像机、录像机、电视及激光唱机等信号源将视频信号实时压缩成全I.P.B帧的标准MPEG-1格式;还支持实时预览。最为重要的是MPEGator卡可以使流行的视频编辑软件通过硬件加速转换成为MPEG-1格式。  MPEGator可与Windows的其它应用程序配合产 PEG文件,3D Studio, Adobe Premiere等各种兼容Video for Window的流行的编辑软件都可以在生成动画或AVI之前,直接选用其压缩驱动程序,通过硬件加速压缩生成为MPEG文件,有效地避免了因AVI—→MPG而导致的画质下降这一难题。另外,MPEGator还具有PAL/NTSC制式兼容,PCI32位总线等特点。通过以上介绍,可以看出:利用这样的软硬件组合,我们将能够制作出效果比较令人满意的多媒体VCD相册。下面,就让我们开始实战VCD相册的制作吧。

  4.制作VCD相册之前,为什么要拟定提纲?

  答:制作VCD相册之前,首先应对所制作的作品的各个方面做到心中有数。最好列个制作提纲,列出主题及各相对独立片段的次序,以及每个片段内的照片、音乐、旁白;将每张照片分别编上序号,并将其序号与欲添加的说明文字对应起来。这样,在实际制作的时候就条理清晰、步骤分明,不易出现错漏等现象。

  5.怎样进行照片的扫描?

  答:选取一张浅色纹理纸,将其裁剪成宽155mm、高127mm(即352:288)大小的矩形模板。用双面胶将照片粘于其上(注意居中,并使底边高度略大于顶边),放入扫描仪进行扫描,并以位图(.bmp)形式存盘。此举好处之一是使扫出的照片具有一个漂亮的彩色相框;另一个好处是可以将添加的说明文字放在相框上,而不是照片内,不会破坏照片的美感(图07)。 


  6. 如何为照片添加说明文字?

  进入Paint Shop Pro 5.0,选择File——open打开一幅已扫好的照片,点击左侧工具栏中的“A”键,接着将鼠标移至照片内并单击左键,在弹出的“Add Text”文字输入框中输入想要的文字,再选定字体、字号及颜色,确定文字位置后点击“OK”,并将编辑后的照片以原文件名存盘。

 7.如何截取音轨?

  答:启动音轨截取软件AudioGrabber,将音乐CD放入光驱,勾选想要截取的CD音轨。

  点击控制栏中的“Settings”按键后勾选其中的“ASPI”项,其它选项用默认值即可。然后,进入“Norm”选项,选择“Use normalizing”后点击“OK”退出。现在,先用鼠标点击一下工具栏里那标有伸开手掌的“截取”(Grab)钮,就可以开始抓取指定的音轨了。如果截获的.WAV文件不能被正常读取或者.WAV文件中有令人讨厌的“爆音”(pops,这是令许多朋友头痛的问题)。请在“开始—设置—控制面板—系统—设备管理—CDROM—属性—设置—选项”中将“同步数据传送”选中,然后按“确定”退出。再重新去试截取刚才有问题的音轨,您可能会发现,问题已经迎刃而解了。其次,若觉得光驱读取音乐数据的速度很缓慢,请先检查光驱上数据线是否直接接到了主板的IDE接口上。

  为了方便管理和使用,可新建Photo、Audio、Wav三个子目录,分别用于存放照片、音乐及旁白。

  准备工作完成后,就可以开始着手制作了。为了工作得更有条理,我们一般先对每一个相对独立的片段进行编辑制作,最后再把它们连成整体。

  8.如何设置才能让Premier5.0利用MPEGator来进行高效硬件压缩?

  答:首先启动Premiere5.0。在弹出的New Project setting窗口,选择General settings,在“编辑模式”里选“Video for window”,其下的两个选项均选“25”(因为我们要制作的是PAL制式的VCD);然后进Video settings,在“压缩器(Compressor)”这一项中选“DVMPEG Video (hardware)”(图08),即选用硬件压缩;分辨率设为352*288,帧率设为25;在Audio settings里,关键是在“Type(类型)”中选择“DVMPEG Audio”---指定用硬件进行音频压缩;采样频率设为44.1KHZ,格式设为16bit--Stereo。


  9.Premiere 5.0的主界面的结构如何?

  答:进入Premiere 5.0的主界面,我们会发现设计新颖的时间轴窗口(Timeline)已取代了其4.2版本中的Constraction窗口。作为Premiere 5.0的核心,Timeline的用户窗口分为两个部份:视频部分和音频部分;其中视频部份又分为两个主视频剪辑轨道——Video 2和Video 1。其中,标成黑体字的Video 1部份又内含两个视频剪辑轨道:Video 1A和Video 1B,它们之间的Transition轨道是视频切换效果轨道。Premiere 5.0提供了非常丰富的视频切换效果,包括滑动、擦除、融合、剥落、分割、爆破、渐变、突变、三维旋转、翻页、推动等多种划变效果,令人目不暇接。音频轨道Audio 1、2、3则用于放置背景音乐、配音、旁白等声音信号。  

  10.如何导入图片及配音文件?

  答:点击“File--Import--Folder”,把刚才准备好的图片文件及背景音乐文件输入进来,在Project窗口下双击刚才输入的文件夹,可以看到文件夹中的各文件的具体信息,双击该文件则可对其进行预览。  

  11.如何设置照片的参数?

  答:将第一幅照片拖到视频剪辑轨道Video 1A中,对其点击鼠标右键,在弹出的菜单中的Duration项里设定照片的播放时间;并在菜单中的Video项里将Maintain Aspect Retio选中(图09)——请注意,这是非常重要的一步,此项设置将使最后输出的照片保持原本的长宽比例,而不致于使照片上的人像、景物发生变形。按上述方法将照片依次拖入Video1的A、B栏,(注意每幅照片的尾与下一幅照片首应留有重叠部份,重叠部份的时间长短依实际需要而定。把背景音乐、旁白拖入Audio栏)。如果是旁白,应特别注意旁白的时间长度应与相应照片的播放时间相当。然后,用鼠标把Timeline窗户上方的浅蓝色横杠拉至最后一张照片的末端,以设定Premiere将进行处理的视频片段的长度。


  12. 如何设定视频切换特效?

  答:Premiere 5.0用户界面上,有一个Transition/Command命令窗口,其中共有75种切换特效可供选择。而且,其中大多数切换特效都可按自己的需要进行设置。将选中的特效用鼠标拖到Video 1窗口的Transition中两幅照片相重叠的部分,其长度应为两幅照片相重叠的长度。如果能根据每幅照片的特点恰当地选择Transitions,将使您的VCD相册在播放时赢得更多的赞叹之声。  

  13. 如何处理背景音乐?

  答:为VCD相册添加优美的背景音乐,也将使你的VCD相册平添许多迷人的魅力。然而,因为背景音乐是我们采用抓音轨的方式获得的,这样就不可避免地在VCD相册一开始播放时就出现突兀而至的音乐声。此外,由于音轨的长度与照片播放时间的长度往往难以很好的吻合。这样,就可能出现一段VCD相册的播放马上就要结束,而音乐声却正处于高潮……。理想的状况应该是:照片播放时,背景音乐逐渐轻柔地响起;照片播放即将结束时,音乐声又渐渐随之淡去。换句话说,也就是要实现背景音乐的淡入淡出。在Premiere 5.0中,按前述方法装入照片及背景音乐后,单击第一音频轨道(Audio 1)上的那个指向右的小三角,这时会弹出该音轨的波形显示框。可以看到该框中部有一条左右各有一方形端点的红线——这就是控制音乐淡入淡出的关键。将鼠标移至红线上,鼠标指针将变为手形(图10)。此时,按住鼠标左键即可通过增加和拖动点子来随意确定该红线条形状。从起始位置开始,将线条置于从左至右渐高状态时,即可实现背景音乐的淡入效果。用类似的方法还可制造音乐的淡出效果,在此不再赘述。有兴趣的朋友尽可自行尝试。 

  14. 如何设置输出参数?

  答:输出参数包含General、Audio、Video三项,只有正确的设备了输出参数,VCD相册才能在VCD机上顺利播放。首先选Project--Setting进入General设置:在“Editing Mode”里选“Video for window”,“Time display”及“Timebase”均选“25”;再进入Video设置,“Compressor”选“DVMPEG Video(Hardware)”,尺寸设为352*288,帧率设为25;最后设置Audio参数:采样频率设为44.1KHZ,格式设为16bit--Stereo(图11);Type设成“DVMPEG Audio”。在这里,音频及视频均设置为硬件(MPEGator卡)加速压缩。 


  15. 如何直接输出MPG文件?

  答:上述参数设置完毕后,接着选File-Export-movie,将刚才组合好的照片,音乐,旁白等素材,通过MPEGator卡的硬件加速直接生成高质量的MPG文件。我们知道,如果利用Xing MPEG Encoder等进行纯软件压缩,即使你的系统配置较高,其压缩时间也将漫长得惊人:约为真实播放时间的10-20倍;而利用MPEGator卡进行硬件压缩的话,可就太节省时间了:压缩时间仅为真实播放时间的3倍左右,大大提高了制作效率。需要注意的是,为了便于分类,应把同一主题的内容输出生成一个独立的MPG文件。

  16. 如何把电子相册刻成VCD碟?

  前面所有辛苦的劳动,都将通过刻制成VCD碟片而变成劳动的果实。说到2.0 VCD刻录,CeQuadrat出品的Video Pack 4.0因其较细致而周到的功能设计,是一个在业余条件下不错的选择,其缺点是操作相对比较繁琐。启动Video Pack 4.0,将刚才压缩好的各个MPG文件依次用鼠标拖至位于主界面下方的工作框内,右键单击MPG文件,在弹出的菜单选中Properties,在Playlist node中的第二个选项(即每一片段的等待时间)里把等待时间设为0秒,按“确定”退出。每个MPG文件都必须重复上述步骤,这样可以使各独立主题内容切换时不至于出现停顿等待状态。这时你也许发现了,刚才拖入工作框的各MPG文件方形图标上下均有一个三角形缺口。原来,在Video Pack 4.0中是用连线的方式来表示各种播放次序的,而这种三角形缺口,就是这些连线的出入口。将鼠标指针移至MPG文件图标下方的缺口,图标里会出现黄颜色的“Next”字样,此时即可开始进行连线,方法是使各MPG文件按顺序首尾相连(图12)。上述步骤完成后,点击工具栏中的红色的“Make CD”钮即可开始刻录VCD光碟了。

  17. 把电子相册刻成VCD碟的过程中要注意哪些事项?

  要是现在问你:自己刻制一张VCD相册光盘困难吗?你肯定会说:一点都不难!

  那么,是不是就像在硬盘上拷贝文件那么轻而易举呢?决非如此简单。VCD相册刻录前进行些必要的准备工作,将能大大提高我们的刻录成功率:  

  a.刻录前为什么要整理硬盘?

  答:我们通常都是以硬盘作为信息源盘来进行刻录的。由于未经整理的磁盘文件大多呈零散状态分布。这样,硬盘在读取数据时,读写头就只能在零散的文件之间来回奔忙,极有可能造成不必要的延误而导致刻出废盘。所以,每次刻录前对硬盘的每一个逻辑分区进行磁盘扫描和碎片整理。进行硬盘整理是成功刻录的第一步。  

  b.为什么刻录过程中要避免执行任何无关程序?

  答:这些程序不仅包括屏幕保护程序和内存驻留程序(比如某些杀毒程序),这些程序都有可能在数据流从硬盘转移到刻录机中光盘上的紧要关头,来与刻录软件争夺有限的系统资源,从而影响数据流的正常传输而引发缓存器欠载等问题,导致刻盘失败。

  c.为什么使用质量可靠的CD-R盘片?

  答:CD-R空白盘片按反射层材料的不同可分绿盘、金盘、蓝盘三大类。这三种盘片并没有明显的优劣差别。从特性上来说,绿盘具有较好的兼容性,另外价格很便宜;蓝盘在写入和读取数据时有较高的准确性;金盘有较好的抗光性。关键的是,分别试刻不同类别和品牌的盘片,从中找出最适合你的刻录机的那几种。  

  至此,两种不同类型的电子相册的制作方法就介绍完毕,预祝你早日制作出自己的精美电子相册!

posted @ 2007-03-27 19:11 konhon 优华 阅读(1563) | 评论 (0)编辑 收藏

首先,我们新建一个类,存放天气信息

/*
 * Created on 2005-3-8
 *
 * To change the template for this generated file go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 
*/
package  org.exoplatform.portlets.chinaweather.component;

/**
 * 
@author  Administrator
 *
 * To change the template for this generated type comment go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 
*/
public   class  Weather {
 
private  String city;
 
private  String state;
 
private  String temperature;
 
private  String time;
 
private  String wind;
 
private  String windpower;
 
private   long  UpdateTime;

 
/**
  * 
@return
  
*/
 
public  String getTemperature() {
  
return  temperature;
 }

 
/**
  * 
@return
  
*/
 
public  String getTime() {
  
return  time;
 }

 
/**
  * 
@return
  
*/
 
public  String getWind() {
  
return  wind;
 }

 
/**
  * 
@return
  
*/
 
public  String getWindpower() {
  
return  windpower;
 }

 
/**
  * 
@param  string
  
*/
 
public   void  setTemperature(String string) {
  temperature 
=  string;
 }

 
/**
  * 
@param  string
  
*/
 
public   void  setTime(String string) {
  time 
=  string;
 }

 
/**
  * 
@param  string
  
*/
 
public   void  setWind(String string) {
  wind 
=  string;
 }

 
/**
  * 
@param  string
  
*/
 
public   void  setWindpower(String string) {
  windpower 
=  string;
 }

 
/**
  * 
@return
  
*/
 
public   long  getUpdateTime() {
  
return  UpdateTime;
 }

 
/**
  * 
@param  l
  
*/
 
public   void  setUpdateTime( long  l) {
  UpdateTime 
=  l;
 }

 
/**
  * 
@return
  
*/
 
public  String getState() {
  
return  state;
 }

 
/**
  * 
@param  string
  
*/
 
public   void  setState(String string) {
  state 
=  string;
 }

 
/**
  * 
@return
  
*/
 
public  String getCity() {
  
return  city;
 }

 
/**
  * 
@param  string
  
*/
 
public   void  setCity(String string) {
  city 
=  string;
 }

}


具体的解析代码为:

private  Weather parserWeather()  throws  Exception {
  Weather w 
=   new  Weather();
  
try  {

   
// Parser parser =
   
//  new Parser("file: // localhost/I:/projects/query.html");
   Parser parser  =
    
new  Parser( " http://weather.news.sohu.com/query.php?city=镇江 " );
   
   parser.setEncoding(
" GBK " );
   Node nodes[] 
=  parser.extractAllNodesThatAre(TableTag. class );

   TableTag table 
=  (TableTag) nodes[ 3 ];
   
// temperature
   StringNode[] stringNodes  =  table.digupStringNode( " 镇江 " );
   StringNode name 
=  stringNodes[ 0 ];
   w.setCity(name.toPlainTextString());
   CompositeTag td 
=  (CompositeTag) name.getParent();
   CompositeTag tr 
=  (CompositeTag) td.getParent();
   
int  columnNo  =  tr.findPositionOf(td);
   TableColumn nextColumn 
=  (TableColumn) tr.childAt( 5 );
   Node expectedName 
=  nextColumn.childAt( 0 );
   Node expectedName2 
=  nextColumn.childAt( 2 );
   
// System.out.println(expectedName.getText());
   
// System.out.println(expectedName2.getText());
   w.setState(expectedName.getText());
   w.setTemperature(expectedName2.getText());
   
// time
   stringNodes  =  table.digupStringNode( " 时间 " );
   name 
=  stringNodes[ 0 ];
   
// System.out.println(name.toPlainTextString());

   String time 
=
    name
     .toPlainTextString()
     .substring(
4 , name.toPlainTextString().length())
     .trim();
   
// System.out.println(time);
   w.setTime(time);
   
// wind
   stringNodes  =  table.digupStringNode( " 风向 " );
   name 
=  stringNodes[ 0 ];
   
// System.out.println(name.toPlainTextString());

   String wind 
=
    name
     .toPlainTextString()
     .substring(
4 , name.toPlainTextString().length())
     .trim();
   
// System.out.println(wind);
   w.setWind(wind);
   
// wind power
   stringNodes  =  table.digupStringNode( " 风力 " );
   name 
=  stringNodes[ 0 ];
   
// System.out.println(name.toPlainTextString());

   String windpower 
=
    name
     .toPlainTextString()
     .substring(
4 , name.toPlainTextString().length())
     .trim();
   
// System.out.println(windpower);
   w.setWindpower(windpower);

   w.setUpdateTime(System.currentTimeMillis());

  } 
catch  (ParserException e) {

   e.printStackTrace();
  }
  
return  w;
 }


解析出来的代码必须做缓存处理,

private   static   long  TIME_TO_LIVE  =   1000   *   60   *   60   *   12 ;

 
private  Weather loadWeather()  throws  Exception {
  Weather weather 
=  weather  =  (Weather) cache_.get( " chinaweather " );
  
long  currentTime  =  System.currentTimeMillis();
  
if  (weather  !=   null
   
&&  currentTime  <  (weather.getUpdateTime()  +  TIME_TO_LIVE)) {
   cache_.remove(
" chinaweather " );
   weather 
=   null ;
  }

  
if  (weather  ==   null ) {
   
synchronized  (cache_) {
    weather 
=  parserWeather();
    cache_.put(
" chinaweather " , weather);
   }
  }

  
return  weather;
 }


posted @ 2007-03-08 06:56 konhon 优华 阅读(2203) | 评论 (0)编辑 收藏

 

import  org.htmlparser.Node;
import  org.htmlparser.NodeFilter;
import  org.htmlparser.Parser;
import  org.htmlparser.filters.TagNameFilter;
import  org.htmlparser.tags.TableTag;
import  org.htmlparser.util.NodeList;

/**
 * <br>
 * 标题: <br>
 * 功能概要: <br>
 * 版权: cityyouth.cn (c) 2005 <br>
 * 公司:上海城市青年网 <br>
 * 创建时间:2005-12-21 <br>
 * 修改时间: <br>
 * 修改原因:
 * 
 * 
@author  张伟
 * 
@version  1.0
 
*/
public   class  TestYahoo {
    
public   static   void  testHtml() {
        
try  {
            String sCurrentLine;
            String sTotalString;
            sCurrentLine 
=   "" ;
            sTotalString 
=   "" ;
            java.io.InputStream l_urlStream;
            java.net.URL l_url 
=   new  java.net.URL(
                    
" http://sports.sina.com.cn/iframe/nba/live/ " );
            java.net.HttpURLConnection l_connection 
=  (java.net.HttpURLConnection) l_url
                    .openConnection();
            l_connection.connect();
            l_urlStream 
=  l_connection.getInputStream();
            java.io.BufferedReader l_reader 
=   new  java.io.BufferedReader(
                    
new  java.io.InputStreamReader(l_urlStream));
            
while  ((sCurrentLine  =  l_reader.readLine())  !=   null ) {
                sTotalString 
+=  sCurrentLine;
            }
            System.out.println(sTotalString);

            System.out.println(
" ==================== " );
            String testText 
=  extractText(sTotalString);
            System.out.println(testText);
        } 
catch  (Exception e) {
            e.printStackTrace();
        }

    }

    
/**
     * 抽取纯文本信息
     * 
     * 
@param  inputHtml
     * 
@return
     
*/
    
public   static  String extractText(String inputHtml)  throws  Exception {
        StringBuffer text 
=   new  StringBuffer();

        Parser parser 
=  Parser.createParser( new  String(inputHtml.getBytes(),
                
" 8859_1 " ),  " 8859-1 " );
        
//  遍历所有的节点
        NodeList nodes  =  parser.extractAllNodesThatMatch( new  NodeFilter() {
            
public   boolean  accept(Node node) {
                
return   true ;
            }
        });
        Node node 
=  nodes.elementAt( 0 );
        text.append(
new  String(node.toPlainTextString().getBytes( " 8859_1 " )));
        
return  text.toString();
    }

    
/**
     * 读取文件的方式来分析内容. filePath也可以是一个Url.
     * 
     * 
@param  resource
     *            文件/Url
     
*/
    
public   static   void  test5(String resource)  throws  Exception {
        Parser myParser 
=   new  Parser(resource);

        
//  设置编码
        myParser.setEncoding( " GBK " );
        String filterStr 
=   " table " ;
        NodeFilter filter 
=   new  TagNameFilter(filterStr);
        NodeList nodeList 
=  myParser.extractAllNodesThatMatch(filter);
        TableTag tabletag 
=  (TableTag) nodeList.elementAt( 11 );
            
            System.out.println(tabletag.toHtml());
            
            System.out.println(
" ============== " );

    }

    
/*
     * public static void main(String[] args) { TestYahoo testYahoo = new
     * TestYahoo(); testYahoo.testHtml(); }
     
*/
    
public   static   void  main(String[] args)  throws  Exception {
        test5(
" http://sports.yahoo.com/nba/scoreboard " );
    }
}
posted @ 2007-03-08 06:50 konhon 优华 阅读(1578) | 评论 (0)编辑 收藏

     摘要: 最近搞一个扣网页内容的SessionBean,需要模拟客户端post提交,然后得到servlet返回的结果。采用Jakarta的HttpClient API解决之.HttpClient扩展和增强了标准java.net包,是一个内容广泛的代码库,功能极其丰富,能够构造出各种使用HTTP协议的分布式应用,或者也可以嵌入到现有应用,为应用增加访问HTTP协议的能力 要求:1:CLASSPATH中有...  阅读全文
posted @ 2007-03-07 21:51 konhon 优华 阅读(3755) | 评论 (0)编辑 收藏


服务器:
1.jsp
<body>
<form name="_ctl0" method="post" action="TestFileManager.aspx" id="_ctl0" enctype="multipart/form-data">
<input type="hidden" name="__VIEWSTATE" value="dDwyNTIzNjA5NDU7Oz7rsE3eBYzQHDVtl+aTn96MvQW6PQ==" />
<p>
<input name="uploadfile1" id="uploadfile1" type="file" size="49" />
<input type="submit" name="Button1" value="?" id="Button1" />
</p>
<p>
<span id="Label1" style="width:459px;"></span>
</p>
<!-- Insert content here -->
</form>
</body>
客户端:
首先创建一个到服务器http的请求
HttpRequest request = new HttpRequest("http://服务器/1.jsp");
第一次使用的是GET方式
request.setMethod("GET");
紧接着进行一些请求的属性设置
request.setRequestHeader("Cache-Control", "no-cache");
这里保持连接,因为后面还要发送数据到服务器呢
request.setRequestHeader("Connection", "Keep-Alive");
下面是一些无关紧要的属性设置了。
request.setRequestHeader("Accept", "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
request.setRequestHeader("Accept-Encoding", "gzip, deflate");
request.setRequestHeader("Accept-Language", "en-au");
request.setRequestHeader("Referer", "http://服务器/1.jsp");
request.setRequestHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3215; .NET CLR 1.0.3705)");
构造好了连接请求,然后连接
request.connect();
紧接着提取Cookie值,在后文的post中可以用到。
String strCookie = request.getResponseHeader("Set-Cookie");
strCookie = strCookie.substring(0,strCookie.indexOf(";"));
下面通过循环查找,提取__VIEWSTATE的值
for ( int i = 0; i < nlist.getLength(); i++) {
node = nlist.item(i);
strName = getNodeAttributeValue(node,"name");
if ( strName.equals("__VIEWSTATE") ) {
strValue = getNodeAttributeValue(node,"value");
break;
}
}
往服务器组织发送数据
DataOutputStream dos = new DataOutputStream(request.getOutputStream());
dos.writeBytes("-----------------------------"+strBoundary);//这是每个要被发送数据间的间隔
dos.writeBytes(" Content-Disposition: form-data; name="__VIEWSTATE"");
dos.writeBytes(" "+strValue);
dos.writeBytes(" -----------------------------"+strBoundary);
这里面是发送文件的部分
dos.writeBytes(" Content-Disposition: form-data; name="uploadfile1"; filename="" + strFileName + """);
dos.writeBytes(" Content-Type: text/xml");
dos.writeBytes(" ");
dos.writeBytes(new String(data));
dos.writeBytes(" -----------------------------"+strBoundary);
dos.writeBytes(" Content-Disposition: form-data; name="Button1"");
dos.writeBytes(" 上传");
dos.writeBytes(" -----------------------------"+strBoundary+"--");
dos.writeBytes(" ");
dos.close();
posted @ 2007-03-07 20:14 konhon 优华 阅读(4244) | 评论 (1)编辑 收藏