??xml version="1.0" encoding="utf-8" standalone="yes"?>久久久精品国产亚洲成人满18免费网站,亚洲成a人片77777kkkk,亚洲精品又粗又大又爽A片http://m.tkk7.com/boddi/׃一生不?芌!zh-cnSun, 11 May 2025 22:43:32 GMTSun, 11 May 2025 22:43:32 GMT60l我d的勇气!Q!http://m.tkk7.com/boddi/archive/2007/08/15/136898.htmlboddiboddiWed, 15 Aug 2007 06:32:00 GMThttp://m.tkk7.com/boddi/archive/2007/08/15/136898.htmlhttp://m.tkk7.com/boddi/comments/136898.htmlhttp://m.tkk7.com/boddi/archive/2007/08/15/136898.html#Feedback3http://m.tkk7.com/boddi/comments/commentRss/136898.htmlhttp://m.tkk7.com/boddi/services/trackbacks/136898.html看来自己q是没有自信Qbs..........myself

boddi 2007-08-15 14:32 发表评论
]]>
Java RMI Tutorialhttp://m.tkk7.com/boddi/archive/2006/10/11/74430.htmlboddiboddiWed, 11 Oct 2006 01:51:00 GMThttp://m.tkk7.com/boddi/archive/2006/10/11/74430.htmlhttp://m.tkk7.com/boddi/comments/74430.htmlhttp://m.tkk7.com/boddi/archive/2006/10/11/74430.html#Feedback1http://m.tkk7.com/boddi/comments/commentRss/74430.htmlhttp://m.tkk7.com/boddi/services/trackbacks/74430.htmlq程Ҏ(gu)调用入门指南QJava RMI TutorialQ?/h4>

Java R MI Tutorial

q程Ҏ(gu)调用入门指南

Copyright © 2005 Stephen Suen. All rights reserved.

Java q程Ҏ(gu)调用QRemote Method Invocation, RMIQ得运行在一?Java 虚拟机(Java Virtual Machine, JVMQ的对象可以调用q行另一?JVM 之上的其他对象的Ҏ(gu)Q从而提供了E序间进行远E通讯的途径。RMI ?J2EE 的很多分布式技术的基础Q比?RMI-IIOP 乃至 EJB。本文是 RMI 的一个入门指南,目的在于帮助读者快速徏立对 Java RMI 的一个感性认识,以便q行更深层次的学?fn)。事实上Q如果你了解 RMI 的目的在于更好的理解和学?EJBQ那么本文就再合适不q了。通过本文所了解?RMI 的知识和技巧,应该_服务于这个目的了?

本文的最新版本将发布?a >E序员咖啡馆|站上(中)。欢q订阅我们的邮gl?/font>Q以获得关于本文的正式发布及更新信息?/p>

全文在保证完整性,且保留全部版权声明(包括上述链接Q的前提下可以在L媒体转蝲——须保留此标注?/p>


1. ?/font>

我们知道q程q程调用QRemote Procedure Call, RPCQ可以用于一个进E调用另一个进E(很可能在另一个远E主ZQ中?span>q程Q从而提供了q程的分布能力。Java ?RMI 则在 RPC 的基上向前又q进了一步,x供分布式 对象间的通讯Q允许我们获得在q程q程中的对象Q称E对象)的引用(UCؓq程引用Q,q而通过引用调用q程对象的方法,好像该对象是与你的客户端代码同栯行在本地q程中一栗RMI 使用了术?Ҏ(gu)"QMethodQ强调了q种q步Q即在分布式基础上,充分支持面向对象的特性?/p>

RMI q不?Java 中支持远E方法调用的唯一选择。在 RMI 基础上发展而来?RMI-IIOPQJava Remote Method Invocation over the Internet Inter-ORB ProtocolQ,不但l承?RMI 的大部分优点Qƈ且可以兼容于 CORBA。J2EE ?EJB 都要求?RMI-IIOP 而不?RMI。尽如此,理解 RMI 大大有助于 RMI-IIOP 的理解。所以,即便你的兴趣?RMI-IIOP 或?EJBQ相信本文也会对你很有帮助。另外,如果你现在就?API 感兴,那么可以告诉你,RMI 使用 java.rmi 包,?RMI-IIOP 则既使用 java.rmi 也用扩展的 javax.rmi 包?/p>

本文的随后内容将仅针?Java RMI?/p>

2. 分布式对?/font>

在学?RMI 之前Q我们需要了解一些基知识。首先需要了解所谓的分布式对象(Distributed ObjectQ。分布式对象是指一个对象可以被q程pȝ所调用。对?Java 而言Q即对象不仅可以被同一虚拟Z的其他客L(fng)序(ClientQ调用,也可以被q行于其他虚拟机中的客户E序调用Q甚臛_以通过|络被其他远E主Z上的客户E序调用?/p>

下面的图C明了客户E序是如何调用分布式对象的:

从图上我们可以看刎ͼ分布式对象被调用的过E是q样的:

  1. 客户E序调用一个被UCؓ Stub Q有时译作存根,Z不生歧义,本文用其英文形式Q的客户端代理对象。该代理对象负责对客L(fng)隐藏|络通讯的细节。Stub 知道如何通过|络套接字(SocketQ发送调用,包括如何调用参数{换ؓ适当的Ş式以便传输等?/p>

  2. Stub 通过|络调用传递到服务器端Q也是分布对象一端的一个被UCؓ Skeleton 的代理对象。同P该代理对象负责对分布式对象隐藏网l通讯的细节。Skeleton 知道如何从网l套接字QSocketQ中接受调用Q包括如何将调用参数从网l传输Ş式{换ؓ Java 形式{?/p>

  3. Skeleton 调用传递给分布式对象。分布式对象执行相应的调用,之后返回g递给 SkeletonQ进而传递到 StubQ最l返回给客户E序?/p>

q个场景Z一个基本的法则Q即行ؓ的定义和行ؓ的具体实现相分离。如图所C,客户端代理对?Stub 和分布式对象都实C相同的接口,该接口称E接口(Remote InterfaceQ。正是该接口定义了行为,而分布式对象本n则提供具体的实现。对?Java RMI 而言Q我们用接口Q?font color="red">interfaceQ定义行为,用类Q?font color="red">classQ定义实现?

3. RMI 架构

RMI 的底层架构由三层构成Q?/p>

  • 首先?Stub/Skeleton 层。该层提供了客户E序和服务程序彼此交互的接口?/p>

  • 然后是远E引用(Remote ReferenceQ层。这一层相当于在其之上?Stub/Skeleton 层和在其之下的传输协议层之前的中间gQ负责处理远E对象引用的创徏和管理?/p>

  • 最后是传输协议QTransport ProtocolQ?层。该层提供了数据协议Q用以通过U\传输客户E序和远E对象间的请求和应答?/p>

q些层之间的交互可以参照下面的示意图Q?/p>

和其它分布式对象机制一PJava RMI 的客L(fng)序用客L(fng)?Stub 向远E对象请求方法调用;服务器对象则通过服务器端?Skeleton 接受h。我们深入进去,来看看其中的一些细节?/p>

注意: 事实上,?Java 1.2 之后QRMI 不再需?Skeleton 对象Q而是通过 Java 的反机ӞReflectionQ来完成Ҏ(gu)务器端的q程对象的调用。ؓ了便于说明问题,本文以下内容仍然Z Skeleton 来讲解?/p>

当客L(fng)序调?Stub ӞStub 负责方法的参数转换为序列化QSerializedQŞ式,我们使用一个特D的术语Q即~列QMarshalQ来指代q个q程。编列的目的是将q些参数转换为可UL的Ş式,从而可以通过|络传输到远E的服务对象一端。不q的是,q个q程没有惌中那么简单。这里我们首先要理解一个经典的问题Q即Ҏ(gu)调用Ӟ参数I竟是传D是传引用呢?对于 Java RMI 来说Q存在四U情况,我们分别加以说明?/p>

  • 对于基本的原始类型(整型Q字W型{等Q,被自动的序列化Q以传值的方式~列?/p>

  • 对于 Java 的对象,如果该对象是可序列化的(实现?java.io.Serializable 接口Q,则通过 Java 序列化机制自动地加以序列化,以传值的方式~列。对象之中包含的原始cd以及所有被该对象引用,且没有声明ؓ transient 的对象也自动的序列化。当Ӟq些被引用的对象也必L可序列化的?/p>

  • l大多数内徏?Java 对象都是可序列化的?对于不可序列化的 Java 对象Q?font color="red">java.io.File 最典型Q,或者对象中包含对不可序列化Q且没有声明?transient 的其它对象的引用。则~列q程向客户E序抛出异常Q而宣告失败?/p>

  • 客户E序可以调用q程对象Q没有理q止调用参数本w也是远E对象(实现?java.rmi.Remote 接口的类的实例)。此ӞRMI 采用一U?span>模拟?/i>传引用方式(当然不是传统意义的传引用Q因为本地对内存的引用到了远E变得毫无意义)Q而不是将参数直接~列复制到远E。这U情况下Q交互的双方发生的戏剧性变化值得我们注意。参数是q程对象Q意味着该参数对象可以远E调用。当客户E序指定q程对象作ؓ参数调用服务器端q程对象的方法时QRMI 的运行时机制向服务器端的远E对象发送作为参数的q程对象的一?Stub 对象。这h务器端的q程对象可以回调(CallbackQ这?Stub 对象的方法,q而调用在客户端的q程对象的对应方法。通过q种Ҏ(gu)Q服务器端的q程对象可以修改作为参数的客户端远E对象的内部状态,q正是传l意义的传引用所具备的特性。是不是有点晕?q里的关键是要明白,在分布式环境中,所谓服务器和客L(fng)都是相对的。被h的一方就是服务器Q而发求的一方就是客L(fng)?

在调用参数的~列q程成功后,客户端的q程引用层从 Stub 那里获得了编列后的参C及对服务器端q程对象的远E引用(参见 java.rmi.server.RemoteRef APIQ。该层负责将客户E序的请求依据底层的 RMI 数据传输协议转换Z输层h。在 RMI 中,有多U的可能的传输机Ӟ比如点对点(Point-to-PointQ以及广播(MulticastQ等。不q,在当前的 JMI 版本中只支持点对点协议,卌E引用层生成唯一的传输层hQ发往指定的唯一q程对象Q参?java.rmi.server.UnicastRemoteObject APIQ?/p>

在服务器端,服务器端的远E引用层接收传输层请求,q将其{换ؓ对远E对象的服务器端代理对象 Skeleton 的调用。Skeleton 对象负责请求{换ؓ对实际的q程对象的方法调用。这是通过与编列过E相对的反编列(UnmarshalQ过E实现的。所有序列化的参数被转换?Java 形式Q其中作为参数的q程对象Q实际上发送的是远E引用)被{换ؓ服务器端本地?Stub 对象?/p>

如果Ҏ(gu)调用有返回值或者抛出异常,?Skeleton 负责~列q回值或者异常,通过服务器端的远E引用层Q经传输层传递给客户端;相应圎ͼ客户端的q程引用层和 Stub 负责反编列ƈ最l将l果q回l客L(fng)序?/p>

整个q程中,可能最让hqh的是q程引用层。这里只要明白,本地?Stub 对象是如何生的Q就不难理解q程引用的意义所在了。远E引用中包含了其所指向的远E对象的信息Q该q程引用用于构造作为本C理对象的 Stub 对象。构造后QStub 对象内部维护该q程引用。真正在|络上传输的实际上就是这个远E引用,而不?Stub 对象?/p>

4. RMI 对象服务

?RMI 的基本架构之上,RMI 提供服务与分布式应用E序的一些对象服务,包括对象的命?注册QNaming/RegistryQ服务,q程对象Ȁz(ActivationQ服务以及分布式垃圾攉QDistributed Garbage Collection, DGCQ。作为入门指南,本文指介绍其中的命?注册服务Q因为它是实?RMI 所必备的。其它内容请读者自行参考其它更加深入的资料?/p>

在前一节中Q如果你喜欢刨根问底Q可能已l注意到Q客L(fng)要调用远E对象,是通过其代理对?Stub 完成的,那么 Stub 最早是从哪里得来的呢?RMI 的命?注册服务正是解决q一问题的。当服务器端惛_客户端提供基?RMI 的服务时Q它需要将一个或多个q程对象注册到本地的 RMI 注册表中Q参?font color="red">java.rmi.registry.Registry APIQ。每个对象在注册旉被指定一个将来用于客L(fng)序引用该对象的名U。客L(fng)序通过命名服务Q参?java.rmi.Naming APIQ,指定cM URL 的对象名U就可以获得指向q程对象的远E引用。在 Naming 中的 lookup() Ҏ(gu)扑ֈq程对象所在的L后,它将索该L上的 RMI 注册表,q请求所需的远E对象。如果注册表发现被请求的q程对象Q它?yu)生成一个对该远E对象的q程引用Qƈ其q回l客L(fng)Q客L(fng)则基于远E引用生成相应的 Stub 对象Qƈ引用传递给调用者。之后,双方可以按照我们前面讲q的方式q行交互了?

注意: RMI 命名服务提供?Naming cdƈ不是你的唯一选择。RMI 的注册表可以与其他命名服务绑定,比如 JNDIQ这样你可以通过 JNDI 来访?RMI 的注册表了?/p>

5. 实战 RMI

理论M开实践Q理?RMI 的最好办法就是通过例子。开?RMI 的分布式对象的大体过E包括如下几步:

  1. 定义q程接口。这一步是通过扩展 java.rmi.Remote 接口Qƈ定义所需的业务方法实现的?/p>

  2. 定义q程接口的实现类。即实现上一步所定义的接口,l出业务Ҏ(gu)的具体实现逻辑?/p>

  3. ~译q程接口和实现类Qƈ通过 RMI ~译?rmic Z实现cȝ成所需?Stub ?Skeleton cR?/p>

RMI 中各个组件之间的关系如下面这个示意图所C:

回忆我们上一节所讲的QStub ?Skeleton 负责代理客户和服务器之间的通讯。但我们q不需要自q成它们,相反QRMI 的编译器 rmic 可以帮我们基于远E接口和实现cȝ成这些类。当客户端对象通过命名服务向服务器端的 RMI 注册表请求远E对象时QRMI 自动构造对应远E对象的 Skeleton 实例对象Qƈ通过 Skeleton 对象远E引用返回给客户端。在客户端,该远E引用将用于构?Stub cȝ实例对象。之后,Stub 对象?Skeleton 对象可以代理客户对象和q程对象之间的交互了?

我们的例子展C一个简单的应用场景。服务器端部|了一个计引擎,负责接受来自客户端的计算dQ在服务器端执行计算dQƈ结果返回给客户端。客L(fng)发送ƈ调用计算引擎的计Q务实际上是计指定精度的 π 倹{?/p>

重要: 本文的例子改~自 The Java?Tutorial Trail:RMI。所有权利属于相应的所有h?/p>

6. 定义q程接口

定义q程接口与非分布式应用中定义接口的方法没有太多的区别。只要遵守下面两个要求:

  • q程接口必须直接或者间接的扩展?java.rmi.Remote 接口。远E接口还可以在扩展该接口的基上,同时扩展其它接口Q只要被扩展的接口的所有方法与q程接口的所有方法一h下一个要求?/p>

  • 在远E接口或者其接口(Super-interfaceQ中声明的方法必L下列对q程Ҏ(gu)的要求:

    • q程Ҏ(gu)必须声明抛出 java.rmi.RemoteException 异常Q或者该异常的超c(SuperclassQ,比如 java.io.IOException 或?java.lang.Exception 异常。在此基上,q程Ҏ(gu)可以声明抛出应用特定的其它异常?/p>

    • 在远E方法声明中Q作为参数或者返回值的q程对象Q或者包含在其它非远E对象中的远E对象,必须声明为其对应的远E接口,而不是实际的实现cR?/p>

注意: ?Java 1.2 之前Q上面关于抛出异常的要求更严|卛_L?java.rmi.RemoteExcptionQ不允许cM java.io.IOException q样的超cR现在之所以放宽了q一要求Q是希望可以使定义既可以用于q程对象Q也可以用于本地对象的接口变得容易一些(x EJB 中的本地接口和远E接口)。当Ӟqƈ没有佉K题好多少Q你q是必须声明异常。不q,一U观点认不是问题Q强制声明异常可以开发h员保持清醒的头脑Q因E对象和本地对象在调用时传参的语意是不同的。本地对象是传引用,而远E对象主要是传|q意呛_参数内部状态的修改产生的结果是不同的?

对于W一个要求,java.rmi.Remote 接口实际上没有Q何方法,而只是用作标记接口。RMI 的运行环境依赖该接口判断对象是否是远E对象。第二个要求则是因ؓ分布式应用可能发生Q何问题,比如|络问题{等?/p>

?1 列出了我们的q程接口定义。该接口只有一个方法:executeTask() 用以执行指定的计Q务,q返回相应的l果。注意,我们用后~ Remote 表明接口是远E接口?/p>

?1. ComputeEngineRemote q程接口

package rmitutorial;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface ComputeEngineRemote extends Remote {
    public Object executeTask(Task task) throws RemoteException;    
}

?2 列出了计Q务接口的定义。该接口也只有一个方法:execute() 用以执行实际的计逻辑Qƈq回l果。注意,该接口不是远E接口,所以没有扩?java.rmi.Remote 接口Q其Ҏ(gu)也不必抛?java.rmi.RemoteException 异常。但是,因ؓ它将用作q程Ҏ(gu)的参敎ͼ所以扩展了 java.io.Serializable 接口?/p>

?2. Task 接口

package rmitutorial;

import java.io.Serializable;

public interface Task extends Serializable {
    Object execute();
}

7. 实现q程接口

接下来,我们实现前面定义的q程接口?a>?3l出了实现的源代码?/p>

?3. ComputeEngine 实现

package rmitutorial;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class ComputeEngine extends UnicastRemoteObject 
        implements ComputeEngineRemote {
    
    public ComputeEngine() throws RemoteException {
        super();
    }
    
    public Object executeTask(Task task) throws RemoteException {
        return task.execute();
    }
}

c?ComputeEngine 实现了之前定义的q程接口Q同时承自 java.rmi.server.UnicastRemoteObject 类?font color="red">UnicastRemoteObject cL一个便L(fng)Q它实现了我们前面所讲的Z TCP/IP 的点对点通讯机制。远E对象都必须从该cL展(除非你想自己实现几乎所?UnicastRemoteObject 的方法)。在我们的实现类的构造函CQ调用了类的构造函敎ͼ当然Q即使你不显式的调用q个构徏函数Q它也一样会被调用。这里这样做Q只是ؓ了突出强调这U调用而已Q。该构造函数的最重要的意义就是调?UnicastRemoteObjectexportObject() Ҏ(gu)。导出(ExportQ对象是指ɘq程对象准备qAQ可以接受进来的调用的过E。而这个过E的最重要内容是建立服务器套接字Q监听特定的端口Q等待客L(fng)的调用请求?/p>

8. 引导E序

Z让客L(fng)序可以找到我们的q程对象Q就需要将我们的远E对象注册到 RMI 的注册表。这个过E有时被UCؓ"引导"q程QBootstrapQ。我们将为此~写一个独立的引导E序负责创徏和注册远E对象?a>?4 l出了引导程序的源代码?

?4. 引导E序

package rmitutorial;

import java.rmi.Naming;
import java.rmi.RMISecurityManager;

public class Bootstrap {
    
    public static void main(String[] args) throws Exception {
        String name = "ComputeEngine";
        
        ComputeEngine engine = new ComputeEngine();
        System.out.println("ComputerEngine exported");
        
        Naming.rebind(name, engine);
        System.out.println("ComputeEngine bound");
    }
}

可以看到Q我们首先创Z一个远E对象(同时导出了该对象Q,之后该对象l定?RMI 注册表中?font color="red">Naming ?rebind() Ҏ(gu)接受一?URL 形式的名字作l定之用。其完整格式如下Q?/p>

protocol://host:port/object

其中Q协议(ProtocolQ默认ؓ rmiQ主机名默认?localhostQ端口默认ؓ 1099。注意,JDK 中提供的默认 Naming 实现只支?rmi 协议。在我们的引导程序里面只l出了对象绑定的名字Q而其它部分均使用~省倹{?/p>

9. 客户端程?/font>

?5 l出了我们的客户端程序。该E序接受两个参数Q分别是q程对象所在的L地址和希望获得的 π 值的_ֺ?

?5. Client.java

package rmitutorial;

import java.math.BigDecimal;
import java.rmi.Naming;

public class Client {
    public static void main(String args[]) throws Exception {
            String name = "rmi://" + args[0] + "/ComputeEngine";
            ComputeEngineRemote engineRemote = 
                    (ComputeEngineRemote)Naming.lookup(name);
            Pi task = new Pi(Integer.parseInt(args[1]));
            BigDecimal pi = (BigDecimal)(engineRemote.executeTask(task));
            System.out.println(pi);
    }
}

?6. Pi.java

package rmitutorial;

import java.math.*;

public class Pi implements Task {
    
    private static final BigDecimal ZERO =
            BigDecimal.valueOf(0);
    private static final BigDecimal  ONE =
            BigDecimal.valueOf(1);
    private static final BigDecimal FOUR =
            BigDecimal.valueOf(4);
    
    private static final int roundingMode =
            BigDecimal.ROUND_HALF_EVEN;
    
    private int digits;
    
    public Pi(int digits) {
        this.digits = digits;
    }
    
    public Object execute() {
        return computePi(digits);
    }
    
    public static BigDecimal computePi(int digits) {
        int scale = digits + 5;
        BigDecimal arctan1_5 = arctan(5, scale);
        BigDecimal arctan1_239 = arctan(239, scale);
        BigDecimal pi = arctan1_5.multiply(FOUR).subtract(
                arctan1_239).multiply(FOUR);
        return pi.setScale(digits,
                BigDecimal.ROUND_HALF_UP);
    }

    public static BigDecimal arctan(int inverseX,
            int scale) {
        BigDecimal result, numer, term;
        BigDecimal invX = BigDecimal.valueOf(inverseX);
        BigDecimal invX2 =
                BigDecimal.valueOf(inverseX * inverseX);
        
        numer = ONE.divide(invX, scale, roundingMode);
        
        result = numer;
        int i = 1;
        do {
            numer =
                    numer.divide(invX2, scale, roundingMode);
            int denom = 2 * i + 1;
            term =
                    numer.divide(BigDecimal.valueOf(denom),
                    scale, roundingMode);
            if ((i % 2) != 0) {
                result = result.subtract(term);
            } else {
                result = result.add(term);
            }
            i++;
        } while (term.compareTo(ZERO) != 0);
        return result;
    }
}

10. ~译CZE序

~译我们的示例程序和~译其它非分布式的应用没什么区别。只是编译之后,需要?RMI ~译器,?rmic 生成所需 Stub ?Skeleton 实现。?rmic 的方式是我们的q程对象的实现类Q不是远E接口)的全cd作ؓ参数来运?rmic 命o。参考下面的CZQ?/p>

E:\classes\rmic rmitutorial.ComputeEngine

~译之后生?rmitutorial.ComputeEngine_Skel ?rmitutorial.ComputeEngine_Stub 两个cR?/p>

11. q行CZE序

q程对象的引用通常是通过 RMI 的注册表服务以及 java.rmi.Naming 接口获得的。远E对象需要导出(注册Q相应的q程引用到注册表服务Q之后注册表服务可以监听ƈ服务于客L(fng)对远E对象引用的h。标准的 Sun Java SDK 提供了一个简单的 RMI 注册表服务程序,?rmiregistry 用于监听特定的端口,{待q程对象的注册,以及客户端对q些q程对象引用的检索请求?

在运行我们的CZE序之前Q首先要启动 RMI 的注册表服务。这个过E很单,只要直接q行 rmiregistry 命o卛_。缺省的情况下,该服务将监听 1099 端口。如果需要指定其它的监听端口Q可以在命o行指定希望监听的端口Q如果你指定了其它端口,需要修改示例程序以适应环境Q。如果希望该E序在后台运行,?Unix 上可以以如下方式q行Q当Ӟ可以~省端口参数Q:

$ rmiregistry 1099 &

?Windows 操作pȝ中可以这栯行:

C:\> start rmiregistry 1099

我们?rmitutorial.Bootstrap cd用于启动q程对象Qƈ其l定?RMI 注册表中。运行该cdQ远E对象也进入监听状态,{待来自客户端的Ҏ(gu)调用h?/p>

$ java rmitutorial.Bootstrap
ComputeEngine exported
ComputeEngine bound

启动q程对象后,打开另一个命令行H口Q运行客L(fng)。命令行的第一个参Cؓ RMI 注册表的地址Q第二个参数为期望的 π 值精度。参考下面的CZQ?/p>

$ java rmitutorial.Client localhost 50
3.14159265358979323846264338327950288419716939937511

12. 其它信息

在演C示例程序时Q我们实际上是在同一L上运行的服务器和客户端,q且无论是服务器和客L(fng)所需的类都在相同的类路径上,可以同时被服务器和客L(fng)所讉K。这忽略?Java RMI 的一个重要细节,卛_态类装蝲。因?RMI 的特性(包括其它几个Ҏ(gu))q不适用?J2EE ?RMI-IIOP ?EJB 技术,所以,本文不作详l介l,误者自行参考本文给出的参考资料。不q,Z让好奇的读者不至于q分失望Q这里简单介l一下动态类装蝲的基本思想?

RMI q行时系l采用动态类装蝲机制来装载分布式应用所需的类。如果你可以直接讉K应用所涉及的所有包括服务器端客L(fng)在内的主机,q且可以把分布式应用所需的所有类都安装在每个L?CLASSPATH 中(上面的示例就是极端情况,所有的东西都在本地LQ,那么你完全不必关?RMI c装载的l节。显Ӟ既然是分布式应用Q情况往往正相反。对?RMI 应用Q客L(fng)需要装载客L(fng)自n所需的类Q将要调用的q程对象的远E接口类以及对应?Stub c;服务器端则要装蝲q程对象的实现类以及对应?Skeleton c(Java 1.2 之后不需?Skeleton c)。RMI 在处理远E调用涉及的q程引用Q参C及返回值时Q可以将一个指定的 URL ~码到流中。交互的另一端可以通过 ?URL 获得处理q些对象所需的类文g。这一点类g Applet 中的 CODEBASE 的概念,交互的两端通过 HTTP 服务器发布各自控制的c,允许交互的另一端动态下载这些类。以我们的示例ؓ例,客户端不必部|?ComputeEngine_Stub 的类文gQ而可以通过服务器端?HTTP 服务器获得类文g。同P服务器端也不需要客L(fng)实现的定制Q?Pi 的类文g?/p>

注意Q这U动态类装蝲需要交互的两端加蝲定制的安全管理器Q参?java.rmi.RMISecurityManager APIQ,以及对应的策略文件?/p>

13. 参考资?/font>

  • The Java?Tutorial Trail:RMI

  • David Flanagan, Jim Farley, William Crawford and Kris Magnusson, 1999, ISBN 1-56592-483-5E, O'Reilly, Java?Enterprise in a Nutshell

  • Ed Roman, Scott Ambler and Tyler Jewell 2002, ISBN 0-471-41711-4, John Wiley &Sons, Inc., Matering Enterprise JavaBeans?/i> , Second Edition



boddi 2006-10-11 09:51 发表评论
]]>
正则表达?转蝲)http://m.tkk7.com/boddi/archive/2006/09/13/69397.htmlboddiboddiWed, 13 Sep 2006 08:28:00 GMThttp://m.tkk7.com/boddi/archive/2006/09/13/69397.htmlhttp://m.tkk7.com/boddi/comments/69397.htmlhttp://m.tkk7.com/boddi/archive/2006/09/13/69397.html#Feedback0http://m.tkk7.com/boddi/comments/commentRss/69397.htmlhttp://m.tkk7.com/boddi/services/trackbacks/69397.html正则表达?转蝲) 关键?/b>Q?正则表达?/a>    模式匚w    Javascript                                          

关键字:正则表达? 模式匚w Javascript

摘要Q收集一些常用的正则表达式?/p>

正则表达式用于字W串处理Q表单验证等场合Q实用高效,但用到时L不太把握Q以致往往要上|查一番。我一些常用的表达式收藏在q里Q作备忘之用。本贴随时会更新?/p>

匚w中文字符的正则表辑ּQ?[\u4e00-\u9fa5]

匚w双字节字W?包括汉字在内)Q[^\x00-\xff]

应用Q计字W串的长度(一个双字节字符长度?QASCII字符?Q?/font>

String.prototype.len=function(){return this.replace([^\x00-\xff]/g,"aa").length;}

匚wI的正则表辑ּQ\n[\s| ]*\r

匚wHTML标记的正则表辑ּQ?<(.*)>.*<\/\1>|<(.*) \/>/

匚w首尾I格的正则表辑ּQ?^\s*)|(\s*$)

String.prototype.trim = function()
{
    return this.replace(/(^\s*)|(\s*$)/g, "");
}

利用正则表达式分解和转换IP地址Q?/font>

下面是利用正则表辑ּ匚wIP地址QƈIP地址转换成对应数值的JavascriptE序Q?/font>

function IP2V(ip)
{
 re=/(\d+)\.(\d+)\.(\d+)\.(\d+)/g  //匚wIP地址的正则表辑ּ
if(re.test(ip))
{
return RegExp.$1*Math.pow(255,3))+RegExp.$2*Math.pow(255,2))+RegExp.$3*255+RegExp.$4*1
}
else
{
 throw new Error("Not a valid IP address!")
}
}

不过上面的程序如果不用正则表辑ּQ而直接用split函数来分解可能更单,E序如下Q?/font>

var ip="10.100.20.168"
ip=ip.split(".")
alert("IP值是Q?+(ip[0]*255*255*255+ip[1]*255*255+ip[2]*255+ip[3]*1))

匚wEmail地址的正则表辑ּQ\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*

匚w|址URL的正则表辑ּQhttp://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?

利用正则表达式去除字串中重复的字W的法E序Q[注:此程序不正确Q原因见本脓(chung)回复]

var s="abacabefgeeii"
var s1=s.replace(/(.).*\1/g,"$1")
var re=new RegExp("["+s1+"]","g")
var s2=s.replace(re,"")
alert(s1+s2)  //l果为:abcefgi

我原来在CSDN上发贴寻求一个表辑ּ来实现去除重复字W的Ҏ(gu)Q最l没有找刎ͼq是我能惛_的最单的实现Ҏ(gu)。思\是用后向引用取出包括重复的字符Q再以重复的字符建立W二个表辑ּQ取C重复的字W,两者串q。这个方法对于字W顺序有要求的字W串可能不适用?/font>

得用正则表达式从URL地址中提取文件名的javascriptE序Q如下结果ؓpage1

s="http://www.9499.net/page1.htm"
s=s.replace(/(.*\/){0,}([^\.]+).*/ig,"$2")
alert(s)

利用正则表达式限制网表单里的文本框输入内容Q?/font>

用正则表辑ּ限制只能输入中文Qonkeyup="value=value.replace(/[^\u4E00-\u9FA5]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\u4E00-\u9FA5]/g,''))"

用正则表辑ּ限制只能输入全角字符Q?/font> onkeyup="value=value.replace(/[^\uFF00-\uFFFF]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\uFF00-\uFFFF]/g,''))"

用正则表辑ּ限制只能输入数字Qonkeyup="value=value.replace(/[^\d]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))"

用正则表辑ּ限制只能输入数字和英文:onkeyup="value=value.replace(/[\W]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))"



boddi 2006-09-13 16:28 发表评论
]]>
javascript技?强jsȝ)http://m.tkk7.com/boddi/archive/2006/09/11/68943.htmlboddiboddiMon, 11 Sep 2006 05:35:00 GMThttp://m.tkk7.com/boddi/archive/2006/09/11/68943.htmlhttp://m.tkk7.com/boddi/comments/68943.htmlhttp://m.tkk7.com/boddi/archive/2006/09/11/68943.html#Feedback0http://m.tkk7.com/boddi/comments/commentRss/68943.htmlhttp://m.tkk7.com/boddi/services/trackbacks/68943.html阅读全文

boddi 2006-09-11 13:35 发表评论
]]>
javaexcelapplicationhttp://m.tkk7.com/boddi/archive/2006/09/09/68705.htmlboddiboddiSat, 09 Sep 2006 05:14:00 GMThttp://m.tkk7.com/boddi/archive/2006/09/09/68705.htmlhttp://m.tkk7.com/boddi/comments/68705.htmlhttp://m.tkk7.com/boddi/archive/2006/09/09/68705.html#Feedback3http://m.tkk7.com/boddi/comments/commentRss/68705.htmlhttp://m.tkk7.com/boddi/services/trackbacks/68705.html
Java Excel是一开放源码项目,通过它Java开发h员可以读取Excel文g的内宏V创建新的Excel文g、更新已l存在的Excel文g。用该API非Windows操作pȝ也可以通过UJava应用来处理Excel数据表。因为是使用Java~写的,所以我们在Web应用中可以通过JSP、Servlet来调用API实现对Excel数据表的讉K?br />
现在发布的稳定版本是V2.0Q提供以下功能:

从Excel 95?7?000{格式的文g中读取数据;
dExcel公式Q可以读取Excel 97以后的公式)Q?
生成Excel数据表(格式为Excel 97Q;
支持字体、数字、日期的格式化;
支持单元格的阴媄操作Q以及颜色操作;
修改已经存在的数据表Q?
现在q不支持以下功能Q但不久׃提供了:

不能够读取图表信息;
可以读,但是不能生成公式QQ何类型公式最后的计算值都可以dQ?
应用CZ

1 从Excel文gd数据?br />
Java Excel API既可以从本地文gpȝ的一个文?.xls)Q也可以从输入流中读取Excel数据表。读取Excel数据表的W一步是创徏Workbook(术语Q工作薄)Q下面的代码片段举例说明了应该如何操作:(完整代码见ExcelReading.java)


import java.io.*;
import jxl.*;
????br />try
{
//构徏Workbook对象, 只读Workbook对象
//直接从本地文件创建Workbook
//从输入流创徏Workbook
   InputStream is = new FileInputStream(sourcefile);
   jxl.Workbook rwb = Workbook.getWorkbook(is);
}
catch (Exception e)
{
e.printStackTrace();
}



一旦创ZWorkbookQ我们就可以通过它来讉KExcel Sheet(术语Q工作表)。参考下面的代码片段Q?br />

//获取W一张Sheet?br />Sheet rs = rwb.getSheet(0);



我们既可能通过Sheet的名U来讉K它,也可以通过下标来访问它。如果通过下标来访问的话,要注意的一Ҏ(gu)下标?开始,像数组一栗?br />
一旦得CSheetQ我们就可以通过它来讉KExcel Cell(术语Q单元格)。参考下面的代码片段Q?br />

//获取W一行,W一列的?br />Cell c00 = rs.getCell(0, 0);
String strc00 = c00.getContents();

//获取W一行,W二列的?br />Cell c10 = rs.getCell(1, 0);
String strc10 = c10.getContents();

//获取W二行,W二列的?br />Cell c11 = rs.getCell(1, 1);
String strc11 = c11.getContents();

System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType());
System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType());
System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());



如果仅仅是取得Cell的|我们可以方便地通过getContents()Ҏ(gu)Q它可以Q何类型的Cell值都作ؓ一个字W串q回。示例代码中Cell(0, 0)是文本型QCell(1, 0)是数字型QCell(1,1)是日期型Q通过getContents()Q三U类型的q回值都是字W型?br />
如果有需要知道Cell内容的确切类型,API也提供了一pd的方法。参考下面的代码片段Q?br />

String strc00 = null;
double strc10 = 0.00;
Date strc11 = null;

Cell c00 = rs.getCell(0, 0);
Cell c10 = rs.getCell(1, 0);
Cell c11 = rs.getCell(1, 1);

if(c00.getType() == CellType.LABEL)
{
LabelCell labelc00 = (LabelCell)c00;
strc00 = labelc00.getString();
}
if(c10.getType() == CellType.NUMBER)
{
NmberCell numc10 = (NumberCell)c10;
strc10 = numc10.getValue();
}
if(c11.getType() == CellType.DATE)
{
DateCell datec11 = (DateCell)c11;
strc11 = datec11.getDate();
}

System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType());
System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType());
System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());



在得到Cell对象后,通过getType()Ҏ(gu)可以获得该单元格的类型,然后与API提供的基本类型相匚wQ强制{换成相应的类型,最后调用相应的取值方法getXXX()Q就可以得到定cd的倹{API提供了以下基本类型,与Excel的数据格式相对应Q如下图所C:





每种cd的具体意义,请参见Java Excel API Document?br />
当你完成对Excel?sh)子表格数据的处理后Q一定要使用close()Ҏ(gu)来关闭先前创建的对象Q以释放d数据表的q程中所占用的内存空_在读取大量数据时昑־ؓ重要。参考如下代码片D:


//操作完成Ӟ关闭对象Q释攑֍用的内存I间
rwb.close();



Java Excel API提供了许多访问Excel数据表的Ҏ(gu)Q在q里我只要地介绍几个常用的方法,其它的方法请参考附录中的Java Excel API Document?br />
WorkbookcL供的Ҏ(gu)

1. int getNumberOfSheets()
获得工作薄(WorkbookQ中工作表(SheetQ的个数Q示例:


jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
int sheets = rwb.getNumberOfSheets();



2. Sheet[] getSheets()
q回工作薄(WorkbookQ中工作表(SheetQ对象数l,CZQ?br />

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
Sheet[] sheets = rwb.getSheets();



3. String getVersion()
q回正在使用的API的版本号Q好像是没什么太大的作用?br />

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
String apiVersion = rwb.getVersion();



Sheet接口提供的方?br />
1) String getName()
获取Sheet的名UͼCZQ?br />

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
String sheetName = rs.getName();



2) int getColumns()
获取Sheet表中所包含的d敎ͼCZQ?br />

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
int rsColumns = rs.getColumns();



3) Cell[] getColumn(int column)
获取某一列的所有单元格Q返回的是单元格对象数组Q示例:


jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
Cell[] cell = rs.getColumn(0);



4) int getRows()
获取Sheet表中所包含的总行敎ͼCZQ?br />

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
int rsRows = rs.getRows();



5) Cell[] getRow(int row)
获取某一行的所有单元格Q返回的是单元格对象数组Q示例子Q?br />

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
Cell[] cell = rs.getRow(0);



6) Cell getCell(int column, int row)
获取指定单元格的对象引用Q需要注意的是它的两个参敎ͼW一个是列数Q第二个是行敎ͼq与通常的行、列l合有些不同?br />

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
jxl.Sheet rs = rwb.getSheet(0);
Cell cell = rs.getCell(0, 0);



2 生成新的Excel工作?br />
下面的代码主要是向大家介l如何生成简单的Excel工作表,在这里单元格的内Ҏ(gu)不带M修饰?如:字体Q颜色等{?Q所有的内容都作为字W串写入?完整代码见ExcelWriting.java)

与读取Excel工作表相|首先要用Workbookcȝ工厂Ҏ(gu)创徏一个可写入的工作薄(Workbook)对象Q这里要注意的是Q只能通过API提供的工厂方法来创徏WorkbookQ而不能用WritableWorkbook的构造函敎ͼ因ؓcWritableWorkbook的构造函Cؓprotectedcd。示例代码片D如下:


import java.io.*;
import jxl.*;
import jxl.write.*;
????br />try
{
//构徏Workbook对象, 只读Workbook对象
//Method 1Q创建可写入的Excel工作?br />   jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile));

//Method 2Q将WritableWorkbook直接写入到输出流
/*
   OutputStream os = new FileOutputStream(targetfile);
   jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(os);
*/
}
catch (Exception e)
{
e.printStackTrace();
}



API提供了两U方式来处理可写入的输出,一U是直接生成本地文gQ如果文件名不带全\径的话,~省的文件会定位在当前目录,如果文g名带有全路径的话Q则生成的Excel文g则会定位在相应的目录Q另外一U是Excel对象直接写入到输出流Q例如:用户通过览器来讉KWeb服务器,如果HTTP头设|正的话,览器自动调用客L(fng)的Excel应用E序Q来昄动态生成的Excel?sh)子表格?br />
接下来就是要创徏工作表,创徏工作表的Ҏ(gu)与创建工作薄的方法几乎一P同样是通过工厂模式Ҏ(gu)获得相应的对象,该方法需要两个参敎ͼ一个是工作表的名称Q另一个是工作表在工作薄中的位|,参考下面的代码片段Q?br />

//创徏Excel工作?br />jxl.write.WritableSheet ws = wwb.createSheet("Test Sheet 1", 0);



"q锅也支好了Q材料也准备齐全了,可以开始下锅了Q?Q现在要做的只是实例化API所提供的Excel基本数据cdQƈ它们添加到工作表中可以了Q参考下面的代码片段Q?br />

//1.dLabel对象
jxl.write.Label labelC = new jxl.write.Label(0, 0, "This is a Label cell");
ws.addCell(labelC);

//d带有字型Formatting的对?br />jxl.write.WritableFont wf = new jxl.write.WritableFont(WritableFont.TIMES, 18, WritableFont.BOLD, true);
jxl.write.WritableCellFormat wcfF = new jxl.write.WritableCellFormat(wf);
jxl.write.Label labelCF = new jxl.write.Label(1, 0, "This is a Label Cell", wcfF);
ws.addCell(labelCF);

//d带有字体颜色Formatting的对?br />jxl.write.WritableFont wfc = new jxl.write.WritableFont(WritableFont.ARIAL, 10, WritableFont.NO_BOLD, false,
Underlinestyle.NO_UNDERLINE, jxl.format.Colour.RED);
jxl.write.WritableCellFormat wcfFC = new jxl.write.WritableCellFormat(wfc);
jxl.write.Label labelCFC = new jxl.write.Label(1, 0, "This is a Label Cell", wcfFC);
ws.addCell(labelCF);

//2.dNumber对象
jxl.write.Number labelN = new jxl.write.Number(0, 1, 3.1415926);
ws.addCell(labelN);

//d带有formatting的Number对象
jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##");
jxl.write.WritableCellFormat wcfN = new jxl.write.WritableCellFormat(nf);
jxl.write.Number labelNF = new jxl.write.Number(1, 1, 3.1415926, wcfN);
ws.addCell(labelNF);

//3.dBoolean对象
jxl.write.Boolean labelB = new jxl.write.Boolean(0, 2, false);
ws.addCell(labelB);

//4.dDateTime对象
jxl.write.DateTime labelDT = new jxl.write.DateTime(0, 3, new java.util.Date());
ws.addCell(labelDT);

//d带有formatting的DateFormat对象
jxl.write.DateFormat df = new jxl.write.DateFormat("dd MM yyyy hh:mm:ss");
jxl.write.WritableCellFormat wcfDF = new jxl.write.WritableCellFormat(df);
jxl.write.DateTime labelDTF = new jxl.write.DateTime(1, 3, new java.util.Date(), wcfDF);
ws.addCell(labelDTF);



q里有两点大家要引v大家的注意。第一点,在构造单元格Ӟ单元格在工作表中的位|就已经定了。一旦创建后Q单元格的位|是不能够变更的Q尽单元格的内Ҏ(gu)可以改变的。第二点Q单元格的定位是按照下面q样的规?column, row)Q而且下标都是?开始,例如QA1被存储在(0, 0)QB1被存储在(1, 0)?br />
最后,不要忘记关闭打开的Excel工作薄对象,以释攑֍用的内存Q参见下面的代码片段Q?br />

//写入Exel工作?br />wwb.write();

//关闭Excel工作薄对?br />wwb.close();



q可能与dExcel文g的操作有少不同Q在关闭Excel对象之前Q你必须要先调用write()Ҏ(gu)Q因为先前的操作都是存储在缓存中的,所以要通过该方法将操作的内容保存在文g中。如果你先关闭了Excel对象Q那么只能得C张空的工作薄了?br />
3 拯、更新Excel工作?br />
接下来简要介l一下如何更C个已l存在的工作薄,主要是下面二步操作,W一步是构造只ȝExcel工作薄,W二步是利用已经创徏的Excel工作薄创建新的可写入的Excel工作薄,参考下面的代码片段Q?完整代码见ExcelModifying.java)


//创徏只读的Excel工作薄的对象
jxl.Workbook rw = jxl.Workbook.getWorkbook(new File(sourcefile));

//创徏可写入的Excel工作薄对?br />jxl.write.WritableWorkbook  wwb = Workbook.createWorkbook(new File(targetfile), rw);
          
//dW一张工作表
jxl.write.WritableSheet ws = wwb.getSheet(0);

//获得W一个单元格对象
jxl.write.WritableCell wc = ws.getWritableCell(0, 0);
          
//判断单元格的cd, 做出相应的{?br />if(wc.getType() == CellType.LABEL)
{
Label l = (Label)wc;
   l.setString("The value has been modified.");
}

//写入Excel对象
wwb.write();

//关闭可写入的Excel对象
wwb.close();

//关闭只读的Excel对象
rw.close();



之所以用这U方式构建Excel对象Q完全是因ؓ效率的原因,因ؓ上面的示例才是API的主要应用。ؓ了提高性能Q在d工作表时Q与数据相关的一些输Z息,所有的格式信息Q如Q字体、颜色等{,是不被处理的Q因为我们的目的是获得行数据的|既没有了修饎ͼ也不会对行数据的g生什么媄响。唯一的不利之处就是,在内存中会同时保存两个同L(fng)工作表,q样当工作表体积比较大时Q会占用相当大的内存Q但现在好像内存的大ƈ不是什么关键因素了?br />
一旦获得了可写入的工作表对象,我们可以对单元格对象进行更新的操作了,在这里我们不必调用API提供的add()Ҏ(gu)Q因为单元格已经于工作表当中Q所以我们只需要调用相应的setXXX()Ҏ(gu)Q就可以完成更新的操作了?br />
单元格原有的格式化修饰是不能去掉的Q我们还是可以将新的单元g饰加上去Q以使单元格的内容以不同的Ş式表现?br />
新生成的工作表对象是可写入的Q我们除了更新原有的单元格外Q还可以d新的单元格到工作表中Q这与示?的操作是完全一L(fng)?br />
最后,不要忘记调用write()Ҏ(gu)Q将更新的内容写入到文g中,然后关闭工作薄对象,q里有两个工作薄对象要关闭,一个是只读的,另外一个是可写入的?br />
以上摘自IBM|站



boddi 2006-09-09 13:14 发表评论
]]>
使用JXLdExcel表格,拯、更新Excel工作?/title><link>http://m.tkk7.com/boddi/archive/2006/09/09/68696.html</link><dc:creator>boddi</dc:creator><author>boddi</author><pubDate>Sat, 09 Sep 2006 04:23:00 GMT</pubDate><guid>http://m.tkk7.com/boddi/archive/2006/09/09/68696.html</guid><wfw:comment>http://m.tkk7.com/boddi/comments/68696.html</wfw:comment><comments>http://m.tkk7.com/boddi/archive/2006/09/09/68696.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.tkk7.com/boddi/comments/commentRss/68696.html</wfw:commentRss><trackback:ping>http://m.tkk7.com/boddi/services/trackbacks/68696.html</trackback:ping><description><![CDATA[ <table cellspacing="0" cellpadding="0" width="100%" border="0"> <tbody> <tr> <td> <table cellspacing="0" cellpadding="0" width="100%" bgcolor="#ffffff" border="0"> <tbody> <tr> <td class="title1" align="middle" width="100%" bgcolor="#eeeeee" colspan="3" height="40"> <b>使用JXLdExcel表格,拯、更新Excel工作?/b> </td> </tr> <tr> <td align="middle" width="100%" bgcolor="#eeeeee"> <a >xymiser</a> 原创  (参与分:41669Q专家分Q?761)   发表Q?006-01-18 22:11   版本Q?.0   阅读Q?b>1666</b>?</td> </tr> </tbody> </table> </td> </tr> <!-- end of article title --> <tr> <td valign="top" align="middle" width="100%"> <!--start of article content --> <table width="98%" border="0"> <tbody> <tr> <td class="text" align="left" width="100%"> <br />/**<br />* <p>dExcel表格,拯、更新Excel工作薄?lt;/p><br />* <p>Description: 可以dExcel文g的内?更新Excel工作?br />* </p><br />* <p>Copyright: Copyright (c) Corparation 2005</p><br />* <p>E序开发环境ؓeclipse</p><br />* @author Walker<br />* @version 1.0<br />*/<br />package cn.com.yitong.xls;<br /><br />import java.io.File;<br />import java.io.FileInputStream;<br />import java.io.InputStream;<br />import java.util.Vector;<br /><br />import cn.com.yitong.ChartImg;<br />import cn.com.yitong.VireObj;<br />import cn.com.yitong.platform.log.YTLogger;<br /><br />import jxl.CellType;<br />import jxl.Workbook;<br />import jxl.format.CellFormat;<br />import jxl.format.Colour;<br />import jxl.format.UnderlineStyle;<br />import jxl.write.Formula;<br />import jxl.write.Label;<br />import jxl.write.Number;<br />import jxl.write.WritableCell;<br />import jxl.write.WritableCellFormat;<br />import jxl.write.WritableFont;<br />import jxl.write.WritableImage;<br />import jxl.write.WritableSheet;<br />import jxl.write.WritableWorkbook;<br />import jxl.write.WriteException;<br />import jxl.write.biff.RowsExceededException;<br /><br />public class XLSDemo<br />{<br />    private static final int TITLE_LENGTH = 7;<br />    private static final int SHEET_WIDTH = 32;<br />    private static final int SHEET_HEIGHT = 116;<br />    <br />    /**<br />     * 创徏Excel<br />     */<br />    private void makeXls()<br />    {<br />        Workbook workbook = null;<br />        try<br />        {<br />            // 构徏Workbook对象, 只读Workbook对象<br />            // 直接从本地文件创建Workbook, 从输入流创徏Workbook<br />            InputStream ins = new FileInputStream("D:/Workspace/testproj/source.xls");<br />            workbook = Workbook.getWorkbook(ins);<br /><br />            // 利用已经创徏的Excel工作薄创建新的可写入的Excel工作?br />            File outFile = new File("D:/Workspace/testproj/test.xls");<br />            WritableWorkbook wwb = Workbook.createWorkbook(outFile, workbook);<br />            // dW一张工作表<br />            WritableSheet dataSheet = wwb.getSheet(0);<br />            //  讄ȝ单元?br />            dataSheet.getSettings().setVerticalFreeze(7);<br />            dataSheet.getSettings().setHorizontalFreeze(2);<br />            <br />            // 试模拟数据<br />            Vector vecData = new Vector();<br />            for(int i = 0; i < 50; i ++)<br />            {<br />                VireObj obj = new VireObj();<br />                obj.setOrgNo("00" + i + "0");<br />                obj.setOrgName("机构" + (i + 1));<br />                obj.setOpenAcc((int)(100 * Math.random()));<br />                obj.setDestoryAcc((int)(10 * Math.random()));<br />                obj.setTotalAcc((int)(500 * Math.random()));<br />                obj.setMonthInCount((int)(500 * Math.random()));<br />                obj.setMonthInMoney(500 * Math.random());<br />                obj.setMonthOutCount((int)(500 * Math.random()));<br />                obj.setMonthOutMoney(500 * Math.random());<br />                <br />                vecData.add(obj);<br />            }            <br />            // 插入数据<br />            insertData(wwb, dataSheet, vecData);            <br />            // 插入模拟囑փ数据<br />            Vector vecImg = new Vector();<br />            for(int i = 0; i < 3; i ++)<br />            {<br />                ChartImg img = new ChartImg();<br />                img.setImgTitle("囑փ" + (i + 1));<br />                img.setImgName("D:/Workspace/testproj/images/barchart.png");<br />                vecImg.add(img);<br />            }<br />            // 插入图表<br />            insertImgsheet(wwb, vecImg);<br />            //写入Excel对象<br />            wwb.write();<br />            wwb.close();<br />        } catch (Exception e)<br />        {<br />            YTLogger.logDebug(e);<br />        } finally<br />        {<br />            // 操作完成Ӟ关闭对象Q释攑֍用的内存I间<br />            workbook.close();<br />        }<br />    }<br />    <br />    /**<br />     * 插入数据<br />     * @param wwb WritableWorkbook : 工作?br />     * @param dataSheet WritableSheet : 工作?br />     * @throws RowsExceededException<br />     * @throws WriteException<br />     */<br />    private void insertData(WritableWorkbook wwb, WritableSheet dataSheet, Vector vecData) throws RowsExceededException, WriteException<br />    {<br />        // 获得标题单元格对象       ?br />        modiStrCell(dataSheet, 2, 0, "工商银行江苏省分行 个人网上银行业务种c?开销hl报表(2005-12Q?, null);<br />        // 修改数据单元格数?br />        for(int i = 0; i < vecData.size(); i ++)<br />        {<br />            VireObj obj = (VireObj)vecData.get(i);<br />            modiStrCell(dataSheet, 0, TITLE_LENGTH + i, obj.getOrgNo(), null);<br />            modiStrCell(dataSheet, 1, TITLE_LENGTH + i, obj.getOrgName(), null);<br />            modiNumCell(dataSheet, 2, TITLE_LENGTH + i, obj.getOpenAcc(), null);<br />            modiNumCell(dataSheet, 3, TITLE_LENGTH + i, obj.getDestoryAcc(), null);<br />            modiNumCell(dataSheet, 4, TITLE_LENGTH + i, obj.getTotalAcc(), null);<br />            modiNumCell(dataSheet, 5, TITLE_LENGTH + i, obj.getMonthInCount(), null);<br />            modiNumCell(dataSheet, 6, TITLE_LENGTH + i, obj.getTotalInMoney(), null);<br />            modiNumCell(dataSheet, 7, TITLE_LENGTH + i, obj.getMonthOutCount(), null);<br />            modiNumCell(dataSheet, 8, TITLE_LENGTH + i, obj.getMonthOutMoney(), null);<br />        }    <br />        // 删除I<br />        for (int j = vecData.size() + TITLE_LENGTH; j < SHEET_HEIGHT; j++)<br />        {<br />            dataSheet.removeRow(vecData.size() + TITLE_LENGTH);<br />        }        <br />        // 插入公式<br />        for(int i = 2; i < SHEET_WIDTH; i ++)<br />        {<br />            modiFormulaCell(dataSheet, i, vecData.size() + TITLE_LENGTH, 8, vecData.size() + TITLE_LENGTH, null);<br />        }        <br />    }<br /><br />    /**<br />     * 修改字符单元格的?br />     * @param dataSheet WritableSheet : 工作?br />     * @param col int : ?br />     * @param row int : ?br />     * @param str String : 字符<br />     * @param format CellFormat : 单元格的样式<br />     * @throws RowsExceededException<br />     * @throws WriteException<br />     */<br />    private void modiStrCell(WritableSheet dataSheet, int col, int row, String str, CellFormat format) throws RowsExceededException, WriteException<br />    {<br />        // 获得单元格对?br />        WritableCell cell = dataSheet.getWritableCell(col, row);<br />        // 判断单元格的cd, 做出相应的{?br />        if (cell.getType() == CellType.EMPTY)<br />        {<br />            Label lbl = new Label(col, row, str);<br />            if(null != format)<br />            {<br />                lbl.setCellFormat(format);<br />            } else<br />            {<br />                lbl.setCellFormat(cell.getCellFormat());<br />            }<br />            dataSheet.addCell(lbl);<br />        } else if (cell.getType() == CellType.LABEL)<br />        {<br />            Label lbl = (Label)cell;<br />            lbl.setString(str);<br />        } else if (cell.getType() == CellType.NUMBER)<br />        {<br />            // 数字单元g?br />            Number n1 = (Number)cell;<br />            n1.setValue(42.05);<br />        }<br />    }<br />    <br />    /**<br />     * 修改数字单元格的?br />     * @param dataSheet WritableSheet : 工作?br />     * @param col int : ?br />     * @param row int : ?br />     * @param num double : 数?br />     * @param format CellFormat : 单元格的样式<br />     * @throws RowsExceededException<br />     * @throws WriteException<br />     */<br />    private void modiNumCell(WritableSheet dataSheet, int col, int row, double num, CellFormat format) throws RowsExceededException, WriteException<br />    {<br />        // 获得单元格对?br />        WritableCell cell = dataSheet.getWritableCell(col, row);<br />        // 判断单元格的cd, 做出相应的{?br />        if (cell.getType() == CellType.EMPTY)<br />        {<br />            Number lbl = new Number(col, row, num);<br />            if(null != format)<br />            {<br />                lbl.setCellFormat(format);<br />            } else<br />            {<br />                lbl.setCellFormat(cell.getCellFormat());<br />            }<br />            dataSheet.addCell(lbl);<br />        } else if (cell.getType() == CellType.NUMBER)<br />        {<br />            // 数字单元g?br />            Number lbl = (Number)cell;<br />            lbl.setValue(num);<br />        } else if (cell.getType() == CellType.LABEL)<br />        {<br />            Label lbl = (Label)cell;<br />            lbl.setString(String.valueOf(num));<br />        }<br />    }<br />    <br />    /**<br />     * 修改公式单元格的?br />     * @param dataSheet WritableSheet : 工作?br />     * @param col int : ?br />     * @param row int : ?br />     * @param startPos int : 开始位|?br />     * @param endPos int : l束位置<br />     * @param format<br />     * @throws RowsExceededException<br />     * @throws WriteException<br />     */<br />    private void modiFormulaCell(WritableSheet dataSheet, int col, int row, int startPos, int endPos, CellFormat format) throws RowsExceededException, WriteException<br />    {<br />        String f = getFormula(col, row, startPos, endPos);<br />        // 插入公式Q只支持插入Q不支持修改Q?br />        WritableCell cell = dataSheet.getWritableCell(col, row);<br />        if (cell.getType() == CellType.EMPTY)<br />        {                    <br />            // 公式单元?br />            Formula lbl = new Formula(col, row, f);<br />            if(null != format)<br />            {<br />                lbl.setCellFormat(format);<br />            } else<br />            {<br />                lbl.setCellFormat(cell.getCellFormat());<br />            }<br />            dataSheet.addCell(lbl);<br />        } else if (cell.getType() == CellType.STRING_FORMULA)<br />        {<br />            YTLogger.logWarn("Formula modify not supported!");<br />        }<br />    }<br />    <br />    /**<br />     * 得到公式<br />     * @param col int : ?br />     * @param row int : ?br />     * @param startPos int : 开始位|?br />     * @param endPos int : l束位置<br />     * @return String<br />     * @throws RowsExceededException<br />     * @throws WriteException<br />     */<br />    private String getFormula(int col, int row, int startPos, int endPos)<br />            throws RowsExceededException, WriteException<br />    {<br />        char base = 'A';<br />        char c1 = base;<br />        StringBuffer formula = new StringBuffer(128);<br />        // l装公式<br />        formula.append("SUM(");<br />        if (col <= 25)<br />        {<br />            c1 = (char) (col % 26 + base);<br />            formula.append(c1).append(startPos).append(":")<br />                   .append(c1).append(endPos).append(")");<br />        } else if (col > 25)<br />        {<br />            char c2 = (char) ((col - 26) / 26 + base);<br />            c1 = (char) ((col - 26) % 26 + base);<br />            formula.append(c2).append(c1).append(startPos).append(":")<br />                   .append(c2).append(c1).append(endPos).append(")");<br />        }<br /><br />        return formula.toString();<br />    }<br />    <br />    /**<br />     * 插入图表工作?br />     * @param wwb WritableWorkbook : 工作?br />     * @param vecImg Vector : 囑փ链表<br />     * @throws RowsExceededException<br />     * @throws WriteException<br />     */<br />    private void insertImgsheet(WritableWorkbook wwb, Vector vecImg)<br />            throws RowsExceededException, WriteException<br />    {<br />        // 插入囑փ<br />        WritableSheet imgSheet;<br />        if((wwb.getSheets()).length < 2)<br />        {<br />            imgSheet = wwb.createSheet("图表", 1);<br />        } else<br />        {<br />            imgSheet = wwb.getSheet(1);<br />        }<br />        <br />        for (int i = 0; i < vecImg.size(); i++)<br />        {<br />            ChartImg chart = (ChartImg) vecImg.get(i);<br />            // 插入囑փ标题<br />            Label lbl = new Label(0, 2 + 20 * i, chart.getImgTitle());<br />            WritableFont font = new WritableFont(WritableFont.ARIAL,<br />                    WritableFont.DEFAULT_POINT_SIZE, WritableFont.NO_BOLD, false,<br />                    UnderlineStyle.NO_UNDERLINE, Colour.DARK_BLUE2);<br />            WritableCellFormat background = new WritableCellFormat(font);<br />            background.setWrap(true);<br />            background.setBackground(Colour.GRAY_25);<br />            imgSheet.mergeCells(0, 2 + 20 * i, 9, 2 + 20 * i);<br />            lbl.setCellFormat(background);<br />            imgSheet.addCell(lbl);<br />            // 插入囑փ单元?br />            insertImgCell(imgSheet, 2, 4 + 20 * i, 8, 15, chart.getImgName());<br />        }<br />    }<br /><br />    /**<br />     * 插入囑փ到单元格Q图像格式只支持pngQ?br />     * @param dataSheet WritableSheet : 工作?br />     * @param col int : ?br />     * @param row int : ?br />     * @param width int : ?br />     * @param height int : ?br />     * @param imgName String : 囑փ的全路径<br />     * @throws RowsExceededException<br />     * @throws WriteException<br />     */<br />    private void insertImgCell(WritableSheet dataSheet, int col, int row, int width,<br />            int height, String imgName) throws RowsExceededException, WriteException<br />    {<br />        File imgFile = new File(imgName);<br />        WritableImage img = new WritableImage(col, row, width, height, imgFile);<br />        dataSheet.addImage(img);<br />    }<br />    <br />    /**<br />     * 试<br />     * @param args<br />     */<br />    public static void main(String[] args)<br />    {<br />        XLSDemo demo = new XLSDemo();<br />        demo.makeXls();<br />    }<br />}</td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <img src ="http://m.tkk7.com/boddi/aggbug/68696.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.tkk7.com/boddi/" target="_blank">boddi</a> 2006-09-09 12:23 <a href="http://m.tkk7.com/boddi/archive/2006/09/09/68696.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jxl实例http://m.tkk7.com/boddi/archive/2006/09/09/68686.htmlboddiboddiSat, 09 Sep 2006 03:20:00 GMThttp://m.tkk7.com/boddi/archive/2006/09/09/68686.htmlhttp://m.tkk7.com/boddi/comments/68686.htmlhttp://m.tkk7.com/boddi/archive/2006/09/09/68686.html#Feedback5http://m.tkk7.com/boddi/comments/commentRss/68686.htmlhttp://m.tkk7.com/boddi/services/trackbacks/68686.html jxl不错Q简单易?br />
import jxl.*;
import jxl.write.*;
import java.io.*;
import java.io.File.*;
import java.util.*;



public class excel
{
public static void main(String[] args)
{

String targetfile = "c:/out.xls";//输出的excel文g?br />String worksheet = "List";//输出的excel文g工作表名
String[] title = {"ID","NAME","DESCRIB"};//excel工作表的标题


WritableWorkbook workbook;
try
{
//创徏可写入的Excel工作?q行生成的文件在tomcat/bin?br />//workbook = Workbook.createWorkbook(new File("output.xls"));
System.out.println("begin");

OutputStream os=new FileOutputStream(targetfile);
workbook=Workbook.createWorkbook(os);

WritableSheet sheet = workbook.createSheet(worksheet, 0); //dW一个工作表
//WritableSheet sheet1 = workbook.createSheet("MySheet1", 1); //可添加第二个工作
/*
jxl.write.Label label = new jxl.write.Label(0, 2, "A label record"); //put a label in cell A3, Label(column,row)
sheet.addCell(label);
*/

jxl.write.Label label;
for (int i=0; i<title.length; i++)
{
//Label(列号,行号 ,内容 )
label = new jxl.write.Label(i, 0, title[i]); //put the title in row1
sheet.addCell(label);
}




//下列d的对字体{的讄均调试通过Q可作参考用


//d数字
jxl.write.Number number = new jxl.write.Number(3, 4, 3.14159); //put the number 3.14159 in cell D5
sheet.addCell(number);

//d带有字型Formatting的对?
jxl.write.WritableFont wf = new jxl.write.WritableFont(WritableFont.TIMES,10,WritableFont.BOLD,true);
jxl.write.WritableCellFormat wcfF = new jxl.write.WritableCellFormat(wf);
jxl.write.Label labelCF = new jxl.write.Label(4,4,"文本",wcfF);
sheet.addCell(labelCF);

//d带有字体颜色,带背景颜?Formatting的对?
jxl.write.WritableFont wfc = new jxl.write.WritableFont(WritableFont.ARIAL,10,WritableFont.BOLD,false,jxl.format.UnderlineStyle.NO_UNDERLINE,jxl.format.Colour.RED);
jxl.write.WritableCellFormat wcfFC = new jxl.write.WritableCellFormat(wfc);
wcfFC.setBackground(jxl.format.Colour.BLUE);
jxl.write.Label labelCFC = new jxl.write.Label(1,5,"带颜?,wcfFC);
sheet.addCell(labelCFC);

//d带有formatting的Number对象
jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##");
jxl.write.WritableCellFormat wcfN = new jxl.write.WritableCellFormat(nf);
jxl.write.Number labelNF = new jxl.write.Number(1,1,3.1415926,wcfN);
sheet.addCell(labelNF);

//3.dBoolean对象
jxl.write.Boolean labelB = new jxl.write.Boolean(0,2,false);
sheet.addCell(labelB);

//4.dDateTime对象
jxl.write.DateTime labelDT = new jxl.write.DateTime(0,3,new java.util.Date());
sheet.addCell(labelDT);

//d带有formatting的DateFormat对象
jxl.write.DateFormat df = new jxl.write.DateFormat("ddMMyyyyhh:mm:ss");
jxl.write.WritableCellFormat wcfDF = new jxl.write.WritableCellFormat(df);
jxl.write.DateTime labelDTF = new jxl.write.DateTime(1,3,new java.util.Date(),wcfDF);
sheet.addCell(labelDTF);

//和宾单元?br />//sheet.mergeCells(int col1,int row1,int col2,int row2);//左上角到右下?br />sheet.mergeCells(4,5,8,10);//左上角到右下?br />wfc = new jxl.write.WritableFont(WritableFont.ARIAL,40,WritableFont.BOLD,false,jxl.format.UnderlineStyle.NO_UNDERLINE,jxl.format.Colour.GREEN);
jxl.write.WritableCellFormat wchB = new jxl.write.WritableCellFormat(wfc);
wchB.setAlignment(jxl.format.Alignment.CENTRE);
labelCFC = new jxl.write.Label(4,5,"单元合ƈ",wchB);
sheet.addCell(labelCFC); //


//讄Ҏ(gu)
jxl.write.WritableCellFormat wcsB = new jxl.write.WritableCellFormat();
wcsB.setBorder(jxl.format.Border.ALL,jxl.format.BorderLineStyle.THICK);
labelCFC = new jxl.write.Label(0,6,"Ҏ(gu)讄",wcsB);
sheet.addCell(labelCFC);
workbook.write();
workbook.close();
}catch(Exception e)
{
e.printStackTrace();
}
System.out.println("end");
Runtime r=Runtime.getRuntime();
Process p=null;
//String cmd[]={"notepad","exec.java"};
String cmd[]={"C:\\Program Files\\Microsoft Office\\Office\\EXCEL.EXE","out.xls"};
try{
p=r.exec(cmd);
}
catch(Exception e){
System.out.println("error executing: "+cmd[0]);
}


}
}


boddi 2006-09-09 11:20 发表评论
]]>
Java中合qXML文档的合q? http://m.tkk7.com/boddi/archive/2006/09/07/68298.htmlboddiboddiThu, 07 Sep 2006 07:25:00 GMThttp://m.tkk7.com/boddi/archive/2006/09/07/68298.htmlhttp://m.tkk7.com/boddi/comments/68298.htmlhttp://m.tkk7.com/boddi/archive/2006/09/07/68298.html#Feedback0http://m.tkk7.com/boddi/comments/commentRss/68298.htmlhttp://m.tkk7.com/boddi/services/trackbacks/68298.htmlJava中合qXML文档的设计与实现
作者: 凌宗?李先?
出处Q?计算Z信息技?
责Q~辑Q?方舟
[ 2005-06-09 08:39 ]

  ??/b>Q介l了XML应用中合qXML文档的方法与应用Q在ZXML的应用中Q有着q泛的应用前景?br />
  关键?/b>QXML文档 解析?元素

  在XML应用中,最常用也最实用的莫q于XML文g的读写。由于XML语义比较严格Qv始标记必配对,所以合qXML文档q不像合q普通文仉L(fng)单。在JAVA中,如何合ƈXML文档Q下面介l一U方法?br />
  设计思想

  应用javax.xml.parsers包中的解析器解析得到两个XML文g的根元素Q再采用递归的方式逐一复制被合q文件的元素?


  实现q程

  ZdXML文gQ需?a class="bluekey" target="_blank">导入如下JAVA包,"http://"后ؓ注释说明Q笔者的环境?a class="bluekey" target="_blank">JDK 1.3.1Q在JDK 1.4.0中测试也通过?

Import java.io. *; //Java基础包,包含各种IO操作
Import java.util. *; //Java基础包,包含各种标准数据l构操作
Import javax.xml.parsers. *; //XML解析器接?
Import org.w3c.dom. *; //XML的DOM实现
import org.apache.crimson.tree.XmlDocument;//写XML文g要用?br />Import javax.xml.transform. *;
Import javax.xml.transform.dom. *;
Import javax.xml.transform.stream. *;

  下面介绍合ƈXML文档的过E。先说明一下各个方法的作用。方?a class="bluekey" target="_blank">is Merging()有两?a class="bluekey" target="_blank">参数Q分别是目标XML文g?/a>和被合ƈ的XML文g名)Q调用JAVA的解析器Q获得两个要合ƈ的XML文档的Documentl构和根元素Qƈ调用Ҏ(gu)duplicateQ)和方法write To()。当Ӟ在XML文档的合q过E中Q可以加入另外的一些判断条Ӟ比如Q当被合qXML文档不存在时Q将如何处理Q等{?br />
Private Boolean is Merging (String mainFileName, String sub Filename) throws Exception {
 Boolean isOver = false;
 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 Document Builder db = null;
 Try {
  Db = dbf.newDocumentBuilder ();
 } Catch (ParserConfigurationException pce) {
  System.err.println(pce); //出现异常Ӟ输出异常信息
 }
 Document doc_main = null,doc_vice = null;
 //获取两个XML文g的Document?br /> Try {
  Doc_main = db.parse (mainFileName);
  Doc_vice = db.parse (sub Filename);
 } Catch (DOM Exception dom) {
  System.err.println (dom.getMessage ());
 } Catch (Exception ioe) {
  System.err.println (ioe);
 }
 //获取两个文g的根元素?br /> Element root_main = doc_main.getDocumentElement ();
 Element root_vice = doc_vice.getDocumentElement ();
 //下面d被合q文件根节点下的每个元素
 Novelist message Items = root_vice.getChildNodes ();
 Int item_number = messageItems.getLength ();
 //如果L根节点下的第一个元素,比如<所属管理系l?gt; Q那么i?开始。否则i?开始?br /> For (int i=1; i < item_number; i=i+2 ) {
  //调用dupliate()Q依ơ复制被合ƈXML文档中根节点下的元素?
  Element messageItem = (Element) messageItems.item (i);
  IsOver = dupliate (doc_main, root_main, messageItem);
 }
 //调用 write To()Q将合ƈ得到的Document写入目标XML文档?br /> Boolean isWritten = write To (doc_main, mainFileName);
 Return isOver && isWritten;
}

  Ҏ(gu)dupliate ()有三个参敎ͼ分别是目标XML文档的DocumentQ目标XML文档中要d节点的父节点和被合ƈXML文档的复制节点)Q采用递归的Ş式,一个XML文档中的元素复制到另一个XML文档中?br />
Private Boolean dupliate (Document doc_dup, Element father, Element son) throws Exception {
 Boolean is done = false;
 String son_name = son.getNodeName ();
 Element sub ITEM = doc_dup.createElement (son_name);
 //复制节点的属?br /> If (son.hasAttributes ()){
  NamedNodeMap attributes = son.getAttributes ();
  For (int i=0; i < attributes.getLength () ; i ++){
   String attribute_name = attributes. Item (i). GetNodeName ();
   String attribute_value = attributes. Item (i). GetNodeValue ();
   SubITEM.setAttribute (attribute_name, attribute_value);
  }
 }
 Father.appendChild (sub ITEM);
 //复制节点的?br /> Text value son = (Text) son.getFirstChild ();
 String nodevalue_root = "";
 If (value_son! = null && value_son.getLength () > 0) nodevalue_root = (String) value_son.getNodeValue ();
 Text valuenode_root = null;
 If ((nodevalue_root! = null)&&(nodevalue_root.length () > 0)) valuenode_root = doc_dup.createTextNode (nodevalue_root);
 If (valuenode_root! = null && valuenode_root.getLength () > 0) subITEM.appendChild (valuenode_root);
 //复制子结?br /> Novelist sub_messageItems = son.getChildNodes ();
 int sub_item_number = sub_messageItems.getLength();
 if (sub_item_number < 2){
  //如果没有子节?则返?br />  Is done = true;
 }
 Else {
  For (int j = 1; j < sub_item_number; j=j+2) {
   //如果有子节点,则递归调用本方?
   Element sub_messageItem = (Element) sub_messageItems.item (j);
   Is done = dupliate (doc_dup, subITEM, sub_messageItem);
  }
 }
 Return is done;
}

  Ҏ(gu)writeTo()有两个参敎ͼ分别是目标XML文档的Document和文件名Q,所得目标XML文档写入文g?br />
Private Boolean write To (Document doc, String fileName) throws Exception {
 Boolean isOver = false;
 DOM Source doms = new DOM Source (doc);
 File f = new File (fileName);
 Stream Result sr = new Stream Result (f);
 Try
 {
  Transformer Factory tf=TransformerFactory.newInstance ();
  Transformer t=tf.newTransformer ();
  Properties properties = t.getOutputProperties ();
  Properties.setProperty (OutputKeys.ENCODING,"GB2312");
  T.setOutputProperties (properties);
  T.transform (doms, sr);
  IsOver = true;
 }
 Catch (TransformerConfigurationException tce)
 {
  Tce.printStackTrace ();
 }
 Catch (Transformer Exception te)
 {
  Te.printStackTrace ();
 }
 Return isOver;
}

  最后用测试函数进行测试。对于两个已l存在的XML文gQ比如,存在文gD:/a.xml和D:/b.xmlQ要b.xml合ƈ到a.xml中)Q可以测试如下:

Public static void main (String [] args) throws Exception {
 Boolean is done = is Merging ("D:/a.xml","D:/b.xml");
 If (is Done) System.out.println ("XML files have been merged.");
 Else System.out.println ("XML files have NOT been merged.");
}

  ȝ

  本文介绍了如何利用JAVA中的XML解析器,合ƈ两个XML文档。当Ӟ在合q的q程中,q可以加入其他的U束条gQ比如要求过滤掉特定的元素等。另外,复制元素的插入位|也可以加以限制?br />


boddi 2006-09-07 15:25 发表评论
]]>
在应用中加入全文索功?/title><link>http://m.tkk7.com/boddi/archive/2006/09/06/68017.html</link><dc:creator>boddi</dc:creator><author>boddi</author><pubDate>Wed, 06 Sep 2006 05:34:00 GMT</pubDate><guid>http://m.tkk7.com/boddi/archive/2006/09/06/68017.html</guid><wfw:comment>http://m.tkk7.com/boddi/comments/68017.html</wfw:comment><comments>http://m.tkk7.com/boddi/archive/2006/09/06/68017.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.tkk7.com/boddi/comments/commentRss/68017.html</wfw:commentRss><trackback:ping>http://m.tkk7.com/boddi/services/trackbacks/68017.html</trackback:ping><description><![CDATA[ <h1>在应用中加入全文索功?br />    ——基于Java的全文烦引引擎Lucene?</h1> <p>作者: 车东 Email: chedongATbigfoot.com/chedongATchedong.com</p> <p>写于Q?002/08 最后更斎ͼ <script language="Javascript" src="http://www.chedong.com/referer.js"></script> 02/22/2006 14:42:55<br /><a >Feed Back >></a> (<a >Read this before you ask question</a>)<a ><br /></a></p> <p>版权声明Q可以Q意{载,转蝲时请务必以超链接形式标明文章原始出处和作者信息及本声?br /><a >http://www.chedong.com/tech/lucene.html</a></p> <p>关键词:Lucene java full-text search engine Chinese word segment</p> <p> <span style="FONT-WEIGHT: bold"> </span>内容摘要Q?/p> <p>Lucene是一个基于Java的全文烦引工具包?/p> <ol> <li> <a >ZJava的全文烦引引擎Lucene介:关于作者和Lucene的历?/a> </li> <li> <a >全文索的实现QLuene全文索引和数据库索引的比?/a> </li> <li> <a >中文切分词机制简介:Z词库和自动切分词法的比?/a> </li> <li> <a >具体的安装和使用介:pȝl构介绍和演C?/a> </li> <li> <a >Hacking LuceneQ简化的查询分析器,删除的实玎ͼ定制的排序,应用接口的扩?/a> </li> <li> <a >从Lucene我们q可以学C?/a> </li> </ol> <p> <a name="intro"> <b>ZJava的全文烦?索引擎——Lucene</b> </a> </p> <p>Lucene不是一个完整的全文索引应用Q而是是一个用Java写的全文索引引擎工具包,它可以方便的嵌入到各U应用中实现针对应用的全文烦?索功能?/p> <p>Lucene的作者:Lucene的A(ch)献?a >Doug Cutting</a>是一位资深全文烦?索专Ӟ曄是V-Twin搜烦引擎(Apple的Copland操作pȝ的成׃一)的主要开发者,后在Excite担Q高pȝ架构设计师,目前从事于一些INTERNET底层架构的研I。他贡献出的Lucene的目标是为各U中型应用E序加入全文索功能?/p> <p>Lucene的发展历E:早先发布在作者自q<a >www.lucene.com</a>Q后来发布在<a >SourceForge</a>Q?001q年底成为APACHE基金会jakarta的一个子目Q?a >http://jakarta.apache.org/lucene/</a></p> <p>已经有很多Java目都用了Lucene作ؓ其后台的全文索引引擎Q比较著名的有:</p> <ul> <li> <a >J</a> <a >ive</a>QWEB论坛pȝQ? </li> <li> <a >Eyebrows</a>Q邮件列表HTML归档/览/查询pȝQ本文的主要参考文档?a >TheLucene search engine: Powerful, flexible, and free</a>”作者就是EyeBrowspȝ的主要开发者之一Q而EyeBrows已经成ؓ目前APACHE目的主要邮件列表归档系l? </li> <li> <a >Cocoon</a>:ZXML的web发布框架Q全文检索部分用了Lucene </li> <li> <p align="left"> <a >Eclipse</a>:ZJava的开攑ּ发^収ͼ帮助部分的全文烦引用了Lucene</p> </li> </ul> <p>对于中文用户来说Q最兛_的问题是其是否支持中文的全文索。但通过后面对于Lucene的结构的介绍Q你会了解到׃Lucene良好架构设计Q对中文的支持只需对其语言词法分析接口q行扩展p实现对中文检索的支持?/p> <p> <b> <a name="compare">全文索的实现机制</a> </b> </p> <p>Lucene的API接口设计的比较通用Q输入输出结构都很像数据库的?=>记录==>字段Q所以很多传l的应用的文件、数据库{都可以比较方便的映到Lucene的存储结?接口中。M上看Q可以先?b>Lucene当成一个支持全文烦引的数据库系l?/b>?/p> <p>比较一下Lucene和数据库Q?/p> <table width="100%" border="1"> <tbody> <tr> <td align="middle" width="50%">Lucene</td> <td align="middle" width="50%">数据?/td> </tr> <tr> <td width="50%"> <pre>索引数据源:doc(field1,field2...) doc(field1,field2...)<br /> \ indexer /<br /> _____________<br /> | Lucene Index|<br /> --------------<br /> / searcher \<br /> l果输出QHits(doc(field1,field2) doc(field1...))</pre> </td> <td width="50%"> <pre> 索引数据源:record(field1,field2...) record(field1..)<br /> \ SQL: insert/<br /> _____________<br /> | DB Index |<br /> -------------<br /> / SQL: select \<br />l果输出Qresults(record(field1,field2..) record(field1...))</pre> </td> </tr> <tr> <td width="50%">DocumentQ一个需要进行烦引的“单元?br />一个Document由多个字D늻?/td> <td width="50%">RecordQ记录,包含多个字段</td> </tr> <tr> <td width="50%">FieldQ字D?/td> <td width="50%">FieldQ字D?/td> </tr> <tr> <td width="50%">HitsQ查询结果集Q由匚w的Documentl成</td> <td width="50%">RecordSetQ查询结果集Q由多个Recordl成</td> </tr> </tbody> </table> <p> <b>全文??like "%keyword%"</b> </p> <p>通常比较厚的书籍后面常常附关键词索引表(比如Q北京:12, 34,上vQ?,77……)Q它能够帮助读者比较快地找到相兛_容的늠。而数据库索引能够大大提高查询的速度原理也是一P惛_一下通过书后面的索引查找的速度要比一一地d定w多少倍……而烦引之所以效率高Q另外一个原因是它是排好序的?b>对于索系l来说核心是一个排序问?/b>?/p> <p align="left">׃数据库烦引不是ؓ全文索引设计的,因此Q?b>使用like "%keyword%"Ӟ数据库烦引是不v作用?/b>Q在使用like查询Ӟ搜烦q程又变成类g一页M的遍历过E了Q所以对于含有模p查询的数据库服务来_LIKEҎ(gu)能的危x极大的。如果是需要对多个关键词进行模p匹配:like"%keyword1%" and like "%keyword2%" ...其效率也可惌知了?/p> <p>所以徏立一个高效检索系l的关键是徏立一个类gU技索引一L(fng)反向索引机制Q将数据源(比如多篇文章Q排序顺序存储的同时Q有另外一个排好序的关键词列表Q用于存储关键词==>文章映射关系Q利用这L(fng)映射关系索引Q[关键?=>出现关键词的文章~号Q出现次敎ͼ甚至包括位置Qv始偏U量Q结束偏U量Q,出现频率]Q检索过E就是把<b>模糊查询变成多个可以利用索引的精查询的逻辑l合的过E?/b>。从而大大提高了多关键词查询的效率,所以,全文索问题归l到最后是一个排序问题?/p> <p>由此可以看出模糊查询相对数据库的_查询是一个非怸定的问题,q也是大部分数据库对全文索支持有限的原因。Lucene最核心的特征是通过Ҏ(gu)的烦引结构实C传统数据库不擅长的全文烦引机Ӟq提供了扩展接口Q以方便针对不同应用的定制?/p> <p>可以通过一下表格对比一下数据库的模p查询:</p> <table height="283" width="100%" border="1"> <tbody> <tr> <td align="middle" width="9%" height="16"> </td> <td align="middle" width="47%" height="16">Lucene全文索引引擎</td> <td align="middle" width="40%" height="16">数据?/td> </tr> <tr> <td width="9%" height="48">索引</td> <td width="47%" height="48">数据源中的数据都通过全文索引一一建立反向索引</td> <td width="40%" height="48">对于LIKE查询来说Q数据传l的索引是根本用不上的。数据需要逐个便利记录q行GREP式的模糊匚wQ比有烦引的搜烦速度要有多个数量U的下降?/td> </tr> <tr> <td width="9%" height="49">匚w效果</td> <td width="47%" height="49">通过词元(term)q行匚wQ通过语言分析接口的实玎ͼ可以实现对中文等非英语的支持?/td> <td width="40%" height="49">使用Qlike "%net%" 会把netherlands也匹配出来,<br />多个关键词的模糊匚wQ用like "%com%net%"Q就不能匚w词序颠倒的xxx.net..xxx.com</td> </tr> <tr> <td width="9%" height="32">匚w?/td> <td width="47%" height="32">有匹配度法Q将匚wE度Q相似度Q比较高的结果排在前面?/td> <td width="40%" height="32">没有匚wE度的控Ӟ比如有记录中net出现5词和出现1ơ的Q结果是一L(fng)?/td> </tr> <tr> <td width="9%" height="32">l果输出</td> <td width="47%" height="32">通过特别的算法,最匚w度最高的?00条结果输出,l果集是~冲式的批量读取的?/td> <td width="40%" height="32">q回所有的l果集,在匹配条目非常多的时候(比如上万条)需要大量的内存存放q些临时l果集?/td> </tr> <tr> <td width="9%" height="32">可定制?/td> <td width="47%" height="32">通过不同的语a分析接口实现Q可以方便的定制出符合应用需要的索引规则Q包括对中文的支持)</td> <td width="40%" height="32">没有接口或接口复杂,无法定制</td> </tr> <tr> <td width="9%" height="32">l论</td> <td width="47%" height="32">高负载的模糊查询应用Q需要负责的模糊查询的规则,索引的资料量比较?/td> <td width="40%" height="32">使用率低Q模p匹配规则简单或者需要模p查询的资料量少</td> </tr> </tbody> </table> <p> <span style="FONT-WEIGHT: bold">全文索和数据库应用最大的不同在于Q让</span> <span style="FONT-WEIGHT: bold">最相关?/span> <span style="FONT-WEIGHT: bold">?00条结果满?8%以上用户的需?br /></span> <br />Lucene的创C处:</p> <p>大部分的搜烦Q数据库Q引擎都是用B?wi)结构来l护索引Q烦引的更新会导致大量的IO操作QLucene在实CQ对此稍微有所改进Q不是维护一个烦引文Ӟ而是在扩展烦引的时候不断创建新的烦引文Ӟ然后定期的把q些新的烦引文件合q到原先的大索引中(针对不同的更新策略,Ҏ(gu)的大可以调_Q这样在不媄响检索的效率的前提下Q提高了索引的效率?/p> <p>Lucene和其他一些全文检索系l?应用的比较:</p> <table width="100%" border="1"> <tbody> <tr> <td align="middle" width="18%"> </td> <td align="middle" width="45%">Lucene</td> <td align="middle" width="37%">其他开源全文检索系l?/td> </tr> <tr> <td width="18%">增量索引和批量烦?/td> <td width="45%">可以q行增量的烦?Append)Q可以对于大量数据进行批量烦引,q且接口设计用于优化扚w索引和小扚w的增量烦引?/td> <td width="37%">很多pȝ只支持批量的索引Q有时数据源有一点增加也需要重建烦引?/td> </tr> <tr> <td width="18%">数据?/td> <td width="45%">Lucene没有定义具体的数据源Q而是一个文档的l构Q因此可以非常灵zȝ适应各种应用Q只要前端有合适的转换器把数据源{换成相应l构Q,</td> <td width="37%">很多pȝ只针对网,~Z其他格式文档的灵zL?/td> </tr> <tr> <td width="18%">索引内容抓取</td> <td width="45%">Lucene的文档是由多个字D늻成的Q甚臛_以控刉些字D需要进行烦引,那些字段不需要烦引,q一步烦引的字段也分为需要分词和不需要分词的cdQ?br />   需要进行分词的索引Q比如:标题Q文章内容字D?br />   不需要进行分词的索引Q比如:作?日期字段</td> <td width="37%">~Z通用性,往往文档整个烦引了</td> </tr> <tr> <td width="18%">语言分析</td> <td width="45%">通过语言分析器的不同扩展实现Q?br />可以qo掉不需要的词:an the of {,<br />西文语法分析Q将jumps jumped jumper都归l成jumpq行索引/?br />非英文支持:对亚z语aQ阿拉伯语言的烦引支?/td> <td width="37%">~Z通用接口实现</td> </tr> <tr> <td width="18%">查询分析</td> <td width="45%">通过查询分析接口的实玎ͼ可以定制自己的查询语法规则:<br />比如Q?多个关键词之间的 + - and or关系{?/td> <td width="37%"> </td> </tr> <tr> <td width="18%">q发讉K</td> <td width="45%">能够支持多用L(fng)使用</td> <td width="37%"> </td> </tr> </tbody> </table> <p> </p> <p> <b> <a name="segment">关于亚洲语言的的切分词问?Word Segment)</a> </b> </p> <p>对于中文来说Q全文烦引首先还要解决一个语a分析的问题,对于英文来说Q语句中单词之间是天焉过I格分开的,但亚z语a的中日韩文语句中的字是一个字挨一个,所有,首先要把语句中按“词”进行烦引的话,q个词如何切分出来就是一个很大的问题?/p> <p>首先Q肯定不能用单个字符?si-gram)为烦引单元,否则查“上”时Q不能让含有“v上”也匚w?/p> <p>但一句话Q“北京天安门”,计算机如何按照中文的语言?fn)惯q行切分呢?<br />“北?天安门?q是“北 ?天安门”?让计机能够按照语言?fn)惯q行切分Q往往需要机器有一个比较丰富的词库才能够比较准的识别句中的单词?/p> <p>另外一个解决的办法是采用自动切分算法:单词按?元语?bigram)方式切分出来Q比如:<br />"北京天安? ==> "北京 京天 天安 安门"?/p> <p>q样Q在查询的时候,无论是查?北京" q是查询"天安?Q将查询词组按同L(fng)规则q行切分Q?北京"Q?天安安门"Q多个关键词之间按与"and"的关pȝ合,同样能够正确地映到相应的烦引中。这U方式对于其他亚z语aQ韩文,日文都是通用的?/p> <p>Z自动切分的最大优Ҏ(gu)没有词表l护成本Q实现简单,~点是烦引效率低Q但对于中小型应用来_Z2元语法的切分q是够用的。基?元切分后的烦引一般大和源文件差不多Q而对于英文,索引文g一般只有原文g?0%-40%不同Q?/p> <table height="68" width="100%" border="1"> <tbody> <tr> <td align="middle" width="11%" height="18"> <br /> </td> <td align="middle" width="39%" height="18">自动切分</td> <td align="middle" width="50%" height="18">词表切分</td> </tr> <tr> <td width="11%" height="16">实现</td> <td width="39%" height="16">实现非常?/td> <td width="50%" height="16">实现复杂</td> </tr> <tr> <td width="11%" height="16">查询</td> <td width="39%" height="16">增加了查询分析的复杂E度Q?/td> <td width="50%" height="16">适于实现比较复杂的查询语法规?/td> </tr> <tr> <td width="11%" height="16">存储效率</td> <td width="39%" height="16">索引冗余大,索引几乎和原文一样大</td> <td width="50%" height="16">索引效率高,为原文大的30Q左?/td> </tr> <tr> <td width="11%" height="16">l护成本</td> <td width="39%" height="16">无词表维护成?/td> <td width="50%" height="16">词表l护成本非常高:中日韩等语言需要分别维护?br />q需要包括词频统计等内容</td> </tr> <tr> <td width="11%" height="16">适用领域</td> <td width="39%" height="16">嵌入式系l:q行环境资源有限<br />分布式系l:无词表同步问?br />多语a环境Q无词表l护成本</td> <td width="50%" height="16">Ҏ(gu)询和存储效率要求高的专业搜烦引擎<br /></td> </tr> </tbody> </table> <p>目前比较大的搜烦引擎的语a分析法一般是Z以上2个机制的l合。关于中文的语言分析法Q大家可以在Google查关键词"wordsegment search"能找到更多相关的资料?/p> <p> <a name="demo"> <b>安装和?/b> </a> </p> <p>下蝲Q?a >http://jakarta.apache.org/lucene/</a></p> <p>注意QLucene中的一些比较复杂的词法分析是用JavaCC生成的(JavaCCQJavaCompilerCompilerQ纯Java的词法分析生成器Q,所以如果从源代码编译或需要修改其中的QueryParser、定制自q词法分析器,q需要从<a >https://javacc.dev.java.net/</a>下蝲javacc?/p> <p>lucene的组成结构:对于外部应用来说索引模块(index)和检索模?search)是主要的外部应用入口</p> <table width="100%" border="1"> <tbody> <tr> <td width="27%">org.apache.Lucene.search/</td> <td width="73%">搜烦入口</td> </tr> <tr> <td width="27%">org.apache.Lucene.index/</td> <td width="73%">索引入口</td> </tr> <tr> <td width="27%">org.apache.Lucene.analysis/</td> <td width="73%">语言分析?/td> </tr> <tr> <td width="27%">org.apache.Lucene.queryParser/</td> <td width="73%">查询分析?/td> </tr> <tr> <td width="27%">org.apache.Lucene.document/</td> <td width="73%">存储l构</td> </tr> <tr> <td width="27%">org.apache.Lucene.store/ </td> <td width="73%">底层IO/存储l构</td> </tr> <tr> <td width="27%">org.apache.Lucene.util/</td> <td width="73%">一些公用的数据l构</td> </tr> </tbody> </table> <p>单的例子演示一下Lucene的用方法:</p>索引q程Q从命o行读取文件名Q多个)Q将文g分\?path字段)和内?body字段)2个字D进行存储,q对内容q行全文索引Q烦引的单位是Document对象Q每个Document对象包含多个字段Field对象Q针对不同的字段属性和数据输出的需求,对字D还可以选择不同的烦?存储字段规则Q列表如下: <table border="1"><tbody><tr><th>Ҏ(gu)</th><th>切词</th><th>索引</th><th>存储</th><th>用?/th></tr><tr><td>Field.Text(String name, String value)</td><td>Yes</td><td>Yes</td><td>Yes</td><td valign="top">切分词烦引ƈ存储Q比如:标题Q内容字D?/td></tr><tr><td>Field.Text(String name, Reader value)</td><td>Yes</td><td>Yes</td><td>No</td><td valign="top">切分词烦引不存储Q比如:META信息Q?br />不用于返回显C,但需要进行检索内?/td></tr><tr><td>Field.Keyword(String name, String value)</td><td>No</td><td>Yes</td><td>Yes</td><td valign="top">不切分烦引ƈ存储Q比如:日期字段</td></tr><tr><td>Field.UnIndexed(String name, String value)</td><td>No</td><td>No</td><td>Yes</td><td valign="top">不烦引,只存储,比如Q文件\?/td></tr><tr><td>Field.UnStored(String name, String value)</td><td>Yes</td><td>Yes</td><td>No</td><td valign="top">只全文烦引,不存?/td></tr></tbody></table><pre>public class IndexFiles { <br /> //使用Ҏ(gu)Q? IndexFiles [索引输出目录] [索引的文件列表] ... <br /> public static void main(String[] args) throws Exception {<br /> String indexPath = args[0];<br /> IndexWriter writer;<br /> //用指定的语言分析器构造一个新的写索引器(W?个参数表C是否ؓq加索引Q?br /> writer = new IndexWriter(indexPath, new SimpleAnalyzer(), false);<br /><br /> for (int i=1; i<args.length; i++) {<br /> System.out.println("Indexing file " + args[i]);<br /> InputStream is = new FileInputStream(args[i]);<br /><br /> //构造包?个字DField的Document对象<br /> //一个是路径path字段Q不索引Q只存储<br /> //一个是内容body字段Q进行全文烦引,q存?br /> Document doc = new Document();<br /> doc.add(Field.UnIndexed("path", args[i]));<br /> doc.add(Field.Text("body", (Reader) new InputStreamReader(is)));<br /> //文档写入烦?br /> writer.addDocument(doc);<br /> is.close();<br /> };<br /> //关闭写烦引器<br /> writer.close();<br /> }<br />}<br /> </pre><p>索引q程中可以看刎ͼ</p><ul><li>语言分析器提供了抽象的接口,因此语言分析(Analyser)是可以定制的Q虽然lucene~省提供?个比较通用的分析器SimpleAnalyser和StandardAnalyserQ这2个分析器~省都不支持中文Q所以要加入对中文语a的切分规则,需要修改这2个分析器? </li><li>Luceneq没有规定数据源的格式,而只提供了一个通用的结构(Document对象Q来接受索引的输入,因此输入的数据源可以是:数据库,W(xu)ORD文档QPDF文档QHTML文档……只要能够设计相应的解析转换器将数据源构造成成Docuement对象卛_q行索引? </li><li>对于大批量的数据索引Q还可以通过调整IndexerWrite的文件合q率属性(mergeFactorQ来提高扚w索引的效率?</li></ul><p>索过E和l果昄Q?/p><p>搜烦l果q回的是Hits对象Q可以通过它再讉KDocument==>Field中的内容?/p><p>假设Ҏ(gu)body字段q行全文索,可以查询结果的path字段和相应查询的匚w?score)打印出来Q?/p><pre>public class Search { <br /> public static void main(String[] args) throws Exception {<br /> String indexPath = args[0], queryString = args[1];<br /> //指向索引目录的搜索器<br /> Searcher searcher = new IndexSearcher(indexPath);<br /> //查询解析器:使用和烦引同L(fng)语言分析?br /> Query query = QueryParser.parse(queryString, "body", <br /> new SimpleAnalyzer());<br /> //搜烦l果使用Hits存储<br /> Hits hits = searcher.search(query);<br /> //通过hits可以讉K到相应字D늚数据和查询的匚w?br /> for (int i=0; i<hits.length(); i++) {<br /> System.out.println(hits.doc(i).get("path") + "; Score: " + <br /> hits.score(i));<br /> };<br /> }<br />}</pre>在整个检索过E中Q语a分析器,查询分析器,甚至搜烦器(SearcherQ都是提供了抽象的接口,可以Ҏ(gu)需要进行定制? <p><b><a name="hacking">Hacking Lucene</a></b></p><p><b>化的查询分析?/b></p><p>个h感觉lucene成ؓJAKARTA目后,d了太多的旉用于调试日趋复杂QueryParserQ而其中大部分是大多数用户q不很熟(zhn)的Q目前LUCENE支持的语法:</p><p>Query ::= ( Clause )*<br />Clause ::= ["+", "-"] [<TERM> ":"] ( <TERM> | "(" Query ")")</p><p>中间的逻辑包括Qand or + - &&||{符P而且q有"短语查询"和针对西文的前缀/模糊查询{,个h感觉对于一般应用来_q些功能有一些华而不实,其实能够实现目前cM于Google的查询语句分析功能其实对于大多数用户来说已经够了。所以,Lucene早期版本的QueryParser仍是比较好的选择?/p><p><b>d修改删除指定记录QDocumentQ?/b></p><p>Lucene提供了烦引的扩展机制Q因此烦引的动态扩展应该是没有问题的,而指定记录的修改也似乎只能通过记录的删除,然后重新加入实现。如何删除指定的记录呢?删除的方法也很简单,只是需要在索引时根据数据源中的记录ID专门另徏索引Q然后利用IndexReader.delete(Termterm)Ҏ(gu)通过q个记录ID删除相应的Document?/p><p><b>Ҏ(gu)某个字段值的排序功能</b></p><p>lucene~省是按照自q相关度算法(scoreQ进行结果排序的Q但能够Ҏ(gu)其他字段q行l果排序是一个在LUCENE的开发邮件列表中l常提到的问题,很多原先Z数据库应用都需要除了基于匹配度QscoreQ以外的排序功能。而从全文索的原理我们可以了解刎ͼM不基于烦引的搜烦q程效率都会D效率非常的低Q如果基于其他字D늚排序需要在搜烦q程中访问存储字D,速度回大大降低,因此非常是不可取的?/p><p>但这里也有一个折中的解决Ҏ(gu)Q在搜烦q程中能够媄响排序结果的只有索引中已l存储的docID和scoreq?个参敎ͼ所以,Zscore以外的排序,其实可以通过数据源预先排好序,然后Ҏ(gu)docIDq行排序来实现。这样就避免了在LUCENE搜烦l果外对l果再次q行排序和在搜烦q程中访问不在烦引中的某个字D倹{?/p><p>q里需要修改的是IndexSearcher中的HitCollectorq程Q?/p><pre>...<br /> scorer.score(new HitCollector() {<br /> private float minScore = 0.0f;<br /> public final void collect(int doc, float score) {<br /> if (score > 0.0f && // ignore zeroed buckets<br /> (bits==null || bits.get(doc))) { // skip docs not in bits<br /> totalHits[0]++;<br /> if (score >= minScore) {<br /> /* 原先QLucenedocID和相应的匚w度score例入l果命中列表中:<br /> * hq.put(new ScoreDoc(doc, score)); // update hit queue<br /> * 如果用doc ?1/doc 代替 scoreQ就实现了根据docID排或逆排<br /> * 假设数据源烦引时已经按照某个字段排好了序Q而结果根据docID排序也就实现?br /> * 针对某个字段的排序,甚至可以实现更复杂的score和docID的拟合?br /> */<br /> hq.put(new ScoreDoc(doc, (float) 1/doc )); <br /> if (hq.size() > nDocs) { // if hit queue overfull<br /> hq.pop(); // remove lowest in hit queue<br /> minScore = ((ScoreDoc)hq.top()).score; // reset minScore<br /> }<br /> }<br /> }<br /> }<br /> }, reader.maxDoc());</pre><p><b>更通用的输入输出接?/b></p><p>虽然lucene没有定义一个确定的输入文档格式Q但来多的h惛_使用一个标准的中间格式作ؓLucene的数据导入接口,然后其他数据Q比如PDF只需要通过解析器{换成标准的中间格式就可以q行数据索引了。这个中间格式主要以XMLZQ类似实现已l不?Q?个:</p><pre>数据? WORD PDF HTML DB other<br /> \ | | | /<br /> XML中间格式<br /> |<br /> Lucene INDEX</pre><p>目前q没有针对MSWord文档的解析器Q因为Word文档和基于ASCII的RTF文档不同Q需要用COM对象机制解析。这个是我在Google上查的相兌料:<a >http://www.intrinsyc.com/products/enterprise_applications.asp</a><br />另外一个办法就是把Word文档转换成textQ?a >http://www.winfield.demon.nl/index.html</a><br /></p><p><br /><b>索引q程优化</b></p><p>索引一般分2U情况,一U是批量的索引扩展Q一U是大批量的索引重徏。在索引q程中,q不是每ơ新的DOC加入q去索引都重新进行一ơ烦引文件的写入操作Q文件I/O是一仉常消耗资源的事情Q?/p><p>Lucene先在内存中进行烦引操作,q根据一定的扚wq行文g的写入。这个批ơ的间隔大Q文件的写入ơ数少Q但占用内存会很多。反之占用内存少Q但文gIO操作频繁Q烦引速度会很慢。在IndexWriter中有一个MERGE_FACTOR参数可以帮助你在构造烦引器后根据应用环境的情况充分利用内存减少文g的操作。根据我的用经验:~省Indexer是每20条记录烦引后写入一ơ,每将MERGE_FACTOR增加50倍,索引速度可以提高1倍左叟?br /></p><p><span style="FONT-WEIGHT: bold">搜烦q程优化<br /></span></p><p><span style="FONT-WEIGHT: bold"></span>lucene支持内存索引Q这L(fng)搜烦比基于文件的I/O有数量的速度提升?br /><a >http://www.onjava.com/lpt/a/3273</a><br />而尽可能减少IndexSearcher的创建和Ҏ(gu)索结果的前台的缓存也是必要的?br /><span style="FONT-WEIGHT: bold"></span></p><p><b></b></p><p>Lucene面向全文索的优化在于首次索引索后Qƈ不把所有的记录QDocumentQ具体内容读取出来,而v只将所有结果中匚w度最高的?00条结果(TopDocsQ的ID攑ֈl果集缓存中q返回,q里可以比较一下数据库索:如果是一?0,000条的数据库检索结果集Q数据库是一定要把所有记录内定w取得以后再开始返回给应用l果集的。所以即使检索匹配L很多QLucene的结果集占用的内存空间也不会很多。对于一般的模糊索应用是用不到这么多的结果的Q头100条已l可以满?0%以上的检索需求?br /></p><p>如果首批~存l果数用完后q要d更后面的l果时Searcher会再ơ检索ƈ生成一个上ơ的搜烦~存数大1倍的~存Qƈ再重新向后抓取。所以如果构造一个SearcherL1Q?20条结果,Searcher其实是进行了2ơ搜索过E:?00条取完后Q缓存结果用完,Searcher重新索再构造一?00条的l果~存Q依此类推,400条缓存,800条缓存。由于每ơSearcher对象消失后,q些~存也访问那不到了,你有可能惛_l果记录~存下来Q缓存数量保证?00以下以充分利用首ơ的l果~存Q不让Lucene费多次索,而且可以分q行l果~存?br /></p><p>Lucene的另外一个特Ҏ(gu)在收集结果的q程中将匚w度低的结果自动过滤掉了。这也是和数据库应用需要将搜烦的结果全部返回不同之处?/p><p><a >我的一些尝?/a>Q?/p><ul><li>支持中文的TokenizerQ这里有2个版本,一个是通过JavaCC生成的,对CJK部分按一个字W一个TOKEN索引Q另外一个是从SimpleTokenizer改写的,对英文支持数字和字母TOKENQ对中文按P代烦引? </li><li>ZXML数据源的索引器:XMLIndexerQ因此所有数据源只要能够按照DTD转换成指定的XMLQ就可以用XMLIndxerq行索引了? </li><li>Ҏ(gu)某个字段排序Q按记录索引序排序l果的搜索器QIndexOrderSearcherQ因此如果需要让搜烦l果Ҏ(gu)某个字段排序Q可以让数据源先按某个字D|好序Q比如:PriceFieldQ,q样索引后,然后在利用这个按记录的ID序索的搜烦器,l果是相当于是那个字段排序的结果了?</li></ul><p><a name="learn"><b>从Lucene学到更多</b></a></p><p>Luene的确是一个面对对象设计的典范</p><ul><li>所有的问题都通过一个额外抽象层来方便以后的扩展和重用:你可以通过重新实现来达到自q目的Q而对其他模块而不需要; </li><li>单的应用入口Searcher, IndexerQƈ调用底层一pdlg协同的完成搜索Q务; </li><li>所有的对象的Q务都非常专一Q比如搜索过E:QueryParser分析查询语句{换成一pd的精查询的l合(Query),通过底层的烦引读取结构IndexReaderq行索引的读取,q用相应的打分器l搜索结果进行打?排序{。所有的功能模块原子化程度非帔RQ因此可以通过重新实现而不需要修改其他模块。? </li><li>除了灉|的应用接口设计,Luceneq提供了一些适合大多数应用的语言分析器实玎ͼSimpleAnalyser,StandardAnalyserQ,q也是新用户能够很快上手的重要原因之一?</li></ul><p>q些优点都是非常值得在以后的开发中学习(fn)借鉴的。作Z个通用工具包,Lunece的确l予了需要将全文索功能嵌入到应用中的开发者很多的便利?/p><p>此外Q通过对Lucene的学?fn)和使用Q我也更深刻地理解了Z么很多数据库优化设计中要求,比如Q?/p><ul><li>可能对字段q行索引来提高查询速度Q但q多的烦引会Ҏ(gu)据库表的更新操作变慢Q而对l果q多的排序条Ӟ实际上往往也是性能的杀手之一? </li><li>很多商业数据库对大批量的数据插入操作会提供一些优化参敎ͼq个作用和烦引器的merge_factor的作用是cM的, </li><li>20%/80%原则Q查的结果多q不{于质量好,其对于q回l果集很大,如何优化q头几十条结果的质量往往才是最重要的? </li><li>可能让应用从数据库中获得比较小的结果集Q因为即使对于大型数据库Q对l果集的随机讉K也是一个非常消耗资源的操作?br /></li></ul><p>参考资料:</p><p>Apache: Lucene Project<br /><a >http://jakarta.apache.org/lucene/<br /></a>Lucene开?用户邮g列表归档<br /><a >Lucene-dev@jakarta.apache.org</a><br /><a >Lucene-user@jakarta.apache.org</a></p><p>The Lucene search engine: Powerful, flexible, and free<br /><a >http://www.javaworld.com/javaworld/jw-09-2000/jw-0915-Lucene_p.html</a></p><p>Lucene Tutorial<br /><a >http://www.darksleep.com/puff/lucene/lucene.html</a></p><p>Notes on distributed searching with Lucene<br /><a >http://home.clara.net/markharwood/lucene/</a></p><p>中文语言的切分词<br /><a >http://www.google.com/search?sourceid=navclient&hl=zh-CN&q=chinese+word+segment</a></p><p>搜烦引擎工具介绍<a ><br />http://searchtools.com/</a></p><p>Lucene作者Cutting的几论文和专利<br /><a >http://lucene.sourceforge.net/publications.html</a> </p><p>Lucene?NET实现QdotLucene<br /><a >http://sourceforge.net/projects/dotlucene/<br /></a></p><p>Lucene作者Cutting的另外一个项目:ZJava的搜索引擎Nutch<br /><a >http://www.nutch.org/</a>   <a >http://sourceforge.net/projects/nutch/<br /></a></p><p>关于Z词表和N-Gram的切分词比较<br /><a >http://china.nikkeibp.co.jp/cgi-bin/china/news/int/int200302100112.html</a><br /><br />2005-01-08 <a >Cutting在Pisa大学做的关于Lucene的讲座:非常详细的Lucene架构解说</a></p><img src ="http://m.tkk7.com/boddi/aggbug/68017.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.tkk7.com/boddi/" target="_blank">boddi</a> 2006-09-06 13:34 <a href="http://m.tkk7.com/boddi/archive/2006/09/06/68017.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Excel API?/title><link>http://m.tkk7.com/boddi/archive/2006/09/04/67576.html</link><dc:creator>boddi</dc:creator><author>boddi</author><pubDate>Mon, 04 Sep 2006 06:17:00 GMT</pubDate><guid>http://m.tkk7.com/boddi/archive/2006/09/04/67576.html</guid><wfw:comment>http://m.tkk7.com/boddi/comments/67576.html</wfw:comment><comments>http://m.tkk7.com/boddi/archive/2006/09/04/67576.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.tkk7.com/boddi/comments/commentRss/67576.html</wfw:commentRss><trackback:ping>http://m.tkk7.com/boddi/services/trackbacks/67576.html</trackback:ping><description><![CDATA[使用Windows操作pȝ的朋友对Excel(?sh)子表?一定不会陌生,但是要用Java语言来操UExcel文gq不是一件容易的事。在Web应用日益盛行的今天,通过Web来操作Excel文g的需求越来越强烈Q目前较为流行的操作是在JSP或Servlet 中创Z个CSV (comma separated values)文gQƈ这个文件以MIMEQtext/csvcdq回l浏览器Q接着览器调用Excelq且昄CSV文g。这样只是说可以讉K刊WExcel文gQ但是还不能真正的操UExcel文gQ本文将l大家一个惊喜,向大家介l一个开放源码项目,Java Excel APIQ用它大家可<br /><br />    <br />以方便地操纵Excel文g了?br /><br />  Java Excel API?br /><br />  Java Excel是一开放源码项目,通过它Java开发h员可以读取Excel文g的内宏V创建新的Excel文g、更新已l存在的Excel文g。用该 API非Windows操作pȝ也可以通过UJava应用来处理Excel数据表。因为是使用Java~写的,所以我们在Web应用中可以通过JSP、 Servlet来调用API实现对Excel数据表的讉K?br /><br />  现在发布的稳定版本是V2.0Q提供以下功能:<br /><br />   从Excel 95?7?000{格式的文g中读取数据;<br /><br />   dExcel公式(可以dExcel 97以后的公?Q?br /><br />   生成Excel数据?格式为Excel 97)Q?br /><br />   支持字体、数字、日期的格式化;<br /><br />   支持单元格的阴媄操作Q以及颜色操作;<br /><br />   修改已经存在的数据表Q?br /><br />  现在q不支持以下功能Q但不久׃提供了:<br /><br />   不能够读取图表信息;<br /><br />   可以读,但是不能生成公式QQ何类型公式最后的计算值都可以dQ?br /><br />   <br /><br />   应用CZ<br /><br />  1、从Excel文gd数据?br /><br />  Java Excel API既可以从本地文gpȝ的一个文?.xls)Q也可以从输入流中读取Excel数据表。读取Excel数据表的W一步是创徏Workbook(术语Q工作薄)Q下面的代码片段举例说明了应该如何操作:(完整代码见ExcelReading.java)<br /><br />  import java.io.*;<br /><br />  import jxl.*;<br /><br />  … … … ?br /><br />  try<br /><br />  {<br /><br />  //构徏Workbook对象, 只读Workbook对象<br /><br />  //直接从本地文件创建Workbook<br /><br />  //从输入流创徏Workbook<br /><br />  InputStream is = new FileInputStream(sourcefile);<br /><br />  jxl.Workbook rwb = Workbook.getWorkbook(is);<br /><br />  }<br /><br />  catch (Exception e)<br /><br />  {<br /><br />  e.printStackTrace();<br /><br />  }<br /><br />  一旦创ZWorkbookQ我们就可以通过它来讉KExcel Sheet(术语Q工作表)。参考下面的代码片段Q?br /><br />  //获取W一张Sheet?br /><br />  Sheet rs = rwb.getSheet(0);<br /><br />  我们既可能通过Sheet的名U来讉K它,也可以通过下标来访问它。如果通过下标来访问的话,要注意的一Ҏ(gu)下标?开始,像数组一栗?br /><br />  一旦得CSheetQ我们就可以通过它来讉KExcel Cell(术语Q单元格)。参考下面的代码片段Q?br /><br />  //获取W一行,W一列的?br /><br />  Cell c00 = rs.getCell(0, 0);<br /><br />  String strc00 = c00.getContents();<br /><br />  //获取W一行,W二列的?br /><br />  Cell c10 = rs.getCell(1, 0);<br /><br />  String strc10 = c10.getContents();<br /><br />  //获取W二行,W二列的?br /><br />  Cell c11 = rs.getCell(1, 1);<br /><br />  String strc11 = c11.getContents();<br /><br />  System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType());<br /><br />  System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType());<br /><br />  System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType()); 如果仅仅是取得Cell的|我们可以方便地通过getContents()Ҏ(gu)Q它可以Q何类型的Cell值都作ؓ一个字W串q回。示例代码中Cell(0, 0)是文本型QCell(1, 0)是数字型QCell(1,1)是日期型Q通过getContents()Q三U类型的q回值都是字W型?br />    <br />    <br /><br />  如果有需要知道Cell内容的确切类型,API也提供了一pd的方法。参考下面的代码片段Q?br /><br />  String strc00 = null;<br /><br />  double strc10 = 0.00;<br /><br />  Date strc11 = null;<br /><br />  Cell c00 = rs.getCell(0, 0);<br /><br />  Cell c10 = rs.getCell(1, 0);<br /><br />  Cell c11 = rs.getCell(1, 1);<br /><br />  if(c00.getType() == CellType.LABEL)<br /><br />  {<br /><br />  LabelCell labelc00 = (LabelCell)c00;<br /><br />  strc00 = labelc00.getString();<br /><br />  }<br /><br />  if(c10.getType() == CellType.NUMBER)<br /><br />  {<br /><br />  NmberCell numc10 = (NumberCell)c10;<br /><br />  strc10 = numc10.getvalue();<br /><br />  }<br /><br />  if(c11.getType() == CellType.DATE)<br /><br />  {<br /><br />  DateCell datec11 = (DateCell)c11;<br /><br />  strc11 = datec11.getDate();<br /><br />  }<br /><br />  System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType());<br /><br />  System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType());<br /><br />  System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());<br /><br />  在得到Cell对象后,通过getType()Ҏ(gu)可以获得该单元格的类型,然后与 API提供的基本类型相匚wQ强制{换成相应的类型,最后调用相应的取值方法getXXX()Q就可以得到定cd的倹{API提供了以下基本类型,与 Excel的数据格式相对应Q如下图所C:<br /><br />  每种cd的具体意义,请参见Java Excel API document.<br /><br />  当你完成对Excel?sh)子表格数据的处理后Q一定要使用close()Ҏ(gu)来关闭先前创建的对象Q以释放d数据表的q程中所占用的内存空_在读取大量数据时昑־ؓ重要。参考如下代码片D:<br /><br />  //操作完成Ӟ关闭对象Q释攑֍用的内存I间<br /><br />  rwb.close();<br /><br />  Java Excel API提供了许多访问Excel数据表的Ҏ(gu)Q在q里我只要地介绍几个常用的方法,其它的方法请参考附录中的Java Excel API document.<br /><br />  WorkbookcL供的Ҏ(gu)<br /><br />  1. int getNumberOfSheets()<br /><br />  获得工作?Workbook)中工作表(Sheet)的个敎ͼCZQ?br /><br />  jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));<br /><br />  int sheets = rwb.getNumberOfSheets();<br /><br />  2. Sheet[] getSheets()<br /><br />  q回工作?Workbook)中工作表(Sheet)对象数组Q示例:<br /><br />  jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));<br /><br />  Sheet[] sheets = rwb.getSheets();<br /><br />  3. String getVersion()<br /><br />  q回正在使用的API的版本号Q好像是没什么太大的作用?br /><br />  jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));<br /><br />  String apiVersion = rwb.getVersion();<br /><br />  Sheet接口提供的方?br /><br />  1) String getName()<br /><br />  获取Sheet的名UͼCZQ?br /><br />  jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));<br /><br />  jxl.Sheet rs = rwb.getSheet(0);<br /><br />  String sheetName = rs.getName();<br /><br />  2) int getColumns()<br /><br />  获取Sheet表中所包含的d敎ͼCZQ?br /><br />  jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));<br /><br />  jxl.Sheet rs = rwb.getSheet(0);<br /><br />  int rsColumns = rs.getColumns();<br /><br />  3) Cell[] getColumn(int column)<br /><br />  获取某一列的所有单元格Q返回的是单元格对象数组Q示例:<br /><br />  jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));<br /><br />  jxl.Sheet rs = rwb.getSheet(0);<br /><br />  Cell[] cell = rs.getColumn(0);<br /><br />  4) int getRows()<br /><br />  获取Sheet表中所包含的总行敎ͼCZQ?br /><br />  jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));<br /><br />  jxl.Sheet rs = rwb.getSheet(0);<br /><br />  int rsRows = rs.getRows();<br /><br />  5) Cell[] getRow(int row)<br /><br />  获取某一行的所有单元格Q返回的是单元格对象数组Q示例子Q?br /><br />  jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));<br /><br />  jxl.Sheet rs = rwb.getSheet(0);<br /><br />  Cell[] cell = rs.getRow(0);<br /><br />  6) Cell getCell(int column, int row)<br /><br />  获取指定单元格的对象引用Q需要注意的是它的两个参敎ͼW一个是列数Q第二个是行敎ͼq与通常的行、列l合有些不同?br /><br />  jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));<br /><br />  jxl.Sheet rs = rwb.getSheet(0);<br /><br />  Cell cell = rs.getCell(0, 0); 2、生成新的Excel工作?br /><br />  下面的代码主要是向大家介l如何生成简单的Excel工作表,在这里单元格的内Ҏ(gu)不带M修饰?如:字体Q颜色等{?Q所有的内容都作为字W串写入?完整代码见ExcelW<br /><br />    <br />riting.java)<br /><br />  与读取Excel工作表相|首先要用Workbookcȝ工厂Ҏ(gu)创徏一个可写入的工作薄(Workbook)对象Q这里要注意的是Q只能通过API提供的工厂方法来创徏WorkbookQ而不能用 WritableWorkbook的构造函敎ͼ因ؓcWritableWorkbook的构造函Cؓprotectedcd。示例代码片D如下:<br /><br />  import java.io.*;<br /><br />  import jxl.*;<br /><br />  import jxl.write.*;<br /><br />  … … … ?br /><br />  try<br /><br />  {<br /><br />  //构徏Workbook对象, 只读Workbook对象<br /><br />  //Method 1Q创建可写入的Excel工作?br /><br />  jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile));<br /><br />  //Method 2Q将WritableWorkbook直接写入到输出流<br /><br />  /*<br /><br />  OutputStream os = new FileOutputStream(targetfile);<br /><br />  jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(os);<br /><br />  */<br /><br />  }<br /><br />  catch (Exception e)<br /><br />  {<br /><br />  e.printStackTrace();<br /><br />  }<br /><br />  API提供了两U方式来处理可写入的输出,一U是直接生成本地文gQ如果文件名不带全\径的话,~省的文件会定位在当前目录,如果文g名带有全路径的话Q则生成的Excel文g则会定位在相应的目录Q另外一U是Excel对象直接写入到输出流Q例如:用户通过览器来讉KWeb服务器,如果HTTP头设|正的话,览器自动调用客L(fng)的Excel应用E序Q来昄动态生成的 Excel?sh)子表格?br /><br />  接下来就是要创徏工作表,创徏工作表的Ҏ(gu)与创建工作薄的方法几乎一P同样是通过工厂模式Ҏ(gu)获得相应的对象,该方法需要两个参敎ͼ一个是工作表的名称Q另一个是工作表在工作薄中的位|,参考下面的代码片段Q?br /><br />  //创徏Excel工作?br /><br />  jxl.write.WritableSheet ws = wwb.createSheet("Test Sheet 1", 0);<br /><br />  "q锅也支好了Q材料也准备齐全了,可以开始下锅了Q?Q现在要做的只是实例化API所提供的Excel基本数据cdQƈ它们添加到工作表中可以了Q参考下面的代码片段Q?br /><br />  //1.dLabel对象<br /><br />  jxl.write.Label labelC = new jxl.write.Label(0, 0, "This is a Label cell");<br /><br />  ws.addCell(labelC);<br /><br />  //d带有字型Formatting的对?br /><br />  jxl.write.WritableFont wf = new jxl.write.WritableFont(WritableFont.TIMES, 18, WritableFont.BOLD, true);<br /><br />  jxl.write.WritableCellFormat wcfF = new jxl.write.WritableCellFormat(wf);<br /><br />  jxl.write.Label labelCF = new jxl.write.Label(1, 0, "This is a Label Cell", wcfF);<br /><br />  ws.addCell(labelCF);<br /><br />  //d带有字体颜色Formatting的对?br /><br />  jxl.write.WritableFont wfc = new jxl.write.WritableFont(WritableFont.ARIAL, 10, WritableFont.NO_BOLD, false,<br /><br />  Underlinestyle.NO_UNDERLINE, jxl.format.Colour.RED);<br /><br />  jxl.write.WritableCellFormat wcfFC = new jxl.write.WritableCellFormat(wfc);<br /><br />  jxl.write.Label labelCFC = new jxl.write.Label(1, 0, "This is a Label Cell", wcfFC);<br /><br />  ws.addCell(labelCF);<br /><br />  //2.dNumber对象<br /><br />  jxl.write.Number labelN = new jxl.write.Number(0, 1, 3.1415926);<br /><br />  ws.addCell(labelN);<br /><br />  //d带有formatting的Number对象<br /><br />  jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##");<br /><br />  jxl.write.WritableCellFormat wcfN = new jxl.write.WritableCellFormat(nf);<br /><br />  jxl.write.Number labelNF = new jxl.write.Number(1, 1, 3.1415926, wcfN);<br /><br />  ws.addCell(labelNF);<br /><br />  //3.dBoolean对象<br /><br />  jxl.write.Boolean labelB = new jxl.write.Boolean(0, 2, false);<br /><br />  ws.addCell(labelB);<br /><br />  //4.dDateTime对象<br /><br />  jxl.write.DateTime labelDT = new jxl.write.DateTime(0, 3, new java.util.Date());<br /><br />  ws.addCell(labelDT);<br /><br />  //d带有formatting的DateFormat对象<br /><br />  jxl.write.DateFormat df = new jxl.write.DateFormat("dd MM yyyy hh:mm:ss");<br /><br />  jxl.write.WritableCellFormat wcfDF = new jxl.write.WritableCellFormat(df);<br /><br />  jxl.write.DateTime labelDTF = new jxl.write.DateTime(1, 3, new java.util.Date(), wcfDF);<br /><br />  ws.addCell(labelDTF); q里有两点大家要引v大家的注意。第一点,在构造单元格Ӟ单元格在工作表中的位|就已经定了。一旦创建后Q单元格的位|是不能够变更的Q尽单元格的内Ҏ(gu)可以改变的。第二点Q单元格的定位是按照下面q样的规?column, row)Q而且下标都是?开始,例如QA1被存储在(0, 0)QB1被存储在(1, 0)?br /><br />    <br /><br />  最后,不要忘记关闭打开的Excel工作薄对象,以释攑֍用的内存Q参见下面的代码片段Q?br /><br />  //写入Exel工作?br /><br />  wwb.write();<br /><br />  //关闭Excel工作薄对?br /><br />  wwb.close();<br /><br />  q可能与dExcel文g的操作有少不同Q在关闭Excel对象之前Q你必须要先调用write()Ҏ(gu)Q因为先前的操作都是存储在缓存中的,所以要通过该方法将操作的内容保存在文g中。如果你先关闭了Excel对象Q那么只能得C张空的工作薄了?br /><br />  3、拷贝、更新Excel工作?br /><br />  接下来简要介l一下如何更C个已l存在的工作薄,主要是下面二步操作,W一步是构造只ȝExcel工作薄,W二步是利用已经创徏的Excel工作薄创建新的可写入的Excel工作薄,参考下面的代码片段Q?完整代码见ExcelModifying.java)<br /><br />  //创徏只读的Excel工作薄的对象<br /><br />  jxl.Workbook rw = jxl.Workbook.getWorkbook(new File(sourcefile));<br /><br />  //创徏可写入的Excel工作薄对?br /><br />  jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile), rw);<br /><br />  //dW一张工作表<br /><br />  jxl.write.WritableSheet ws = wwb.getSheet(0);<br /><br />  //获得W一个单元格对象<br /><br />  jxl.write.WritableCell wc = ws.getWritableCell(0, 0);<br /><br />  //判断单元格的cd, 做出相应的{?br /><br />  if(wc.getType() == CellType.LABEL)<br /><br />  {<br /><br />  Label l = (Label)wc;<br /><br />  l.setString("The value has been modified.");<br /><br />  }<br /><br />  //写入Excel对象<br /><br />  wwb.write();<br /><br />  //关闭可写入的Excel对象<br /><br />  wwb.close();<br /><br />  //关闭只读的Excel对象<br /><br />  rw.close(); 之所以用这U方式构建Excel对象Q完全是因ؓ效率的原因,因ؓ上面的示例才是 API的主要应用。ؓ了提高性能Q在d工作表时Q与数据相关的一些输Z息,所有的格式信息Q如Q字体、颜色等{,是不被处理的Q因为我们的目的是获得行数据的|既没有了修饎ͼ也不会对行数据的g生什么媄响。唯一的不利之处就是,在内存中会同时保存两<br />    <br />个同L(fng)工作表,q样当工作表体积比较大时Q会占用相当大的内存Q但现在好像内存的大ƈ不是什么关键因素了?br /><br />  一旦获得了可写入的工作表对象,我们可以对单元格对象进行更新的操作了,在这里我们不必调用API提供的add()Ҏ(gu)Q因为单元格已经于工作表当中Q所以我们只需要调用相应的setXXX()Ҏ(gu)Q就可以完成更新的操作了?br /><br />  单元格原有的格式化修饰是不能去掉的Q我们还是可以将新的单元g饰加上去Q以使单元格的内容以不同的Ş式表现?br /><br />  新生成的工作表对象是可写入的Q我们除了更新原有的单元格外Q还可以d新的单元格到工作表中Q这与示?的操作是完全一L(fng)?br /><br />  最后,不要忘记调用write()Ҏ(gu)Q将更新的内容写入到文g中,然后关闭工作薄对象,q里有两个工作薄对象要关闭,一个是只读的,另外一个是可写入的?img src ="http://m.tkk7.com/boddi/aggbug/67576.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.tkk7.com/boddi/" target="_blank">boddi</a> 2006-09-04 14:17 <a href="http://m.tkk7.com/boddi/archive/2006/09/04/67576.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://m.tkk7.com/" title="亚洲av成人片在线观看">亚洲av成人片在线观看</a> <div class="friend-links"> </div> </div> </footer> վ֩ģ壺 <a href="http://ywzms.com" target="_blank">˾ҹѸ</a>| <a href="http://1408600.com" target="_blank">һһһ</a>| <a href="http://zjpcyh.com" target="_blank">޵һҳĻ</a>| <a href="http://kimheezk.com" target="_blank">һ</a>| <a href="http://323c.com" target="_blank">˳δʮ˽վ</a>| <a href="http://qq2071.com" target="_blank">С˵ɫͼ</a>| <a href="http://www33399.com" target="_blank">ѹۿ</a>| <a href="http://jdwx58.com" target="_blank">Ĺѹۿ߲</a>| <a href="http://www-c559.com" target="_blank">18ڵվ</a>| <a href="http://av520av.com" target="_blank">ĻһëƬ</a>| <a href="http://by2988.com" target="_blank">þþƷƵ</a>| <a href="http://xx16xx.com" target="_blank">һ</a>| <a href="http://sewo66.com" target="_blank">a߹ۿ</a>| <a href="http://cswangli.com" target="_blank">ŮſȸͰƵ</a>| <a href="http://hzczj.com" target="_blank">޹91Ʒר</a>| <a href="http://345504.com" target="_blank">avר߲</a>| <a href="http://16688shop.com" target="_blank">һĻ</a>| <a href="http://8222se.com" target="_blank">gayվ</a>| <a href="http://6123fa.com" target="_blank">þѸƵ</a>| <a href="http://jyd56.com" target="_blank">߹ۿվ</a>| <a href="http://cqtchtwq.com" target="_blank">AVվ߹ۿ</a>| <a href="http://7778kk.com" target="_blank">Ƶȫ</a>| <a href="http://qdhengjun.com" target="_blank">ɫëƬѹۿ</a>| <a href="http://783944.com" target="_blank">99re6ƵƷ</a>| <a href="http://31xyz.com" target="_blank">ҹѸӰԺ</a>| <a href="http://45-po.com" target="_blank">av޹av</a>| <a href="http://3333seav.com" target="_blank">anպר</a>| <a href="http://ttvv77.com" target="_blank"></a>| <a href="http://nblfkj.com" target="_blank">þۺAVѹۿ</a>| <a href="http://jiuse54.com" target="_blank">޾Ʒר</a>| <a href="http://www-44334.com" target="_blank">avƬһ</a>| <a href="http://www09191z.com" target="_blank">ձһѹۿ </a>| <a href="http://oakuu.com" target="_blank">ůůձ</a>| <a href="http://kdltuliao.com" target="_blank">ˬִ̼߳Ƶ </a>| <a href="http://dwj28.com" target="_blank">AëƬA</a>| <a href="http://yeshenghuowang.com" target="_blank">޵һ˵</a>| <a href="http://zc-zk.com" target="_blank">˾ҹƷƵ</a>| <a href="http://ocn888.com" target="_blank">ŮƵ</a>| <a href="http://327www.com" target="_blank">ŮƷþþ</a>| <a href="http://caita88.com" target="_blank">˳δʮ˽վ</a>| <a href="http://www1616hh.com" target="_blank">պƵһ</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>