悠闲的测试和生活。。。 www.happy4tao.com

发布新日志

  • LoadRunner编程之文件的操作

    2010-08-23 08:55:32


    这篇文章主要写下LoadRunner下如何进行文件的操作。
    1,文件的声明
        LoadRunner不支持FILE数据类型,所以在LoadRunner中用int来声明一个文件:
         int MyFile;
    2,文件的打开
         fopen():返回一个FILE数据类型的指针.因为LoadRunner不支持FILE数据类型,所以返回值需要转化成int型.
          int MyFile;
          MyFile=(int)fopen("C:\\temp\\loans.txt","w");
          fopen()函数的第一个参数是创建文件的路径.第二个参数指定了创建文件的模式.下面是常用的几种模式:
    “w” - 写,当需要往文件中写的时候.如果文件存在,就覆盖该文件,如果文件不存在,根据第一个参数来创建新文件.
    “r” –读,需要从文件中读的时候.这个文件必须已经存在.
    “a” –附加,当往文件末尾添加数据时用到.
    “rw” –读和写.
    第一个参数中注意文件路径为"\\",因为"\"在C语言中为转义字符.另外,如果文件和脚本在同一个目录中,文件的完整路径可以省略.
    3,读文件
           fscanf():用来读文件。函数语法如下:
          int MyFile;
               int LoanNumber;
               MyFile = fopen(“C:\\temp\\loans.txt”,”r”);
               fscanf(MyFile,”%d”, &LoanNumber);
        需要注意的是:MyFile是个文件指针(在LoadRunner中并不是真正的指针),被用来代替实际的文件。所有文件的操作都是使用文件指针而不是文件名称。
        练习1:
        先创建一个文件c:\temp\loans.txt,文件包含的内容为:
        11111
        22222
        33333
        44444
        55555
    循环读取并显示该文件中的每行数据,脚本如下:
    Action()
    {
           int MyFile;
           int LoanNumber,i;
           // Assigning the file path to a string
           char FileName[80] = "C:\\temp\\loans.txt";
     
     
           // Opening the file
           // Note the use of variable to replace the file path
           //
           MyFile = (int)fopen(FileName,"r");
     
     
           // Reading and printing one loan number at a time
           for(i=1;i<=5;i++)
                  {
                  fscanf(MyFile,"%d", &LoanNumber);
                  lr_output_message("Loan Number %d: %d",i,LoanNumber);
                  }
     
           fclose(MyFile);
     
           return 0;
    }
    当文件打开之后,文件指针在文件的最开始。每读一次,指针就移动到下一行,这就是为什么程序能自动读取下面元素的原因。
    a) 如果把循环改为for(i=1;i<=7;i++),会出现什么样的结果呢?
    会出现三个都是打印55555的结果,原因是文件指针移动到最后就不再移动了,但是有7次循环,那多出来的2次循环打印的数据仍然是最后一行的数据。
    b)如果不知道该文件下包含数据的行数,就无法用for循环。需要使用while循环。可以使用feof函数。feof(MyFile)在文件指针没有到达返回文件末尾的时候返回0,如果到末尾返回非0值。这样可以修改程序的循环为:
          while(feof(MyFile)== 0);
    注意:如果文件末尾有空行,feof也会把它最为文件的一部分。
    4,写文件
      fprintf():用来往文件写。语法如下:
              int MyFile;
            char Name[] = “John Doe”;
            MyFile = fopen(“C:\\temp\\loan.txt”,”w”);
                  fscanf(MyFile,”%s”,Name);
    第一个参数是文件指针,第二个参数是格式,第三个参数是指定格式对应的变量。
    Action()
    {
           int MyFile;
           char Name[] = "John Doe";
     
     
           MyFile = fopen("C:\\temp\\names.txt","w");
    // note that "w" is used to write
          
    fprintf(MyFile,"%s", Name);
    // note that we are printing a string here
    转载请注明源自www.SCMLife.com,请保留版权. 本贴地址:http://bbs.scmlife.com/viewthread.php?tid=23466
     
  • 微软UI自动化测试的技术演变(下)

    2010-07-27 12:52:07

    UIAutomation和WPF

        UIAutomation是微软从Windows Vista开始推出的一套全新UI自动化测试技术, 简称UIA。在最新的Windows SDK中,UIA和MSAA等其它支持UI自动化技术的组件放在一起发布,叫做Windows Automation API。

        和前面的介绍相比,我倾向于认为UIA是一项自动化测试“技术”,而MSAA和Win32 API只是实现自动化测试的两种“方法”。这里区分“技术”和 “方法”的原因是, 一项“技术”往往有独立的模型,体贴的开发接口,用来专门解决某一类的问题,同时允许不同的实现细节。UIA可以被看作“技术”,是因为:

        UIA定义了全新的、针对UI自动化的接口和模式。 分别是支持对UI元素进行遍历和条件化查询的TreeWalker/FindAll。定义了读写UI元素属性的UIA Property, 包括Name、 ID、Type、ClassName、Location、 Visibility等等。定义了UI元素行为的UIA Pattern, 比如Select、Expand、Resize、 Check、Value等等。 还引入了UIA Event接口,可以让测试程序在某些事件发生后得到通知,比如新窗口打开事件等。 

        以往的Win32和MSAA 设计出发点并不是为解决UI自动化。Win32旨在提供的通用开发接口, MSAA旨在提供程序的多种访问方式。相反,UIA的设计目的,以及新引入的模式和接口都完全是针对UI自动化测试的。

        在后面的文章中我们会详细分析UIA的内部实现。可以看到,UIA这一套接口和模式,可以在不同平台,不同开发工具中实现和使用。其内部实现方式也因地制宜, 前后的兼容性都照顾得很好。 同时,UIA提供了托管的和非托管两种API,这些都是Win32和MSAA无法比拟的。

        下面一段简单的C#代码演示了如何使用UIA测试Windows自带计算器完成计算3+5-2的操作(下述代码可能需要修改以适应不同Windows版本的calc.exe程序。本代码使用Visual Studio 2008针对Windows 2008 Server R2 English 编写

     

    code 1code 2code 3code 4

     

     

     

    UIA的优势

        UIA的优势非常明显,主要包括以下几点:

        1. 适应不同类型的UI程序,包括Win32、WinForm、 WPF和Silverlight。 由于WPF和Silverlight中的子窗口和控件并不是传统的HWND,所以Win32 API和MSAA无能为力。 而UIA可以直接支持这两种程序。 

        2. 兼容传统的Win32和MSAA模式。 前面提到过,UIA技术的内部实现可以多样化。 这一点在下一篇文章中会详细讨论。 UIA通过一项叫做UIA<->MSAA的桥技术, 针对传统程序, 可以在内部实现中借用MSAA的接口和直接调用Win32 API。这样不需要对控件或者程序的既有实现做任何改动, 就可以直接适用于UIA的新模式。

        3. 新引入的TreeWalker、UIA Event、Pattern、 Property模式易于使用,贴合自动化测试。这些模式高度抽象了各种UI自动化测试的需求,同时又不和传统模式相冲突。比如执行点击按钮操作, 传统方法要么模拟鼠标键盘操作,要么发送Windows Message,而Message还分为WM_COMMAND或者WM_BUTTONDOWN。 而通过UIA Pattern, 统一归类于Invoke接口,这个接口对于测试者来说就统一了。无论是Win32、 WPF还是Silverlight按钮,都可以通过统一接口执行, 从而把具体实现隔离开。同时, 调用者若希望继续沿用键盘鼠标模拟,仍旧可以通过SendKey加上UIA获取坐标的方法实现。而UIA Event和对UI元素支持条件化区域化搜索,更是极大简化了测试人员的工作。

        4. 提供托管的和非托管接口, 方便各种工具的开发人员。 同时提供了简洁方便的方式支持UI程序和控件开发人员扩展,自定义UIA的实现。比如通过AutomationPeer来扩展 基于WPF的控件, 通过实现简单的IRawElementProviderSimple来扩展基于WinForm的控件等。具体细节在下一篇文章中会详细介绍。

        5. 针对WPF程序,除了支持基本的端对端(End to End)UI自动化以外,还支持基于AutomationPeer的单元测试。具体例子可以参考UI Automation in Silverlight - Simulating User Interactions

        6. 提供了完善的工具、文档、开发包、例子程序等。比如通过UI Spy(图三)获取任意窗口或者元素的UIA信息。

     

    UISpy 

    图三:UI Spy

     

    自动化技术和自动化框架

        前面提到了UIA作为全新UI自动化测试技术的优势,但这并不能解决所有的UI 自动化问题。 自动化框架正是为了自动化技术没有完全解决的问题。比如:

        1. 自动化中的同步和等待。 对于稍复杂的UI 程序,测试程序往往需要根据测试目标的状态决定 下一步的操作。 比如测试文件另存为功能的时候,若保存路径是网络路径,可能会因为网络延迟导致整个UI停顿比较长的时间。这个时候测试,程序如果不顾当前状态而简单地执行下一步操作,比如新建文件, 很可能会因为UI延迟而失败。 正确的做法是,测试程序应该等待文件保存成功返回后,再进行下一步操作。 这就是自动化中同步和等待的一个例子。实现同步和等待有多种方法,最简单粗暴的做法是硬编码一个长时间的 Sleep在测试代码中。 稍微好一点的做法可以采取小时间片的轮询状态检查, 或者反复重试。 借助 UIA的Event Pattern,可以尝试捕获另存为窗口的关闭WindowClosedEvent。 如果要做得完善一点, 可以把多种方法结合, 另外再额外检查目标程序的CPU使用情况,消息循环是否有回应,设定超时时间等等。 

        2. 冗繁的编码过程。 对于一个UI窗口,里面可能有几十个子控件或者子窗口。 在编写测试代码的时候, 如果对这些子元素的获取,操作不能简化, 势必导致代码冗繁,难以维护。 借助自动代码生成和ORM (Object Role Modeling)等技术, 可以解决这个问题。 比如可以用工具把窗口及其子元素的关系和搜索条件都序列化到XML文件中, 然后采用ORM技术即可在代码中轻松获取子元素。

        3. 多语言和本地化测试。多语言和本地化的测试对UI来说显得尤为重要。 UI程序往往通过资源文件来定义所显示的内容, 这就要求自动化测试要可以方便读取和定位程序的资源文件, 来支持多语言和本地化测试。

        4. 支持工具和辅助函数的匮乏。 对于大的项目研发, 通过好的工具来减小开发成本是非常必要的。 就UI自动化来说, 如果自动化测试用例可以通过一次录制,多次播放来做的话,成本会减少很多。 在VS2010中就提供了这样的录制-播放功能。 详细视频可以参考How to create record and playback Test Cases in Visual Studio Beta2。

        5. 区分功能性测试和用户真实行为模拟。 前面提到, 就点击按钮功能来说, 可以通过SendKey来模拟鼠标操作, 或者通过Windows Message来直接触发点击事件。 这两种不同方法各有优劣。 比如当按钮被其它元素遮挡, 通过SendKey进行模拟就会导致失败,而直接发送Windows Message还是会成功。 孰优孰劣取决于要达到的目的。 如果单纯为了测试按钮点击后导致的结果,通过Windows Message来模拟就省去了很多麻烦。 相反, 如果是界面测试, 通过SendKey来模拟就可以让按钮被遮挡的bug暴露出来, 而Windows Message则不能发现这样的问题。

        所以,单纯的某个自动化技术或者方法也无法满足需求。为了解决上述问题,各种自动化测试框架逐渐涌现和发展。微软内部有多个不同的自动化框架,设计理念和侧重点各有不同。 Visual Studio 2010将加入对自动化测试的支持。 在CodePlex上面, 也可以找到多种框架,比如White和UI Automation Verify。

    小结

        本篇主要介绍和比较了Windows平台UI自动化技术的演变。 UIA技术势必成为UI自动化的主流。 下个月, 我们会着重介绍UIA技术的内部机制、原理、实现以及如何在程序和控件中扩展UIA的功能。

  • 微软UI自动化测试的技术演变(上)转

    2010-07-27 12:50:59


    发布: 2010-2-01 13:21 | 作者: stbchina | 来源: cnblogs.com/stbchina | 查看: 76次

    字号:    | 推荐给好友

    Windows平台的桌面开发技术, 从最原始的Win32 SDK,发展到.NET WinForm,一直到今天的WPF和Silverlight, 发生了翻天覆地的变化,相对应的UI自动化测试技术,也随之演变。

        微软UI自动化技术揭秘将分两个部分介绍Windows平台桌面程序自动化技术。上篇将介绍从Win32 SDK至今的UI自动化技术演变,下篇将着重介绍最新的UI Automation(UIA)的内部实现和使用技巧。

        

        自动测试是指用一个程序自动地控制另外一个程序,模拟用户的操作进行测试。通常自动化测试涉及到下面三个步骤:

    测试源侦测

        测试源侦测是定位测试目标元素的过程。比如要测试Windows附件中的计算器,首先要把计算器窗口和其他程序比如写字板区分开。进一步测试计算器窗口菜单的时候,需要首先定位菜单条的位置,获取第二层子菜单等等。简单地说,自动化测试首先要能够获取从桌面开始的整个UI树结构,定位到特定测试目标。

    用户行为模拟

        用户行为模拟指模拟用户的输入,比如鼠标、键盘和触摸笔的操作,中间可能会涉及IME输入法、组合键、特定用户习惯,比如输入速度的模拟等。

    测试目标检查

        指获取测试元素的属性,比如读取窗口标题, Listbox的子元素, Checkbox的状态等等,以便进行测试检查。

    Win32 SDK和Windows Message

        在.NET问世以前,Windows平台上的UI程序无外乎两种技术:Win32 Windows SDK 或者DirectX。由于DirectX多用于专业领域如游戏和CAD,本文并不讨论。

        无论是MFC,VCL还是VB6,Win32 SDK都是其根本,最终打交道的其实都是HWND和Windows Message。实现上述自动化的三个步骤 无外乎三件法宝,Win32 API,Windows Message和Windows Hook。

        测试程序首先通过 FindWindowEx和EnumWindow遍历窗口和子窗口, 找到测试元素比如某个按钮,然后可以通过Windows Message或者API检查测试目标。比如通过WM_GETTEXT或者GetWindowText读取窗口标题,通过GetWindowRect读取按钮坐标位置等等。对于用户行为模拟,可以直接通过SendKey API来完成,当然也可以发送WM_CHAR或者WM_KEYDOWN通知等等。

        除此以外,Windows Hook更加丰富了技术的选取。通过Windows Hook,测试人员还可以直接监控、 截取、模拟目标程序的Windows消息,实现更灵活的模拟,检查甚至录制的功能

        Windows Spy++(图一)虽然不是测试工具,也算是使用这套技术的典型例子。通过Windows Spy++可以定位任意窗口, 读取窗口属性,监视窗口消息等等。

    spyxx

    图一: Microsoft Spy++

        采用Win32 SDK和Windows Message的优点是直接灵活。由于直接使用Win32 API,没有额外的学习曲线,遇上问题可以直接参考Win32 SDK解决。使用Message Hook使得测试程序可以灵活实现,直接对Window Message的操作不仅可以把很多情况化繁为简,还方便test hook的实现。(所谓test hook,是指产品中为了方便测试而专门设计的隐藏功能,该功能对普通用户不可见,只是为了方便测试。)

        缺点包括以下三个方面:

       使用复杂,实现成本高。Win32 AP的使用上有很多需要特别注意的细节, 比如有的Win32 API不能跨进程工作,有的Windows Message只能发给当前线程所创建的窗口,稍有不慎,就导致测试程序不稳定。

        过于底层,不便使用。为了方便测试用例调用,需要对API进行封装,增加了实现成本。同时 Win32 API的也使得很多VB程序员不便调用。再者, 不同的开发工具,比如MFC, VCL,以及后来的.NET Framework,在内部实现上对Win32 API有很多细节的处理, 要实现出针对各种情况都通用的测试框架,并非易事。比如, .NET 中的WinForm. Control对Win32 HWND的维护是动态的,同一个WinForm. Control的HWND在程序的生命周期内是可能发生改变的,这一点对于依赖HWND作为唯一标识的Win32 API就是一个致命伤。

        无法操作自绘窗口。比如打开Excel的工作表,会发现表格中的每一个Cell并没有对应到HWND上。Excel的cell都是通过代码绘制,而不是依赖于现成的Win32 Control。这就使得Win32 API对于自绘窗口没有用武之地。

    MSAA

        MSAA的全称是Microsoft Active Accessibility。这是类似DCOM技术。技术模型是这样的,UI程序可以暴露出一个Interface,方便另一个程序对其进行控制。MSAA技术的初衷是为了方便残疾人使用Windows 程序。比如盲人看不到窗口,但是盲人可以通过一个USB读屏器连接到电脑上, 读屏器通过UI程序暴露出来的这个Interface,就可以获取程序信息,通过盲文或者其它形式传递给盲人。

        MSAA提供了如此方便的功能, UI自动化测试自然可以借用这项技术。MSAA暴露出来的Interface叫做IAccessible。测试程序和目标UI程序互操作流程如下:

        1. 测试程序调用Windows API: AccessibleObjectFromWindow,传入目标UI程序HWND。

        2. AccessibleObjectFromWindow函数向UI程序发送WM_GETOBJECT消息。

        3. UI程序创建实现了IAccessible的内部类,然后通过LresultFromObject API把IAccessible 接口返回给测试程序。

        4. 测试程序拿到IAccessible接口,开始调用IAccessible接口函数操作测试目标。

        IAccessible接口里面的几个关键函数是:

    • IAccessible::get_accChild/ IAccessible::get_accParent通过这两个函数,调用者可以浏览目标程序的窗口关系树,定位到UI元素。
    • IAccessible::accLocation/I Accessible::accHitTest读取和分辨目标元素的屏幕位置。
    • IAccessible::accName/ I Accessible:: accSelect读取元素的名字,对UI元素进行指定的操作,比如选取Listbox里面的某一项等等。
    • IAccessible::accValue 开发人员可以自定义value属性的实现。比如针对折线图控件,开发人员可以在accValue中返回折线的坐标数列。

        MSAA的理念类似于test hook。 通过主动让UI程序暴露一个接口来让调用者控制。在具体使用中,测试人员往往是结合MSAA和Win32 API操作,取长补短。一方面对于UI元素丰富的属性,比如style,钩选状态,是否最大化和模拟用户输入等,继续采用Win32 API。另一方面用MSAA的优势来弥补Win32 API的一些不足,比如:

        由于MSAA有自己的get_accChild方法,使其控件树关系并不一定要和Win32 HWNDd关系对应一致。对于自绘窗口,虽然说只有一个HWND,但是开发人员可以通过实现IAccessible接口来实现逻辑上的层次关系。比如Excel中就可以通过IAccessible把多个cell的子IAccessible接口暴露给调用者。

        IAccessible的实现是由开发者提供,开发者可以灵活地根据实际情况决定方法的实现。比如前面提到了折线图控件可以返回坐标数列。对于.NET WinForm, 微软在Framework中就提供了IAccessible的默认实现,这样在具体实现中,就可以处理.NET动态维护HWND的细节等等。

        针对MSAA的工具也有很多,比如AccExplorer(图二)可以像Spy++一样对指定程序进行控件的树形浏览,检查MSAA属性等。

    AccExp

    图二: AccExplorer

        如果您是开发人员, 对于unmanaged UI程序的MSAA实现,参考MSDN中关于WM_GETOBJECT的说明返回IAccessible interface就可以了。对于managed程序, 实现方法更简单, 现成的例子可以参考:

    •  Control..::.ControlAccessibleObject Class
    •  How to create accessible controls by using Visual Basic .NET or Visual Basic 2005

        对于测试程序如何直接获取并使用IAccessible接口,并非本系列重点,所以并不提供更多介绍。在后面的文章中, 会介绍如何隐含使用IAccessible和MSAA。

        MSAA也有自身的缺点:

        1. 虽然说MSAA基于COM技术, 但IAccessible并不是一个COM标准接口。 比如使用者不需要调用CoInitialize即可使用,也无法通过QueryInterface进一步获取更多的自定义接口。这局限了MSAA所能提供的功能。

        2. IAccessible接口的定义有缺陷。里面不少方法是可有可无的,但是又缺少一些支持UI自动化的关键方法。 比如它提供了accSelect支持控件的选取,但是却没有类似accExpand这样的方法支持树状控件的展开等。

        关于MSAA和UI自动化的更多渊源,MSAA设计理念,现状和缺陷,可以参考微软早期的一篇名为What is UI Automation的文章。

  • selenium备忘(转)

    2010-07-17 15:32:10

    今天有个朋友问起selenium RC怎么使用,一时居然想不起来怎么开始着手。问了同事,看了个小例子,稍微回忆起来一点。

     

    唉,上一个项目使用了selenium RC做自动化测试,这个项目一换工具就往的一干二净,看来还是要经常温故而知新啊。

     

    我这里用java,Eclipse来举例。

     

    简单的来说,

    1. 先去 http://selenium-rc.openqa.org/download.jsp 下载selenium包。解压。 

    2. 用命令行来到解压的文件夹下: \selenium-remote-control-0.9.2\selenium-server-0.9.2

    3. 运行: java -jar selenium-server.jar 启动selenium server

    4. 在Eclipse创建一个项目,在项目的build path里面加上junit.jar和selenium-java-client-driver.jar(这个在刚解压的包里面)

    5. 在项目里面新建一个junit文件,比如说openQA这个网站上的GoolgeTest文件

    6. 在红色波浪线的那些地方,比如Selenium, DefaultSelenium上面‘Ctrl+1’来增加相应要import的类

    7. 然后在Eclipse里运行 “Run As -> unit Test”即可看到自动化的范例

     

    最后粘贴一下那个测试程序

     

    import com.thoughtworks.selenium.DefaultSelenium;
    import com.thoughtworks.selenium.Selenium;

    import junit.framework.TestCase;

    public class GoogleTest extends TestCase {
        private Selenium selenium;

        public void setUp() throws Exception {
            String url = "http://www.google.com";
            selenium = new DefaultSelenium("localhost", 4444, "*firefox", url);  //4444 is default server port

       selenium.start();     
        }

        protected void tearDown() throws Exception {
            selenium.stop();
        }

        public void testGoogle() throws Throwable {
            selenium.open("http://www.google.com/webhp?hl=en");

            assertEquals("Google", selenium.getTitle());
            selenium.type("q", "Selenium OpenQA");
            assertEquals("Selenium OpenQA", selenium.getValue("q"));
            selenium.click("btnG");
            selenium.waitForPageToLoad("5000");
            assertEquals("Selenium OpenQA - Google Search", selenium.getTitle());
        }
    }

  • oracle “exists”和“in”的效率问题(转)

    2010-06-29 14:56:09

    有两个简单例子,以说明 “exists”和“in”的效率问题

    1) select * from T1 where exists(select 1 from T2 where T1.a=T2.a) ;

        T1数据量小而T2数据量非常大时,T1<<T2 时,1) 的查询效率高。

    2) select * from T1 where T1.a in (select T2.a from T2) ;

         T1数据量非常大而T2数据量小时,T1>>T2 时,2) 的查询效率高。

    exists 用法:

    请注意 1)句中的有颜色字体的部分 ,理解其含义;

    其中 “select 1 from T2 where T1.a=T2.a” 相当于一个关联表查询,相当于

    “select 1 from T1,T2     where T1.a=T2.a”

    但是,如果你当当执行 1) 句括号里的语句,是会报语法错误的,这也是使用exists需要注意的地方。

    “exists(xxx)”就表示括号里的语句能不能查出记录,它要查的记录是否存在。

    因此“select 1”这里的 “1”其实是无关紧要的,换成“*”也没问题,它只在乎括号里的数据能不能查找出来,是否存在这样的记录,如果存在,这 1) 句的where 条件成立。

    in 的用法:

    继续引用上面的例子

    “2) select * from T1 where T1.a in (select T2.a from T2) ”

    这里的“in”后面括号里的语句搜索出来的字段的内容一定要相对应,一般来说,T1和T2这两个表的a字段表达的意义应该是一样的,否则这样查没什么意义。

    打个比方:T1,T2表都有一个字段,表示工单号,但是T1表示工单号的字段名叫“ticketid”,T2则为“id”,但是其表达的意义是一样的,而且数据格式也是一样的。这时,用 2)的写法就可以这样:

    “select * from T1 where T1.ticketid in (select T2.id from T2) ”

    Select name from employee where name not in (select name from student);

    Select name from employee where not exists (select name from student);

    第一句SQL语句的执行效率不如第二句。

    通过使用EXISTS,Oracle会首先检查主查询,然后运行子查询直到它找到第一个匹配项,这就节省了时间。Oracle在执行IN子查询时,首先执行子查询,并将获得的结果列表存放在一个加了索引的临时表中。在执行子查询之前,系统先将主查询挂起,待子查询执行完毕,存放在临时表中以后再执行主查询。这也就是使用EXISTS比使用IN通常查询速度快的原因。

  • 解决用startup.bat启动tomcat时一闪而过的办法(转)

    2010-06-27 15:58:32

     

    当用startup.bat启动tomcat时,如果server.xml配置有错误,那么startup.bat将闪一下就消失了,如果想让命令窗口停住,好方便查看错误信息,则用如下方法修改:修改catalina.bat,找到里面的:

    doStart

    shift

    if not "%OS%" == "Windows_NT" goto noTitle

    set _EXECJAVA=start %_RUNJAVA%

    goto gotTitle

    :noTitle

    set _EXECJAVA=start %_RUNJAVA%

    :gotTitle

    if not ""%1"" == ""-security"" goto execCmd

    shift

    echo Using Security Manager

    set SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy

    goto execCmd

    将上面set _EXECJAVA=start %_RUNJAVA% 中的start去掉,就可以让命令行出错后不消失。set _EXECJAVA=start %_RUNJAVA% 的意识是重新启动一个命令行窗口。去掉start后,则不会再重新启动窗口,就达到了使命令行停住的效果。要在命令行中启动startup.bat才能看到效果。

  • VU创建Java模板虚拟用户(转)

    2010-06-21 14:59:57

    我们首先需要创建Java Vuser,然后在Java Vuser环境下加载或开发Java程序。

    启动VU,在“File”菜单下选择“New”,在弹出的Vuser列表中选择“Java Vuser”,如图8-16所示。

     

     

    单击“OK”按钮,在VU脚本中我们看到生成如下模板脚本:

    import lrapi.lr;

    public class Actions

    {

        public int init() {

        return 0;

        }//end of init

        public int action() {

        return 0;

        }//end of action

        public int end() {

        return 0;

        }//end of end

    }

    由上可见,Java Vuser中的Int()、action()、end()函数和C Vuser中的vuser_init、action、vuser_end一样,迭代的设置只对action生效,而对init和end无效。我们的开发主要在action函数中完成。

    8.3.2  设置Java环境

    我们知道Java环境最主要的参数有两个:一个是Java虚拟机path,另一个是classpath。

    打开Vuser的运行时设置(Run-time Settings)窗口,选择Java VM,如图8-17所示。

     

    图8-17  运行时设置中的Java VM配置页面

    这里如果Xbootclasspath没有特殊的参数设置,对此页面不要做任何修改。因为VU将自动从path中获得Java虚拟机的路径。

    单击“Classpath”,显示如图8-18所示的配置页面。

    对于SMTP协议,Java开源组织已经提供了很多相关package封装,我们可以在Internet上下载,比如在jakarta网站。

    我们在这里添加3个mail的jar包,即mail.jar、poi.jar和jakarta-oro-2.0.8.jar。单击“OK”按钮,关闭“Run-time Settings”窗口。

     

    图8-18  运行时设置中的Java Classpath配置页面

    单击VU运行按钮,编译运行通过,验证我们的设置是正确的。

    8.3.3  在Java Vuser中开发SMTP发送mail脚本

    首先我们需要在Java文件头进行import声明,包含相关package。

    import lrapi.lr;

    import java.io.File;

    import java.io.FileInputStream;

    import java.util.ArrayList;

    import java.util.Date;

    import java.util.Properties;

    import javax.mail.*;

    import javax.mail.internet.*;

    import org.apache.poi.hssf.usermodel.*;

    import com.sun.mail.smtp.*;

    再次运行脚本,以验证import是否成功,否则Java会报错,显示package找不到。

    在action函数中编写发送邮件的Java代码,如下:

    public int action() {

         boolean ssl = false;

         //获得SMTP环境

         Properties props = System.getProperties();

         //设置SMTP主机地址

         props.put("mail.smtp.host", "192.168.1.100");

         //设置SMTP端口号

         props.put("mail.smtp.port","25");

         //设置SMTP用户名

         props.put("mail.smtp.user","test1");

         Session session=Session.getInstance(System.getProperties(),null);

         //设置邮件header字段

         String mailer = "send from VU java SMTP";

         //设置收件人

         String sendTo = "test1@cesoo.com";

         //设置抄送人

         String sendCC = "test1@cesoo.com";

         //设置发件人

         String sendFrom = "test1@cesoo.com";

         try{

            //获得发送实体

            SMTPTransport t = (SMTPTransport)session.getTransport(ssl ? "smtps" : "smtp");

            //与SMTP主机相连

            t.connect("192.168.1.100","test1","123456");

            MimeMessage msg = new MimeMessage(session);

            //设置邮件各个字段

            String subject = "testing mail subject";

            String personalName = "LR JAVA Vuser";

            String body = "testing mail body";

            String charset = "utf-8";

            InternetAddress[] to = new InternetAddress[1];

            InternetAddress from = new

        InternetAddress(sendFrom,personalName,charset);

            to[0] =new InternetAddress(sendTo,personalName,charset);

            msg.setFrom(from);

            msg.setRecipients(Message.RecipientType.TO,to);

            msg.setSubject(subject,charset);

            msg.setText(body,charset);

            msg.setSentDate(new Date()); 

            //发送邮件

            t.sendMessage(msg, msg.getAllRecipients());

        }

        catch (Exception e){

            e.printStackTrace();

        }

        return 0;

    }//end of action

    以上代码运行成功后,Outlook即可收到一封邮件,如图8-19所示。

     

    图8-19  Java Vuser成功回放脚本并发送邮件

    8.3.4  参数化增强脚本

    以上脚本中的配置信息和邮件字段信息可使用参数化方法,转移到参数表中存储。参数化后,代码有如下变化:

    public int action() {

        boolean ssl = false;

         //获得SMTP环境

         Properties props = System.getProperties();

         //设置SMTP主机地址

         props.put("mail.smtp.host", "<SMTPHost>");

         //设置SMTP端口号

         props.put("mail.smtp.port","<SMTPPort>");

         //设置SMTP用户名

         props.put("mail.smtp.user","<UserName>");

         Session session=Session.getInstance(System.getProperties(),null);   

         //设置邮件header字段

         String mailer = "<MailHeadere>";

         //设置收件人

         String sendTo = "<SendTo>";

         //设置抄送人

         String sendCC = "<sendCC>";

         //设置发件人

         String sendFrom = "<sendFrom>";

         try{

            //获得发送实体

            SMTPTransport t = (SMTPTransport)session.getTransport(ssl ? "smtps" : "smtp");

            //与SMTP主机相连

            t.connect("<SMTPHost>","<UserName>","<Passwd>");

            MimeMessage msg = new MimeMessage(session);

            //设置邮件各个字段

            String subject = "<mailSubject>";

            String personalName = "<PersonalName>";

            String body = "<mailBody>";

            String charset = "utf-8";

            InternetAddress[] to = new InternetAddress[1];

            InternetAddress from = new

        InternetAddress(sendFrom,personalName,charset);

            to[0] =new InternetAddress(sendTo,personalName,charset);

            msg.setFrom(from);

            msg.setRecipients(Message.RecipientType.TO,to);

            msg.setSubject(subject,charset);

            msg.setText(body,charset);

            msg.setSentDate(new Date()); 

            //发送邮件

            t.sendMessage(msg, msg.getAllRecipients());

         }

        catch (Exception e){

            e.printStackTrace();

        }

         return 0;

    }//end of action

    参数化后,验证脚本,能够通过。

    我们就可以在参数表中更改测试数据,发送不同字符的邮件了!

    中文邮件:如图8-20所示。

     

    图8-20  Java Vuser参数化后成功发送中文邮件

    日文邮件:如图8-21所示。

    由此可见,通过Java Vuser的自开发程序代码,脚本的灵活性大大增强。这是录制脚本无法比拟的优势。

    录制和开发是VU生成脚本的两种方式,我们应该根据项目需求、时间、资源等综合因素考虑选择最合适的方式。

     

    图8-21  Java Vuser参数化后成功发送日文邮件

  • 利用Java编写简单的WebService实例(转)

    2010-06-21 11:31:05

    利用Java编写简单的WebService实例

          使用Axis编写WebService比较简单,就我的理解,WebService的实现代码和编写Java代码其实没有什么区别,主要是将哪些Java类发布为WebService。下面是一个从编写测试例子到发布WebService,以及编写测试代码的过程介绍。

          本例子的WebService提供了两个方法,分别是sayHello和sayHelloToPerson,第一个只是返回一个"Hello"字符串,没有参数,第二个函数接受一个字符串作为参数,返回"Hello 参数值",该例子比较简单,但是清楚的说明了从编写代码到发布为WebService以及测试编写好的WebService全过程。

    编写服务代码

          服务代码提供了两个函数,分别为sayHello和sayHelloToPerson,源代码如下:

     


    /*
     * File name: HelloService.java
     * 
     * Version: v1.0
     * 
     * Created on Aug 2, 2008 9:40:20 AM
     * 
     * Designed by Stephen
     * 
     * (c)Copyright 2008
     
    */

    package com.sinosoft.webservice;

    /**
     * 
    @author Stephen
     * 
     * Test web service
     
    */

    public class HelloService {
        
    /**
         * 不带参数的函数
         * 
         * 
    @return 返回Hello字符串
         
    */

        
    public String sayHello() {
            
    return "Hello";
        }


        
    /**
         * 带参数的函数
         * 
         * 
    @param name
         *            名称
         * 
    @return 返回加上名称的欢迎词
         
    */

        
    public String sayHelloToPerson(String name) {
            
    if (name == null || name.equals("")) {
                name 
    = "nobody";
            }

            
    return "Hello " + name;
        }

    }

    发布WebService

          要将上边写的HelloService类发布为WebService,需要先搭建Web应用。下面是在Tomcat下使用Axis创建WebService服务的例子。

    在Tomcat下创建Web应用

         在该例子中,在Tomcat下创建了一个context path为ws的WEB应用。

         1. 在Tomcat的webapps下创建如下文件系统

              ws

                   WEB-INF

                        lib

                        classes

         2. 在WEB-INF文件夹下创建web.xml文件,该文件的内容如下:

     


    <?xml version="1.0" encoding="ISO-8859-1"?>

    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web
    Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"
    >

    <web-app>
      
    <display-name>Apache-Axis</display-name>
        
        
    <listener>
            
    <listener-class>org.apache.axis.transport.http.AxisHTTPSessionListener</listener-class>
        
    </listener>
        
      
    <servlet>
        
    <servlet-name>AxisServlet</servlet-name>
        
    <display-name>Apache-Axis Servlet</display-name>
        
    <servlet-class>
            org.apache.axis.transport.http.AxisServlet
        
    </servlet-class>
      
    </servlet>

      
    <servlet>
        
    <servlet-name>AdminServlet</servlet-name>
        
    <display-name>Axis Admin Servlet</display-name>
        
    <servlet-class>
            org.apache.axis.transport.http.AdminServlet
        
    </servlet-class>
        
    <load-on-startup>100</load-on-startup>
      
    </servlet>

      
    <servlet>
        
    <servlet-name>SOAPMonitorService</servlet-name>
        
    <display-name>SOAPMonitorService</display-name>
        
    <servlet-class>
            org.apache.axis.monitor.SOAPMonitorService
        
    </servlet-class>
        
    <init-param>
          
    <param-name>SOAPMonitorPort</param-name>
          
    <param-value>5001</param-value>
        
    </init-param>
        
    <load-on-startup>100</load-on-startup>
      
    </servlet>

      
    <servlet-mapping>
        
    <servlet-name>AxisServlet</servlet-name>
        
    <url-pattern>/servlet/AxisServlet</url-pattern>
      
    </servlet-mapping>

      
    <servlet-mapping>
        
    <servlet-name>AxisServlet</servlet-name>
        
    <url-pattern>*.jws</url-pattern>
      
    </servlet-mapping>

      
    <servlet-mapping>
        
    <servlet-name>AxisServlet</servlet-name>
        
    <url-pattern>/services/*</url-pattern>
      
    </servlet-mapping>

      
    <servlet-mapping>
        
    <servlet-name>SOAPMonitorService</servlet-name>
        
    <url-pattern>/SOAPMonitor</url-pattern>
      
    </servlet-mapping>

     
    <!-- uncomment this if you want the admin servlet -->
     
    <!--
      <servlet-mapping>
        <servlet-name>AdminServlet</servlet-name>
        <url-pattern>/servlet/AdminServlet</url-pattern>
      </servlet-mapping>
     
    -->

        
    <session-config>
            
    <!-- Default to 5 minute session timeouts -->
            
    <session-timeout>5</session-timeout>
        
    </session-config>

        
    <!-- currently the W3C havent settled on a media type for WSDL;
        http://www.w3.org/TR/2003/WD-wsdl12-20030303/#ietf-draft
        for now we go with the basic 'it's XML' response 
    -->
      
    <mime-mapping>
        
    <extension>wsdl</extension>
         
    <mime-type>text/xml</mime-type>
      
    </mime-mapping>
      

      
    <mime-mapping>
        
    <extension>xsd</extension>
        
    <mime-type>text/xml</mime-type>
      
    </mime-mapping>

      
    <welcome-file-list id="WelcomeFileList">
        
    <welcome-file>index.jsp</welcome-file>
        
    <welcome-file>index.html</welcome-file>
        
    <welcome-file>index.jws</welcome-file>
      
    </welcome-file-list>

    </web-app>

     

         在上面的web.xml中主要是配置了axis的相关配置。

    axis的相关配置

         在上述的web.xml文件中已经对axis进行了配置,但是还需要进行额外的配置。

         复制axis相关的jar文件

         将axis的相关jar文件复制到WEB-INF\lib文件夹下。这些文件包括:

    activation.jar
    axis.jar
    axis-ant.jar
    axis-schema.jar
    commons-discovery-0.2.jar
    commons-logging-1.0.4.jar
    jaxrpc.jar
    log4j-1.2.8.jar
    mailapi.jar
    saaj.jar
    wsdl4j-1.5.1.jar
    xmlsec-1.3.0.jar

         复制WebService服务主文件

         将HelloService.java编译后的class文件复制到WEB-INF\classes文件夹下,也就是说在WEB-INF\classes文件夹下的文件夹结构为:com\sinosoft\webservice,在webservice文件夹下有一个helloservice.class文件。

    测试发布的Web应用

         启动Tomcat服务,打开IE浏览器,访问地址http:host:port/ws/services,如果看到如下界面就说明AXIS部署成功了。

     

    发布WebService

         发布WebService需要使用现有的AdminService来实现,这里我写了一个批处理文件来发布WebService,以后如果需要发布其他文件,只需要修改相应的参数就可以了。

    创建deploy.wsdd文件

         文件deploy.wsdd内容如下所示:

     


    <?xml version="1.0" encoding="UTF-8"?>
    <deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
        
    <service name="HelloServices" provider="java:RPC">
            
    <parameter name="className" value="com.sinosoft.webservice.HelloService"/>
            
    <parameter name="allowedMethods" value="*"/>
        
    </service>
    </deployment>

    创建发布WebService服务的批处理文件

         批处理文件deploywebservice.bat内容如下:

     


    java -cp E:\Stephen\Lib\axislib\activation.jar;E:\Stephen\Lib\axislib\axis-ant.jar;E:\Stephen\Lib\axislib\axis-schema.jar;E:\Stephen\Lib\axislib\axis.jar;E:\Stephen\Lib\axislib\commons-discovery-0.2.jar;E:\Stephen\Lib\axislib\commons-logging-1.0.4.jar;E:\Stephen\Lib\axislib\jaxrpc.jar;E:\Stephen\Lib\axislib\log4j-1.2.8.jar;E:\Stephen\Lib\axislib\mailapi.jar;E:\Stephen\Lib\axislib\saaj.jar;E:\Stephen\Lib\axislib\wsdl4j-1.5.1.jar;E:\Stephen\Lib\axislib\xmlsec-1.3.0.jar org.apache.axis.client.AdminClient -lhttp://localhost:8090/ws/services/AdminService deploy.wsdd

         其中E:\Stephen\Lib\axislib是存放axis对应的jar文件的文件夹,现在将所有的jar文件都加入到classpath中进行执行。

         -l后的参数是本地要发布WebService的AdminService对应的访问地址。

         最后deploy.wsdd是对应的配置文件名称。

    发布WebService服务

         将deploy.wsdd文件和deploywebservice.bat文件复制到同一个文件夹下,执行deploywebservice.bat批处理文件,就可以将deploy.wsdd中描述的Java类发布为WebService。发布完成之后在访问http://host:port/ws/services如下图所示:

     

     

    从上图可以看出,发布成功后,多了一个HelloServices的服务。这样就说明HelloService发布成功了。

    查看HelloServices的wsdl

         访问http://host:port/ws/services/HelloServices?wsdl可以看到如下wsdl的内容:

     

    Code
  • VS2008中使用用ANKHSVN插件

    2010-06-17 08:46:34

            插件安装很简单,在经过以上步骤后,并且确认你已经在vs中安装好了ankhsvn插件,那么恭喜你,你已经可以在VS中使用svn的强大代码管理功能了。首先打开Visual Studio,点击 File(文件)你将会看到多出了Subversion选项,点击Open from subversion…URL中输入http://服务器IP/repository/repos1,然后会提示输入http认证的用户名和密码,输入确定后就可以看到已经创建好的项目了选中要打开的项目.sln文件打开,然后需要制定一个本地存放路径,设定好后点击OK,就可以看到程序从服务器传到了本地。

              在程序修改时一般操作流程及方法

              打开项目,首选点update,更新为最新版本:

    Snap3

             然后对需要修改的程序文件,进行锁定:

    Snap5

              并且可以加入提示信息: 

    Snap4    

             修改后更新程序:
             注意两个提交,用下面的提交后,仍然锁定程序,上面的提交,在提交后自动解除锁定。

    Snap6

             另外,在提交前最好加上注释,在上面的界面中点击如下按钮可以在message中输入备注信息。

    Snap7

              手动解锁:

      Snap8

              查看修改编码者信息:

    Snap9

             查看版本历史:

    Snap10

     Snap11

          比较版本点击diff:

    Snap12

           撤消未提交的更改:

    Snap14

           查看待提交信息:

    Snap13

              Change列的不同状态,代表不同的含义,具体如下:
                     New 表示新增加的文件,还没有增加到版本库中。 
                     Added 表示已经使用ADD功能,但是还没提交.
                     Edited 表示已经修改但还没保存. 
                     Modified Edited状态保存之后的状态.
                     Conflicted 有冲突时的状态 
                     Deleted, Copied 改名之后,列示的状态。Deleted也表示删除文件的状态。

  • JAVA调用DLL动态库(JNI)(转)

    2010-06-14 17:48:20

     

    文章分类:Java编程
    一、JAVA中所需要做的工作 
      在JAVA程序中,首先需要在类中声明所调用的库名称,如下: 
    static { 
    System.loadLibrary(“goodluck”); 
    }

      在这里,库的扩展名字可以不用写出来,究竟是DLL还是SO,由系统自己判定。 

      还需对将要调用的方法做本地声明,要害字为native。且只需要声明,而不需要具体实现。如下: 
    public native static void set(int i); 
    public native static int get();

      然后编译该JAVA程序文件,生成CLASS,再用JAVAH命令,JNI就会生成C/C 的头文件。 

      例如程序testdll.java,内容为: 
    public class testdll 

    static 

    System.loadLibrary("goodluck"); 

    public native static int get(); 
    public native static void set(int i); 
    public static void main(String[] args) 

    testdll test = new testdll(); 
    test.set(10); 
    System.out.println(test.get()); 

    }

      用javac testdll.java编译它,会生成testdll.class。 
      再用javah testdll,则会在当前目录下生成testdll.h文件,这个文件需要被C/C 程序调用来生成所需的库文件。 

      二、C/C 中所需要做的工作 
      对于已生成的.h头文件,C/C 所需要做的,就是把它的各个方法具体的实现。然后编译连接成库文件即可。再把库文件拷贝到JAVA程序的路径下面,就可以用JAVA调用C/C 所实现的功能了。 
      接上例子。我们先看一下testdll.h文件的内容: 
    /* DO NOT EDIT THIS FILE - it is machine generated */ 
    #include 
    /* Header for class testdll */ 
    #ifndef _Included_testdll 
    #define _Included_testdll 
    #ifdef __cplusplus 
    extern "C" { 
    #endif 
    /* 
    * Class: testdll 
    * Method: get 
    * Signature: ()I 
    */ 
    JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass); 
    /* 
    * Class: testdll 
    * Method: set 
    * Signature: (I)V 
    */ 
    JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint); 
    #ifdef __cplusplus 

    #endif 
    #endif

      在具体实现的时候,我们只关心两个函数原型 
      JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass); 和 
      JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint);

      这里JNIEXPORT和JNICALL都是JNI的要害字,表示此函数是要被JNI调用的。而jint是以JNI为中介使JAVA的int类型与本地的int沟通的一种类型,我们可以视而不见,就当做int使用。函数的名称是JAVA_再加上java程序的package路径再加函数名组成的。参数中,我们也只需要关心在JAVA程序中存在的参数,至于JNIEnv*和jclass我们一般没有必要去碰它。 

      好,下面我们用testdll.cpp文件具体实现这两个函数: 
    #include "testdll.h" 
    int i = 0; 
    JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass) 

    return i; 

    JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint j) 

    i = j; 
    }

      编译连接成库文件,本例是在WINDOWS下做的,生成的是DLL文件。并且名称要与JAVA中需要调用的一致,这里就是goodluck.dll 。把goodluck.dll拷贝到testdll.class的目录下,java testdll运行它,就可以观察到结果了。 
    我的项目比较复杂,需要调用动态链接库,这样在JNI传送参数到C程序时,需要对参数进行处理转换。才可以被C程序识别。
      大体程序如下:
    public class SendSMS { 
    static 

    System.out.println(System.getProperty("java.library.path")); 
    System.loadLibrary("sms"); 

    public native static int SmsInit(); 
    public native static int SmsSend(byte[] mobileNo, byte[] smContent); 
    }

      在这里要注重的是,path里一定要包含类库的路径,否则在程序运行时会抛出异常:
      java.lang.UnsatisfiedLinkError: no sms in java.library.path
      at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1491)
      at java.lang.Runtime.loadLibrary0(Runtime.java:788)
      at java.lang.System.loadLibrary(System.java:834)
      at com.mobilesoft.sms.mobilesoftinfo.SendSMS.(SendSMS.java:14)
      at com.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18)
      Exception in thread "main"

      指引的路径应该到.dll文件的上一级,假如指到.dll,则会报:
      java.lang.UnsatisfiedLinkError: C:\sms.dll: Can’t find dependent libraries
      at java.lang.ClassLoader$NativeLibrary.load(Native Method)
      at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1560)
      at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1485)
      at java.lang.Runtime.loadLibrary0(Runtime.java:788)
      at java.lang.System.loadLibrary(System.java:834)
      at com.mobilesoft.sms.mobilesoftinfo.test.main(test.java:18)
      Exception in thread "main"

      通过编译,生成com_mobilesoft_sms_mobilesoftinfo_SendSMS.h头文件。(建议使用Jbuilder进行编译,操作比较简单!)这个头文件就是Java和C之间的纽带。要非凡注重的是方法中传递的参数jbyteArray,这在接下来的过程中会重点介绍。
    /* DO NOT EDIT THIS FILE - it is machine generated */ 
    #include 
    /* Header for class com_mobilesoft_sms_mobilesoftinfo_SendSMS */ 
    #ifndef _Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS 
    #define _Included_com_mobilesoft_sms_mobilesoftinfo_SendSMS 
    #ifdef __cplusplus 
    extern "C" { 
    #endif 
    /* 
    * Class: com_mobilesoft_sms_mobilesoftinfo_SendSMS 
    * Method: SmsInit 
    * Signature: ()I 
    */ 
    JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit 
    (JNIEnv *, jclass); 
    /* 
    * Class: com_mobilesoft_sms_mobilesoftinfo_SendSMS 
    * Method: SmsSend 
    * Signature: ([B[B)I 
    */ 
    JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend 
    (JNIEnv *, jclass, jbyteArray, jbyteArray); 
    #ifdef __cplusplus 

    #endif 
    #endif

      对于我要调用的C程序的动态链接库,C程序也要提供一个头文件,sms.h。这个文件将要调用的方法罗列了出来。
    /* 
    * SMS API 
    * Author: yippit 
    * Date: 2004.6.8 
    */ 
    #ifndef MCS_SMS_H 
    #define MCS_SMS_H 
    #define DLLEXPORT __declspec(dllexport) 
    /*sms storage*/ 
    #define SMS_SIM 0 
    #define SMS_MT 1 
    /*sms states*/ 
    #define SMS_UNREAD 0 
    #define SMS_READ 1 
    /*sms type*/ 
    #define SMS_NOPARSE -1 
    #define SMS_NORMAL 0 
    #define SMS_FLASH 1 
    #define SMS_MMSNOTI 2 
    typedef struct tagSmsEntry { 
    int index; /*index, start from 1*/ 
    int status; /*read, unread*/ 
    int type; /*-1-can’t parser 0-normal, 1-flash, 2-mms*/ 
    int storage; /*SMS_SIM, SMS_MT*/ 
    char date[24]; 
    char number[32]; 
    char text[144]; 
    } SmsEntry; 
    DLLEXPORT int SmsInit(void); 
    DLLEXPORT int SmsSend(char *phonenum, char *content); 
    DLLEXPORT int SmsSetSCA(char *sca); 
    DLLEXPORT int SmsGetSCA(char *sca); 
    DLLEXPORT int SmsSetInd(int ind); 
    DLLEXPORT int SmsGetInd(void); 
    DLLEXPORT int SmsGetInfo(int storage, int *max, int *used); 
    DLLEXPORT int SmsSaveFlash(int flag); 
    DLLEXPORT int SmsRead(SmsEntry *entry, int storage, int index); 
    DLLEXPORT int SmsDelete(int storage, int index); 
    DLLEXPORT int SmsModifyStatus(int storage, int index); /*unread -> read*/ 
    #endif
    在有了这两个头文件之后,就可以进行C程序的编写了。也就是实现对JNI调用的两个方法。在网上的资料中,由于调用的方法实现的都比较简单,(大多是打印字符串等)所以避开了JNI中最麻烦的部分,也是最要害的部分,参数的传递。由于Java和C的编码是不同的,所以传递的参数是要进行再处理,否则C程序是会对参数在编译过程中提出警告,例如;warning C4024: ’SmsSend’ : different types for formal and actual parameter 2等。
      Sms.c的程序如下:
    #include "sms.h" 
    #include "com_mobilesoft_sms_mobilesoftinfo_SendSMS.h" 
    JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsInit(JNIEnv * env, jclass jobject) 

    return SmsInit(); 


    JNIEXPORT jint JNICALL Java_com_mobilesoft_sms_mobilesoftinfo_SendSMS_SmsSend(JNIEnv * env, jclass jobject, jbyteArray mobileno, jbyteArray smscontent) 

    char * pSmscontent ; 
    //jsize theArrayLengthJ = (*env)->GetArrayLength(env,mobileno); 
    jbyte * arrayBody = (*env)->GetByteArrayElements(env,mobileno,0); 
    char * pMobileNo = (char *)arrayBody; 
    printf("[%s]\n ", pMobileNo); 
    //jsize size = (*env)->GetArrayLength(env,smscontent); 
    arrayBody = (*env)->GetByteArrayElements(env,smscontent,0); 
    pSmscontent = (char *)arrayBody; 
    printf("<%s>\n", pSmscontent); 
    return SmsSend(pMobileNo,pSmscontent); 
    }

      对于C或C ,在程序上是会有稍微的不同,这可以由读者对其进行适当的修改。这里要注重的是GetArrayLength,GetByteArrayElements等这些JNI中已经包含的方法,这些方法是专门对转换参数类型而提供的。具体的方法有很多,在下一篇中会做专门的介绍。
      在完成了上述的文件后,可以对sms.c进行编译,生成.dll文件(建议在release中编译,这样动态链接库的容积会比较小!)
      完成.dll文件的编译后,就可以在Java中调用C程序中的方法了。例如文件test.java
    public class test { 
    public test() { 

    public static void main(String[] args) { 
    byte[] mobileno = { 
    0x31, 0x33, 0x36, 0x36, 0x31, 0x36, 0x33, 0x30, 0x36, 0x36, 0x37, 0x00}; 
    String smscontentemp = "早上好"; 
    byte[] temp = {0}; 
    try { 
    byte[] smscontentdb = smscontentemp.getBytes("gbk"); 
    byte[] smscontent = new byte[smscontentdb.length temp.length]; 
    System.arraycopy(smscontentdb, 0, smscontent, 0, smscontentdb.length); 
    System.arraycopy(temp, 0, smscontent, smscontentdb.length, temp.length); 
    SendSMS sendSMS = new SendSMS(); 
    sendSMS.SmsInit(); 
    if (sendSMS.SmsSend(mobileno, smscontent) >= 0) { 
    System.out.println("chenggong !"); 

    else { 
    System.out.println("shibai !"); 

    }catch (Exception ex) {} 

    }

      在这个文件中要注重的有一点,就是在传递字节数组到C程序中时,最后的结尾一定要以0结束。这是一个偷懒的做法,不过是个有效的做法。因为大多数情况下,接口是由第三方提供的。所以我们一般是不知道在C的方法里,具体是怎么处理参数的。而C又是要求数组是有长度。所以,在Java中,假如你不想写程序传数组的长度,那么在数组中以0结尾就是最方便的方法了。当然,假如有更好的方法也希望大家提出。

      到这里,一个完整的Java通过JNI调用动态链接库的程序就完成了。实际上也不是很复杂。只要多注重一下细节,是很轻易得出来的。 
  • Java正则表达式详解-Jakarta-ORO API(转)

    2010-06-13 17:17:51

    如果你曾经用过Perl或任何其他内建正则表达式支持的语言,你一定知道用正则表达式处理文本和匹配模式是多么简单。如果你不熟悉这个术语,那么“正则表达式”(Regular Expression)就是一个字符构成的串,它定义了一个用来搜索匹配字符串的模式。
    许多语言,包括Perl、PHP、Python、JavaScript和JScript,都支持用正则表达式处理文本,一些文本编辑器用正则表达式实现高级“搜索-替换”功能。那么Java又怎样呢?本文写作时,一个包含了用正则表达式进行文本处理的Java规范需求(Specification Request)已经得到认可,你可以期待在JDK的下一版本中看到它。
    然而,如果现在就需要使用正则表达式,又该怎么办呢?你可以从Apache.org下载源代码开放的Jakarta-ORO库。本文接下来的内容先简要地介绍正则表达式的入门知识,然后以Jakarta-ORO API为例介绍如何使用正则表达式。
    一、正则表达式基础知识
    我们先从简单的开始。假设你要搜索一个包含字符“cat”的字符串,搜索用的正则表达式就是“cat”。如果搜索对大小写不敏感,单词“catalog”、“Catherine”、“sophisticated”都可以匹配。也就是说:
    1.1 句点符号
    假设你在玩英文拼字游戏,想要找出三个字母的单词,而且这些单词必须以“t”字母开头,以“n”字母结束。另外,假设有一本英文字典,你可以用正则表达式搜索它的全部内容。要构造出这个正则表达式,你可以使用一个通配符——句点符号“.”。这样,完整的表达式就是“t.n”,它匹配“tan”、“ten”、“tin”和“ton”,还匹配“t#n”、“tpn”甚至“t n”,还有其他许多无意义的组合。这是因为句点符号匹配所有字符,包括空格、Tab字符甚至换行符:
    1.2 方括号符号
    为了解决句点符号匹配范围过于广泛这一问题,你可以在方括号(“[]”)里面指定看来有意义的字符。此时,只有方括号里面指定的字符才参与匹配。也就是说,正则表达式“t[aeio]n”只匹配“tan”、“Ten”、“tin”和“ton”。但“Toon”不匹配,因为在方括号之内你只能匹配单个字符:
    1.3 “或”符号
    如果除了上面匹配的所有单词之外,你还想要匹配“toon”,那么,你可以使用“|”操作符。“|”操作符的基本意义就是“或”运算。要匹配“toon”,使用“t(a|e|i|o|oo)n”正则表达式。这里不能使用方扩号,因为方括号只允许匹配单个字符;这里必须使用圆括号“()”。圆括号还可以用来分组,具体请参见后面介绍。
    1.4 表示匹配次数的符号
    表一显示了表示匹配次数的符号,这些符号用来确定紧靠该符号左边的符号出现的次数:

    假设我们要在文本文件中搜索美国的社会安全号码。这个号码的格式是999-99-9999。用来匹配它的正则表达式如图一所示。在正则表达式中,连字符(“-”)有着特殊的意义,它表示一个范围,比如从0到9。因此,匹配社会安全号码中的连字符号时,它的前面要加上一个转义字符“\”。

    图一:匹配所有123-12-1234形式的社会安全号码

    假设进行搜索的时候,你希望连字符号可以出现,也可以不出现——即,999-99-9999和999999999都属于正确的格式。这时,你可以在连字符号后面加上“?”数量限定符号,如图二所示:

    图二:匹配所有123-12-1234和123121234形式的社会安全号码

    下面我们再来看另外一个例子。美国汽车牌照的一种格式是四个数字加上二个字母。它的正则表达式前面是数字部分“[0-9]{4}”,再加上字母部分“[A-Z]{2}”。图三显示了完整的正则表达式。

    图三:匹配典型的美国汽车牌照号码,如8836KV

    1.5 “否”符号
    “^”符号称为“否”符号。如果用在方括号内,“^”表示不想要匹配的字符。例如,图四的正则表达式匹配所有单词,但以“X”字母开头的单词除外。

    图四:匹配所有单词,但“X”开头的除外

    1.6 圆括号和空白符号
    假设要从格式为“June 26, 1951”的生日日期中提取出月份部分,用来匹配该日期的正则表达式可以如图五所示:

    图五:匹配所有Moth DD,YYYY格式的日期

    新出现的“\s”符号是空白符号,匹配所有的空白字符,包括Tab字符。如果字符串正确匹配,接下来如何提取出月份部分呢?只需在月份周围加上一个圆括号创建一个组,然后用ORO API(本文后面详细讨论)提取出它的值。修改后的正则表达式如图六所示:

    图六:匹配所有Month DD,YYYY格式的日期,定义月份值为第一个组

    1.7 其它符号
    为简便起见,你可以使用一些为常见正则表达式创建的快捷符号。如表二所示:
    表二:常用符号

    例如,在前面社会安全号码的例子中,所有出现“[0-9]”的地方我们都可以使用“\d”。修改后的正则表达式如图七所示:

    图七:匹配所有123-12-1234格式的社会安全号码

    二、Jakarta-ORO库
    有许多源代码开放的正则表达式库可供Java程序员使用,而且它们中的许多支持Perl 5兼容的正则表达式语法。我在这里选用的是Jakarta-ORO正则表达式库,它是最全面的正则表达式API之一,而且它与Perl 5正则表达式完全兼容。另外,它也是优化得最好的API之一。
    Jakarta-ORO库以前叫做OROMatcher,Daniel Savarese大方地把它赠送给了Jakarta Project。你可以按照本文最后参考资源的说明下载它。
    我首先将简要介绍使用Jakarta-ORO库时你必须创建和访问的对象,然后介绍如何使用Jakarta-ORO API。
    ▲ PatternCompiler对象
    首先,创建一个Perl5Compiler类的实例,并把它赋值给PatternCompiler接口对象。Perl5Compiler是PatternCompiler接口的一个实现,允许你把正则表达式编译成用来匹配的Pattern对象。
    ▲ Pattern对象
    要把正则表达式编译成Pattern对象,调用compiler对象的compile()方法,并在调用参数中指定正则表达式。例如,你可以按照下面这种方式编译正则表达式“t[aeio]n”:
    默认情况下,编译器创建一个大小写敏感的模式(pattern)。因此,上面代码编译得到的模式只匹配“tin”、“tan”、 “ten”和“ton”,但不匹配“Tin”和“taN”。要创建一个大小写不敏感的模式,你应该在调用编译器的时候指定一个额外的参数:
    创建好Pattern对象之后,你就可以通过PatternMatcher类用该Pattern对象进行模式匹配。
    ▲ PatternMatcher对象
    PatternMatcher对象根据Pattern对象和字符串进行匹配检查。你要实例化一个Perl5Matcher类并把结果赋值给PatternMatcher接口。Perl5Matcher类是PatternMatcher接口的一个实现,它根据Perl 5正则表达式语法进行模式匹配:
    使用PatternMatcher对象,你可以用多个方法进行匹配操作,这些方法的第一个参数都是需要根据正则表达式进行匹配的字符串:
    · boolean matches(String input, Pattern pattern):当输入字符串和正则表达式要精确匹配时使用。换句话说,正则表达式必须完整地描述输入字符串。
    · boolean matchesPrefix(String input, Pattern pattern):当正则表达式匹配输入字符串起始部分时使用。
    · boolean contains(String input, Pattern pattern):当正则表达式要匹配输入字符串的一部分时使用(即,它必须是一个子串)。
    另外,在上面三个方法调用中,你还可以用PatternMatcherInput对象作为参数替代String对象;这时,你可以从字符串中最后一次匹配的位置开始继续进行匹配。当字符串可能有多个子串匹配给定的正则表达式时,用PatternMatcherInput对象作为参数就很有用了。用PatternMatcherInput对象作为参数替代String时,上述三个方法的语法如下:
    · boolean matches(PatternMatcherInput input, Pattern pattern)
    · boolean matchesPrefix(PatternMatcherInput input, Pattern pattern)
    · boolean contains(PatternMatcherInput input, Pattern pattern)
    三、应用实例
    下面我们来看看Jakarta-ORO库的一些应用实例。
    3.1 日志文件处理
    任务:分析一个Web服务器日志文件,确定每一个用户花在网站上的时间。在典型的BEA WebLogic日志文件中,日志记录的格式如下:
    分析这个日志记录,可以发现,要从这个日志文件提取的内容有两项:IP地址和页面访问时间。你可以用分组符号(圆括号)从日志记录提取出IP地址和时间标记。
    首先我们来看看IP地址。IP地址有4个字节构成,每一个字节的值在0到255之间,各个字节通过一个句点分隔。因此,IP地址中的每一个字节有至少一个、最多三个数字。图八显示了为IP地址编写的正则表达式:

    图八:匹配IP地址

    IP地址中的句点字符必须进行转义处理(前面加上“\”),因为IP地址中的句点具有它本来的含义,而不是采用正则表达式语法中的特殊含义。句点在正则表达式中的特殊含义本文前面已经介绍。
    日志记录的时间部分由一对方括号包围。你可以按照如下思路提取出方括号里面的所有内容:首先搜索起始方括号字符(“[”),提取出所有不超过结束方括号字符(“]”)的内容,向前寻找直至找到结束方括号字符。图九显示了这部分的正则表达式。

    图九:匹配至少一个字符,直至找到“]”

    现在,把上述两个正则表达式加上分组符号(圆括号)后合并成单个表达式,这样就可以从日志记录提取出IP地址和时间。注意,为了匹配“- -”(但不提取它),正则表达式中间加入了“\s-\s-\s”。完整的正则表达式如图十所示。

    图十:匹配IP地址和时间标记

    现在正则表达式已经编写完毕,接下来可以编写使用正则表达式库的Java代码了。
    为使用Jakarta-ORO库,首先创建正则表达式字符串和待分析的日志记录字符串:
    这里使用的正则表达式与图十的正则表达式差不多完全相同,但有一点例外:在Java中,你必须对每一个向前的斜杠(“\”)进行转义处理。图十不是Java的表示形式,所以我们要在每个“\”前面加上一个“\”以免出现编译错误。遗憾的是,转义处理过程很容易出现错误,所以应该小心谨慎。你可以首先输入未经转义处理的正则表达式,然后从左到右依次把每一个“\”替换成“\\”。如果要复检,你可以试着把它输出到屏幕上。
    初始化字符串之后,实例化PatternCompiler对象,用PatternCompiler编译正则表达式创建一个Pattern对象:
    现在,创建PatternMatcher对象,调用PatternMatcher接口的contain()方法检查匹配情况:
    接下来,利用PatternMatcher接口返回的MatchResult对象,输出匹配的组。由于logEntry字符串包含匹配的内容,你可以看到类如下面的输出:
    3.2 HTML处理实例一
    下面一个任务是分析HTML页面内FONT标记的所有属性。HTML页面内典型的FONT标记如下所示:
    程序将按照如下形式,输出每一个FONT标记的属性:
    在这种情况下,我建议你使用两个正则表达式。第一个如图十一所示,它从字体标记提取出“"face="Arial, Serif" size="+2" color="red"”。

    图十一:匹配FONT标记的所有属性

    第二个正则表达式如图十二所示,它把各个属性分割成名字-值对。

    图十二:匹配单个属性,并把它分割成名字-值对

    分割结果为:
    现在我们来看看完成这个任务的Java代码。首先创建两个正则表达式字符串,用Perl5Compiler把它们编译成Pattern对象。编译正则表达式的时候,指定Perl5Compiler.CASE_INSENSITIVE_MASK选项,使得匹配操作不区分大小写。
    接下来,创建一个执行匹配操作的Perl5Matcher对象。
    假设有一个String类型的变量html,它代表了HTML文件中的一行内容。如果html字符串包含FONT标记,匹配器将返回true。此时,你可以用匹配器对象返回的MatchResult对象获得第一个组,它包含了FONT的所有属性:
    接下来创建一个PatternMatcherInput对象。这个对象允许你从最后一次匹配的位置开始继续进行匹配操作,因此,它很适合于提取FONT标记内属性的名字-值对。创建PatternMatcherInput对象,以参数形式传入待匹配的字符串。然后,用匹配器实例提取出每一个FONT的属性。这通过指定PatternMatcherInput对象(而不是字符串对象)为参数,反复地调用PatternMatcher对象的contains()方法完成。PatternMatcherInput对象之中的每一次迭代将把它内部的指针向前移动,下一次检测将从前一次匹配位置的后面开始。
    本例的输出结果如下:
    3.3 HTML处理实例二
    下面我们来看看另一个处理HTML的例子。这一次,我们假定Web服务器从widgets.acme.com移到了newserver.acme.com。现在你要修改一些页面中的链接:
    执行这个搜索的正则表达式如图十三所示:

    图十三:匹配修改前的链接

    如果能够匹配这个正则表达式,你可以用下面的内容替换图十三的链接:
    注意#字符的后面加上了$1。Perl正则表达式语法用$1、$2等表示已经匹配且提取出来的组。图十三的表达式把所有作为一个组匹配和提取出来的内容附加到链接的后面。
    现在,返回Java。就象前面我们所做的那样,你必须创建测试字符串,创建把正则表达式编译到Pattern对象所必需的对象,以及创建一个PatternMatcher对象:
    接下来,用com.oroinc.text.regex包Util类的substitute()静态方法进行替换,输出结果字符串:
    Util.substitute()方法的语法如下:
    这个调用的前两个参数是以前创建的PatternMatcher和Pattern对象。第三个参数是一个Substiution对象,它决定了替换操作如何进行。本例使用的是Perl5Substitution对象,它能够进行Perl5风格的替换。第四个参数是想要进行替换操作的字符串,最后一个参数允许指定是否替换模式的所有匹配子串(Util.SUBSTITUTE_ALL),或只替换指定的次数。
    【结束语】在这篇文章中,我为你介绍了正则表达式的强大功能。只要正确运用,正则表达式能够在字符串提取和文本修改中起到很大的作用。另外,我还介绍了如何在Java程序中通过Jakarta-ORO库利用正则表达式。至于最终采用老式的字符串处理方式(使用StringTokenizer,charAt,和substring),还是采用正则表达式,这就有待你自己决定了。
  • PS去除图片中的文字

    2010-06-04 11:20:30

    Photoshop 是最常用和强大的图片编辑工具,我们利用它的强大功能,可以快速去除一些图片中的 logo、文字,说到去除图片 logo 大家应该还记得早几天偶介绍的 Teorex.Inpaint 吧!今天就跟着下面简单的动画教程,利用 Photoshop 去除图片中那些恶心的位置吧!

    1.仿制图章工具:



    2.修补工具:



    3.修复画笔工具:



    4.复制相似图案:



    5.消失点:



    6.线性渐变:



    学会上面这些技巧,去除一些图片中的logo、文字应该可以得心应手了!如果你没有 Photoshop 你还可以试试 Teorex.Inpaint !
  • 学习一种新编程语言的15个练习(转)

    2010-05-28 10:10:51

     有时工作常常要求在较短的时间内学会另一种新的编程语言。我不选择去读那上百页的操作或指导手册,我只快速阅读最简单的,一般不超过15页的初级入门手册。(你知道,现在google可以很方便的帮我找到这一类的帮助文档)。我会打印这种语言的语法规则参考文档放在手边备用。(现在网上有很多这样的参考文档,你在google搜索栏里输入‘你要学习的编程语言’+‘reference card’)
     
           首先,你得先熟悉它的编译器、编译环境、编辑器快捷键或集成开发环境。从一个简单的“Hello World”程序开始,编译运行这个入门程序。使用最基础的调试工具,例如:设置断点、显示变量的值、跳到下一个或者指定的程序位置、停止调试等等。
     
           这是我使用过的一些练习,可以帮助较快的掌握一种新编程语言的基本要点。记住:很多程序对于初学者来说并不合适。
     
    (1) 在一个死循环里显示数字串(例:1,2,3,4, 5....),这个程序要在有人按下特定的退出键时结束。
     
    (2) 编写程序实现斐波纳契(Fibonacci)数列、交换两个变量的值、找出一组数中的最大或最小值。
     
    (3) 从键盘输入一组数,把它们按照升序或降序排列。
     
    (4) 雷诺数(Reynolds number)是由公式(D*v*rho)/mu计算得到的,其中D =流束的定型直径, V= 流体的平均速度, rho = 被测流体密度, mu = 流体的运动粘度。
       写一个对任何输入参数没有要求的程序。
       如果结果数< 2100,显示“Laminar flow层流状态”;
       如果结果数在2100和4000之间,显示“Transient flow过渡状态”;
       如果结果数>40000,显示“Turbulent Flow紊流状态”。
      (使用If,else,then)
     
    (5) 修改上面的程序,在显示结果完成之后附加询问'Do you want to calculate again (y/n),如果回答'y',则要求重新输入参数,如果回答'n',程序退出(Do while 循环)。如果参数mu = 0,程序如何处理,是否给出了参数错误提示?会不会出现分段错误,缓冲区溢出,信息转储,这些情况要如何处理。程序语言本身有没有做这样的工作?
     
    (6) 科学计算,加、减、乘、除、开方、平方、三次方、三角函数(sin, cos, tan)、阶乘、求反、取模等。
     
    (7) 在屏幕上输出不同格式的结果(保留5位有效数字;保留小数点后4位,其余舍去;在左边或右边补0;左对齐或右对齐)。(输入输出操作符)
     
    (8) 读取文本文件,并把它转化为HTML文件。(文件操作符/字符串)
     
    (9) 时间和日期:获取系统时间,并转换成不同格式。
    如:'DD-MON-YYYY', 'mm-dd-yyyy', 'dd/mm/yy'
     
    (10) 创建除文件名外还附加了时间和日期戳的文件。
     
    (11) 输入HTML表格,去掉中间所有的标签,然后用逗号或空格分隔文件数据。
     
    (12) 找出一个文件中的大写字母,或者唯一指定字符。
     
    (13) 完成文字的自动换行功能(参考Windows的记事本是如何自动换行的)。
     
    (14) 在数组的前面、后面、中间添加或删除数组元素。
     
    (15) 编程语言是否支持操作符重载、虚函数、引用、指针等等
  • 正则表达式30分钟入门教程(转)

    2010-04-23 15:16:30


    如何使用本教程

    最重要的是——请给我30分钟,如果你没有使用正则表达式的经验,请不要试图在30内入门——除非你是超人 :)

    别被下面那些复杂的表达式吓倒,只要跟着我一步一步来,你会发现正则表达式其实并没有你想像中的那么困难。当然,如果你看完了这篇教程之后,发现自己明白了很多,却又几乎什么都记不得,那也是很正常的——我认为,没接触过正则表达式的人在看完这篇教程后,能把提到过的语法记住80%以上的可能性为零。这里只是让你明白基本的原理,以后你还需要多练习,多使用,才能熟练掌握正则表达式。

    除了作为入门教程之外,本文还试图成为可以在日常工作中使用的正则表达式语法参考手册。就作者本人的经历来说,这个目标还是完成得不错的——你看,我自己也没能把所有的东西记下来,不是吗?

    清除格式 文本格式约定:专业术语 元字符/语法格式 正则表达式 正则表达式中的一部分(用于分析) 对其进行匹配的源字符串 对正则表达式或其中一部分的说明

    隐藏边注 本文右边有一些注释,主要是用来提供一些相关信息,或者给没有程序员背景的读者解释一些基本概念,通常可以忽略。

    正则表达式到底是什么东西?

    字符是计算机软件处理文字时最基本的单位,可能是字母,数字,标点符号,空格,换行符,汉字等等。字符串是0个或更多个字符的序列。文本也就是文字,字符串。说某个字符串匹配某个正则表达式,通常是指这个字符串里有一部分(或几部分分别)能满足表达式给出的条件。

    在编写处理字符串的程序或网页时,经常会有查找符合某些复杂规则的字符串的需要。正则表达式就是用于描述这些规则的工具。换句话说,正则表达式就是记录文本规则的代码。

    很可能你使用过Windows/Dos下用于文件查找的通配符(wildcard),也就是*?。如果你想查找某个目录下的所有的Word文档的话,你会搜索*.doc。在这里,*会被解释成任意的字符串。和通配符类似,正则表达式也是用来进行文本匹配的工具,只不过比起通配符,它能更精确地描述你的需求——当然,代价就是更复杂——比如你可以编写一个正则表达式,用来查找所有以0开头,后面跟着2-3个数字,然后是一个连字号“-”,最后是7或8位数字的字符串(像010-123456780376-7654321)。

    入门

    学习正则表达式的最好方法是从例子开始,理解例子之后再自己对例子进行修改,实验。下面给出了不少简单的例子,并对它们作了详细的说明。

    假设你在一篇英文小说里查找hi,你可以使用正则表达式hi

    这几乎是最简单的正则表达式了,它可以精确匹配这样的字符串:由两个字符组成,前一个字符是h,后一个是i。通常,处理正则表达式的工具会提供一个忽略大小写的选项,如果选中了这个选项,它可以匹配hi,HI,Hi,hI这四种情况中的任意一种。

    不幸的是,很多单词里包含hi这两个连续的字符,比如him,history,high等等。用hi来查找的话,这里边的hi也会被找出来。如果要精确地查找hi这个单词的话,我们应该使用\bhi\b

    \b是正则表达式规定的一个特殊代码(好吧,某些人叫它元字符,metacharacter),代表着单词的开头或结尾,也就是单词的分界处。虽然通常英文的单词是由空格,标点符号或者换行来分隔的,但是\b并不匹配这些单词分隔字符中的任何一个,它只匹配一个位置

    如果需要更精确的说法,\b匹配这样的位置:它的前一个字符和后一个字符不全是(一个是,一个不是或不存在)\w

    假如你要找的是hi后面不远处跟着一个Lucy,你应该用\bhi\b.*\bLucy\b

    这里,.是另一个元字符,匹配除了换行符以外的任意字符*同样是元字符,不过它代表的不是字符,也不是位置,而是数量——它指定*前边的内容可以连续重复使用任意次以使整个表达式得到匹配。因此,.*连在一起就意味着任意数量的不包含换行的字符。现在\bhi\b.*\bLucy\b的意思就很明显了:先是一个单词hi,然后是任意个任意字符(但不能是换行),最后是Lucy这个单词

    换行符就是'\n',ASCII编码为10(十六进制0x0A)的字符。

    如果同时使用其它元字符,我们就能构造出功能更强大的正则表达式。比如下面这个例子:

    0\d\d-\d\d\d\d\d\d\d\d匹配这样的字符串:以0开头,然后是两个数字,然后是一个连字号“-”,最后是8个数字(也就是中国的电话号码。当然,这个例子只能匹配区号为3位的情形)。

    这里的\d是个新的元字符,匹配一位数字(0,或1,或2,或……)-不是元字符,只匹配它本身——连字符(或者减号,或者中横线,或者随你怎么称呼它)。

    为了避免那么多烦人的重复,我们也可以这样写这个表达式:0\d{2}-\d{8}。 这里\d后面的{2}({8})的意思是前面\d必须连续重复匹配2次(8次)

    测试正则表达式

    如果你不觉得正则表达式很难读写的话,要么你是一个天才,要么,你不是地球人。正则表达式的语法很令人头疼,即使对经常使用它的人来说也是如此。由于难于读写,容易出错,所以找一种工具对正则表达式进行测试是很有必要的。

    不同的环境下正则表达式的一些细节是不相同的,本教程介绍的是微软 .Net Framework 2.0下正则表达式的行为,所以,我向你介绍一个.Net下的工具Regex Tester。首先你确保已经安装了.Net Framework 2.0,然后下载Regex Tester。这是个绿色软件,下载完后打开压缩包,直接运行RegexTester.exe就可以了。

    下面是Regex Tester运行时的截图:

    Regex Tester运行时的截图

    元字符

    现在你已经知道几个很有用的元字符了,如\b,.,*,还有\d.正则表达式里还有更多的元字符,比如\s匹配任意的空白符,包括空格,制表符(Tab),换行符,中文全角空格等\w匹配字母或数字或下划线或汉字等

    对中文/汉字的特殊处理是由.Net提供的正则表达式引擎支持的,其它环境下的具体情况请查看相关文档。

    下面来看看更多的例子:

    \ba\w*\b匹配以字母a开头的单词——先是某个单词开始处(\b),然后是字母a,然后是任意数量的字母或数字(\w*),最后是单词结束处(\b)

    好吧,现在我们说说正则表达式里的单词是什么意思吧:就是不少于一个的连续的\w。不错,这与学习英文时要背的成千上万个同名的东西的确关系不大 :)

    \d+匹配1个或更多连续的数字。这里的+是和*类似的元字符,不同的是*匹配重复任意次(可能是0次),而+则匹配重复1次或更多次

    \b\w{6}\b 匹配刚好6个字符的单词

    表1.常用的元字符
    代码说明
    .匹配除换行符以外的任意字符
    \w匹配字母或数字或下划线或汉字
    \s匹配任意的空白符
    \d匹配数字
    \b匹配单词的开始或结束
    ^匹配字符串的开始
    $匹配字符串的结束

    正则表达式引擎通常会提供一个“测试指定的字符串是否匹配一个正则表达式”的方法,如JavaScript里的RegExp.test()方法或.NET里的Regex.IsMatch()方法。这里的匹配是指是字符串里有没有符合表达式规则的部分。如果不使用^$的话,对于\d{5,12}而言,使用这样的方法就只能保证字符串里包含5到12连续位数字,而不是整个字符串就是5到12位数字。

    元字符^(和数字6在同一个键位上的符号)和$都匹配一个位置,这和\b有点类似。^匹配你要用来查找的字符串的开头,$匹配结尾。这两个代码在验证输入的内容时非常有用,比如一个网站如果要求你填写的QQ号必须为5位到12位数字时,可以使用:^\d{5,12}$

    这里的{5,12}和前面介绍过的{2}是类似的,只不过{2}匹配只能不多不少重复2次{5,12}则是重复的次数不能少于5次,不能多于12次,否则都不匹配。

    因为使用了^$,所以输入的整个字符串都要用来和\d{5,12}来匹配,也就是说整个输入必须是5到12个数字,因此如果输入的QQ号能匹配这个正则表达式的话,那就符合要求了。

    和忽略大小写的选项类似,有些正则表达式处理工具还有一个处理多行的选项。如果选中了这个选项,^$的意义就变成了匹配行的开始处和结束处

    字符转义

    如果你想查找元字符本身的话,比如你查找.,或者*,就出现了问题:你没办法指定它们,因为它们会被解释成别的意思。这时你就得使用\来取消这些字符的特殊意义。因此,你应该使用\.\*。当然,要查找\本身,你也得用\\.

    例如:deerchao\.net匹配deerchao.netC:\\Windows匹配C:\Windows

    重复

    你已经看过了前面的*,+,{2},{5,12}这几个匹配重复的方式了。下面是正则表达式中所有的限定符(指定数量的代码,例如*,{5,12}等):

    表2.常用的限定符
    代码/语法说明
    *重复零次或更多次
    +重复一次或更多次
    ?重复零次或一次
    {n}重复n次
    {n,}重复n次或更多次
    {n,m}重复n到m次

    下面是一些使用重复的例子:

    Windows\d+匹配Windows后面跟1个或更多数字

    ^\w+匹配一行的第一个单词(或整个字符串的第一个单词,具体匹配哪个意思得看选项设置)

    字符类

    要想查找数字,字母或数字,空白是很简单的,因为已经有了对应这些字符集合的元字符,但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办?

    很简单,你只需要在方括号里列出它们就行了,像[aeiou]就匹配任何一个英文元音字母[.?!]匹配标点符号(.或?或!)

    我们也可以轻松地指定一个字符范围,像[0-9]代表的含意与\d就是完全一致的:一位数字;同理[a-z0-9A-Z_]也完全等同于\w(如果只考虑英文的话)。

    下面是一个更复杂的表达式:\(?0\d{2}[) -]?\d{8}

    “(”和“)”也是元字符,后面的分组节里会提到,所以在这里需要使用转义

    这个表达式可以匹配几种格式的电话号码,像(010)88886666,或022-22334455,或02912345678等。我们对它进行一些分析吧:首先是一个转义字符\(,它能出现0次或1次(?),然后是一个0,后面跟着2个数字(\d{2}),然后是)-空格中的一个,它出现1次或不出现(?),最后是8个数字(\d{8})。

    分枝条件

    不幸的是,刚才那个表达式也能匹配010)12345678(022-87654321这样的“不正确”的格式。要解决这个问题,我们需要用到分枝条件。正则表达式里的分枝条件指的是有几种规则,如果满足其中任意一种规则都应该当成匹配,具体方法是用|把不同的规则分隔开。听不明白?没关系,看例子:

    0\d{2}-\d{8}|0\d{3}-\d{7}这个表达式能匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是4位区号,7位本地号(0376-2233445)

    \(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。你可以试试用分枝条件把这个表达式扩展成也支持4位区号的。

    \d{5}-\d{4}|\d{5}这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用分枝条件时,要注意各个条件的顺序。如果你把它改成\d{5}|\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。

    分组

    我们已经提到了怎么重复单个字符(直接在字符后面加上限定符就行了);但如果想要重复多个字符又该怎么办?你可以用小括号来指定子表达式(也叫做分组),然后你就可以指定这个子表达式的重复次数了,你也可以对子表达式进行其它一些操作(后面会有介绍)。

    (\d{1,3}\.){3}\d{1,3}是一个简单的IP地址匹配表达式。要理解这个表达式,请按下列顺序分析它:\

  • HTTP协议

    2010-04-23 09:13:44

    HTTP协议
    2007年03月03日 星期六 16:05
    计算机专家设计出HTTP,使HTTP客户(如Web浏览器)能够从HTTP服务器(Web服务器)请求信息和服务,HTTP目前协议的版本是1.1.HTTP是一种无状态的协议,无状态是指Web浏览器和Web服务器之间不需要建立持久的连接,这意味着当一个客户端向服务器端发出请求,然后Web服务器返回响应(response),连接就被关闭了,在服务器端不保留连接的有关信息.HTTP遵循请求(Request)/应答(Response)模型。(2) Web浏览器向Web服务器发送请求命令。
    HTTP(HyperText Transfer Protocol)是一套计算机通过网络进行通信的规则。计算机专家设计出HTTP,使HTTP客户(如Web浏览器)能够从HTTP服务器(Web服务器)请求信息和服务,HTTP目前协议的版本是1.1.HTTP是一种无状态的协议,无状态是指Web浏览器和Web服务器之间不需要建立持久的连接,这意味着当一个客户端向服务器端发出请求,然后Web服务器返回响应(response),连接就被关闭了,在服务器端不保留连接的有关信息.HTTP遵循请求(Request)/应答(Response)模型。Web浏览器向Web服务器发送请求,Web服务器处理请求并返回适当的应答。所有HTTP连接都被构造成一套请求和应答。

    HTTP使用内容类型,是指Web服务器向Web浏览器返回的文件都有与之相关的类型。所有这些类型在MIME Internet邮件协议上模型化,即Web服务器告诉Web浏览器该文件所具有的种类,是HTML文档、GIF格式图像、声音文件还是独立的应用程序。大多数Web浏览器都拥有一系列的可配置的辅助应用程序,它们告诉浏览器应该如何处理Web服务器发送过来的各种内容类型。

    HTTP通信机制是在一次完整的HTTP通信过程中,Web浏览器与Web服务器之间将完成下列7个步骤:

    (1) 建立TCP连接

    在HTTP工作开始之前,Web浏览器首先要通过网络与Web服务器建立连接,该连接是通过TCP来完成的,该协议与IP协议共同构建Internet,即著名的TCP/IP协议族,因此Internet又被称作是TCP/IP网络。HTTP是比TCP更高层次的应用层协议,根据规则,只有低层协议建立之后才能,才能进行更层协议的连接,因此,首先要建立TCP连接,一般TCP连接的端口号是80

    (2) Web浏览器向Web服务器发送请求命令

    一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令

    例如:GET/sample/hello.jsp HTTP/1.1

    (3) Web浏览器发送请求头信息

    浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。

    (4) Web服务器应答

    客户机向服务器发出请求后,服务器会客户机回送应答,

    HTTP/1.1 200 OK

    应答的第一部分是协议的版本号和应答状态码

    (5) Web服务器发送应答头信息

    正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。

    (6) Web服务器向浏览器发送数据

    Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据

    (7) Web服务器关闭TCP连接

    一般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码

    Connection:keep-alive

    TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。

        

    HTTP请求格式

    当浏览器向Web服务器发出请求时,它向服务器传递了一个数据块,也就是请求信息,HTTP请求信息由3部分组成:

    l 请求方法URI协议/版本

    l 请求头(Request Header)

    l 请求正文

    下面是一个HTTP请求的例子:

    GET/sample.jspHTTP/1.1

    Accept:image/gif.image/jpeg,*/*

    Accept-Language:zh-cn

    Connection:Keep-Alive

    Host:localhost

    User-Agent:Mozila/4.0(compatible;MSIE5.01;Window NT5.0)

    Accept-Encoding:gzip,deflate


    username=jinqiao&password=1234


    (1) 请求方法URI协议/版本

    请求的第一行是“方法URL议/版本”:GET/sample.jsp HTTP/1.1

    以上代码中“GET”代表请求方法,“/sample.jsp”表示URI,“HTTP/1.1代表协议和协议的版本。

    根据HTTP标准,HTTP请求可以使用多种请求方法。例如:HTTP1.1支持7种请求方法:GET、POST、HEAD、OPTIONS、PUT、DELETE和TARCE。在Internet应用中,最常用的方法是GET和POST。

    URL完整地指定了要访问的网络资源,通常只要给出相对于服务器
  • jmeter 2.3.4

    2010-03-22 19:20:38

    1,从https://svn.apache.org/repos/asf/jakarta/jmeter检出版本后,将目录下的eclipse.classpath的内容,复制到.classpath中。
    2,重启eclipse.
    3,使用ant构建项目。成功后。
    4,在run configuration中的argument的tab中,选择work directory 为other:${workspace_loc:jmeter_2.3.4/bin}
    5,选择src/core
    下的org.jmeter.core.NewDriver
  • Qtp中调用网页中的javascript函数

    2010-02-22 21:30:00



    环境:IE6.0,Qtp9.2

    Set IE = CreateObject("InternetExplorer.Application")
    oIE.Visible = True
    oIE.Navigate "http://localhost:8080/index.html"
    While oIE.Busy:Wend
    Set Doc = oIE.Document
    oDoc.parentWindow.execScript. "openwin()"
    'oDoc.parentWindow.execScript. "alert("test")"

    可以使用document.parentWindow.execScript. 后接js函数名或者js语句。
    其中openwin()是index.html中的js函数
    <script. LANGUAGE="JavaScript"   TYPE="text/javascript">
    function openwin()
    {
       alert("test");
    }
    </script>
  • linux cpu python

    2010-01-27 20:37:46

    www.chinaunix.net 作者:zlzj2010  发表于:2008-12-17 14:53:53

    经过1天的努力,还是勉强完成了cpu利用率的统计,贴出来,希望对后来这有些帮助:

    里面的sendlog大家可以不用,这是我发送日志到特定的日志服务器统一处理的。

    在Linux系统中,可以用/proc/stat文件来计算cpu的利用率(详细的解释可参考:http: //www.linuxhowtos.org/System/procstat.htm)。这个文件包含了所有CPU活动的信息,该文件中的所有值都是从系统启动开始累计到当前时刻。 
    如: 
    [sailorhzr@builder ~]$ cat /proc/stat 
    cpu 432661 13295 86656 422145968 171474 233 5346 
    cpu0 123075 2462 23494 105543694 16586 0 4615 
    cpu1 111917 4124 23858 105503820 69697 123 371 
    cpu2 103164 3554 21530 105521167 64032 106 334 
    cpu3 94504 3153 17772 105577285 21158 4 24 
    intr 1065711094 1057275779 92 0 6 6 0 4 0 3527 0 0 0 70 0 20 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7376958 0 0 0 0 0 0 0 1054602 0 0 0 0 0 0 0 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
    ctxt 19067887 
    btime 1139187531 
    processes 270014 
    procs_running 1 
    procs_blocked 0 

    输出解释 
    CPU 以及CPU0、CPU1、CPU2、CPU3每行的每个参数意思(以第一行为例)为: 
    参数 解释 
    user (432661) 从系统启动开始累计到当前时刻,用户态的CPU时间(单位:jiffies) ,不包含 nice值为负进程。 1jiffies=0.01秒 
    nice (13295) 从系统启动开始累计到当前时刻,nice值为负的进程所占用的CPU时间(单位:jiffies) 
    system (86656) 从系统启动开始累计到当前时刻,核心时间(单位:jiffies) 
    idle (422145968) 从系统启动开始累计到当前时刻,除硬盘IO等待时间以外其它等待时间(单位:jiffies) 
    iowait (171474) 从系统启动开始累计到当前时刻,硬盘IO等待时间(单位:jiffies) , 
    irq (233) 从系统启动开始累计到当前时刻,硬中断时间(单位:jiffies) 
    softirq (5346) 从系统启动开始累计到当前时刻,软中断时间(单位:jiffies) 

    CPU时间=user+system+nice+idle+iowait+irq+softirq 

    “intr”这行给出中断的信息,第一个为自系统启动以来,发生的所有的中断的次数;然后每个数对应一个特定的中断自系统启动以来所发生的次数。 
    “ctxt”给出了自系统启动以来CPU发生的上下文交换的次数。 
    “btime”给出了从系统启动到现在为止的时间(in seconds since the Unix epoch),单位为秒。 
    “processes (total_forks) 自系统启动以来所创建的任务的个数目。 
    “procs_running”:当前运行队列的任务的数目。 
    “procs_blocked”:当前被阻塞的任务的数目。 

    那么CPU利用率可以使用以下两个方法(后一种比较准确)。先取两个采样点,然后计算其差值: 
    cpu usage=(idle2-idle1)/(cpu2-cpu1)*100 
    cpu usage=[(user_2 +sys_2+nice_2) - (user_1 + sys_1+nice_1)]/(total_2 - total_1)*100 





    import sys 

    import re

    import time 

    from scribe import scribe

    from thrift.transport import TTransport, TSocket

    from thrift.protocol import TBinaryProtocol

     

    def read_cpu_usage(): 

        """Read the current system cpu usage from /proc/stat.""" 

        lines = open("/proc/stat").readlines() 

        for line in lines: 

            #print "l = %s" % line 

            l = line.split() 

            if len(l) < 5: 

                continue 

            if l[0].startswith('cpu'): 

                return l;

        return {} 

     

    def sendlog(host,port,messa):

        #"""send log to scribe

        socket = TSocket.TSocket(host=host, port=port)

        transport = TTransport.TFramedTransport(socket)

        protocol = TBinaryProtocol.TBinaryProtocol(trans=transport, strictRead=False, strictWrite=False)

        client = scribe.Client(iprot=protocol, oprot=protocol)

        transport.open()

        log_entry = scribe.LogEntry(dict(category='SYSD', message=messa))

        result = client.Log(messages=[log_entry])

        transport.close()

        return result



    if len(sys.argv) >= 2:

      host_port = sys.argv[1].split(':')

      host = host_port[0]

      if len(host_port) > 1:

        port = int(host_port[1])

      else:

        port = 1463

    else:

      sys.exit('usage : py.test  host[:port]] ')



    cpustr=read_cpu_usage()

    down=True

    #cpu usage=[(user_2 +sys_2+nice_2) - (user_1 + sys_1+nice_1)]/(total_2 - total_1)*100 

    usni1=long(cpustr[1])+long(cpustr[2])+long(cpustr[3])+long(cpustr[5])+long(cpustr[6])+long(cpustr[7])+long(cpustr[4])

    usn1=long(cpustr[1])+long(cpustr[2])+long(cpustr[3])

    #usni1=long(cpustr[1])+long(cpustr[2])+long(cpustr[3])+long(cpustr[4])

    while(down):

           time.sleep(2)

           cpustr=read_cpu_usage()

           usni2=long(cpustr[1])+long(cpustr[2])+float(cpustr[3])+long(cpustr[5])+long(cpustr[6])+long(cpustr[7])+long(cpustr[4])

           usn2=long(cpustr[1])+long(cpustr[2])+long(cpustr[3])

           #usni2=long(cpustr[1])+long(cpustr[2])+float(cpustr[3])+long(cpustr[4])

           print usn2

           print usni2

           cpuper=(usn2-usn1)/(usni2-usni1)

           s="CPUTotal used percent =%.4f \r\n" % cpuper

           print s

           sendlog(host,port,s)

           usn1=usn2

           usni1=usni2


  • python 记录linux下的内存和cpu使用率

    2010-01-25 21:47:23

    #!/usr/bin/python -u
    #-*- coding: UTF-8 -*-
    # vi:ts=4:et
    ##==========================================================================
    ##
    ##  Copyright (c) onboss Inc. All Rights Reserved.
    ##
    ##--------------------------------------------------------------------------
    ##
    ##  File:        $Workfile: osinfo.py$
    ##               $Revision: 1$
    ##
    ##  Last Update: $2009-3-25 19:57$
    ##
    ##--------------------------------------------------------------------------
    ##

    import os
    import re
    import sys
    import time
    import thread


    # globa re
    re_meminfo_parser = re.compile(r'^(?P<key>\S*):\s*(?P<value>\d*)\s*kB')

    #
    class OSstatus:
        """
        result = report client status.
        """
        def __init__(self, sleep=2):
            """Constructor
            """
            self.sleep=sleep

        def _get_mem_usage(self):
            """get mem used by percent

            self.result = falot
            """
            result={}
            try:
                fd=open('/proc/meminfo', 'r')
                lines=fd.readlines()
            finally:
                if fd:
                    fd.close()
            for line in lines:
                match=re_meminfo_parser.match(line)
                if not match:
                    continue # skip lines that don't parse
                key, value=match.groups(['key', 'value'])
                result[key]=int(value)
            #print "mem :", 100*(result["MemTotal"]-result["MemFree"])/result["MemTotal"]
            return 100.0*(result["MemTotal"]-result["MemFree"])/result["MemTotal"]

        def get_mem_usage(self):
            """safe to call _get_memused()
            self.result = falot
            """
            try:
                return self._get_mem_usage()
            except Exception, e:
                print "_get_mem_usage(self) Exception, %s"%e
                return 0

        def get_5m_load(self):
            """get 5 mines avg load

            self.result = float
            """
            try:
                return (os.getloadavg())[2]
            except Exception, e:
                print "_get_5m_load(self) Exception, %s"%e
                return 0
        
        def _read_cpu_usage(self):
            """Read the current system cpu usage from /proc/stat."""
            try:
                fd = open("/proc/stat", 'r')
                lines = fd.readlines()
            finally:
                if fd:
                    fd.close()
            for line in lines:
                l = line.split()
                if len(l) < 5:
                    continue
                if l[0].startswith('cpu'):
                    return l
            return {}

        def get_cpu_usage(self):
            """get cpu avg used by percent
            """
            cpustr=self._read_cpu_usage()
            if not cpustr:
                return 0
            #cpu usage=[(user_2 +sys_2+nice_2) - (user_1 + sys_1+nice_1)]/(total_2 - total_1)*100
            usni1=long(cpustr[1])+long(cpustr[2])+long(cpustr[3])+long(cpustr[5])+long(cpustr[6])+long(cpustr[7])+long(cpustr[4])
            usn1=long(cpustr[1])+long(cpustr[2])+long(cpustr[3])
            #usni1=long(cpustr[1])+long(cpustr[2])+long(cpustr[3])+long(cpustr[4])
            time.sleep(self.sleep)
            cpustr=self._read_cpu_usage()
            if not cpustr:
                return 0
            usni2=long(cpustr[1])+long(cpustr[2])+float(cpustr[3])+long(cpustr[5])+long(cpustr[6])+long(cpustr[7])+long(cpustr[4])
            usn2=long(cpustr[1])+long(cpustr[2])+long(cpustr[3])
            cpuper=(usn2-usn1)/(usni2-usni1)
            return 100*cpuper

        def get_os_info(self):
            """overide all functions.
            """
            return {"cpu":  "%s"%round(float(self.get_cpu_usage()), 2),\
                    "mem":  "%s"%round(float(self.get_mem_usage()), 2),\
                    "load": "%s"%round(float(self.get_5m_load()), 2),\
                    }


    ###############################################
    RUNNING = True


    def startnow():
        i = 1
        sinfo = OSstatus()
        f = file('poem.txt', 'w')
        f.write('序号,时间,内存使用率,CPU使用率\r\n')
        global RUNNING
        while RUNNING:
                strtime = time.ctime(time.time())
                strmem = str(osinfo.get_mem_usage())
                strcpu = str(osinfo.get_cpu_usage())
                strtime = str(i)+','+strtime+','+strmem+','+strcpu+'\r\n'
                f.write(strtime)
                i=i+1
                time.sleep(1)
        else:
            print "done"
            f.close()


    def exitnow():
        if (raw_input('Enter e to exit: ') == 'e'):
           global RUNNING
           RUNNING = False
        else:
           print 'Enter e to exit'


    thread.start_new_thread(startnow,())
    exitnow()
     
  • 在 soapUI 中建立测试用例

    2010-01-21 19:49:56

    在 soapUI 中建立测试用例

    基本概念

    在创建测试用例之前,我们先来看一看在 soapUI 中的基本概念,soapUI 把 REST 服务、资源及其操作组织为一个层次结构。如 图 1所示,主要包括如下层次:

    • 项目定义:位于最上层 (BookStoreTest),项目可以包含多个服务的定义。
    • REST 服务定义:服务其实是对多个 REST 资源的一个分组,在我们的例子中只有一个服务 BookStoreServie
    • REST 资源定义:具体描述该资源的名称,URI, 参数等属性
    • REST 方法定义:针对每个资源的方法 (GET,POST,PUT,DELETE 等 ),图 1 中的方法名就是 GetBookList
    • REST 操作请求定义:基于每个方法,可以有一个或多个请求操作,如 GetBookListRequest,这些请求操作才是真正被 soapUI 所调用执行的。每个请求可以设置非常丰富的信息,例如 Accept 类型,请求的 Header 信息,运行了该请求以后,就能以各种方式查看运行结果。但是这里还不能加入断言来验证结果 - 必须在建立测试用例以后才能使用。

    注: 读者可以在 下载区的 bookstore-soapui-project.zip 找到完整的例子,下文中主要以该例子为基础进行讲解。读者解压 zip 文件以后,能得到一个 xml 文件,可以通过 soapUI 的 File->import project 把项目导入到自己的工作区中。


    图 1. soapUI 中的层次结构
    soapUI 中的层次结构

    对于测试用例来讲,同样是一个层次结构:

    • TestSuite:类似于 Junit 中的测试套件,其中可以加入多个 TestCase
    • TestCase:可以包含多个 TestStep
    • TestStep:一个 TestCase 可以包含多个 TestStep,TestStep 有多种类型,它可以是上面提到一个 REST 操作请求,也可以是一个 Groovy 的脚本,还可以试一个设置属性的操作。 TestStep 甚至支持分支跳转操作:根据特定的条件,从一个 step 可以跳转到其他 step, 而不必顺序执行。

    soapUI 实际上是一个平台,它支持强大的编程能力,开发或者测试人员可以利用 groovy 脚本来访问 soapUI 中的对象,在运行时修改 REST request/response, 这就提供了极大的灵活性。


    图 2. TestCase 定义
    TestSuite

    创建测试用例

    有了上面的基本概念以后,在 soapUI 中创建测试用例就比较简单了, 用户几乎可以根据自己的直觉来一步一步的完成一个测试。图 3展示的就是一个建立书籍列表 REST 服务的步骤:

    1. 新建一个名为 BookStoreTest 的项目
    2. 在项目上点击右键,选择"New Rest Service",在对话框中输入 Service Name(BookStoreService) 和 Endpoint(http://localhost:9080)
    3. 在"BookstoreService"上点击右键,选择“New Resource”, 在对话框中输入 Resource Name(BookList) 和 Resource Path (/books),点击 OK
    4. 在弹出的对话框中输入 Method Name: GetBookList,HTTP Method 选择默认的 GET, 点击 OK


    图 3. 创建一个 REST 服务
    REST servive

    有了 REST 服务,就可以建立 TestCase,主要有两种方式:

    • 自动生成,步骤如下:
      (1). 右键点击一个 REST 服务,例如本例中的"BookStoreService", 选择"Generate TestSuite"
      (2). 在弹出的对话框中,保持默认设置, 选择"OK"
      (3). 输入名称 , 例如"BookStoreService TestSuite", 选择"OK"即可。
    • 手工创建,步骤如下:
      (1). 在项目"BooksStoreTest"上点击右键,选择“New TestSuite”, 在对话框中输入"BookStoreService TestSuite"
      (2). 在 BookStoreService TestSuite 上点击右键, 选择"New TestCase", 在对话框中输入"BookList TestCase"
      (3). 然后在左边的导航栏中展开 BookList TestCase, 在“Test Steps”上点右键,选择 Add Step->Rest Test Request
      (4). 在弹出的对话框中选择 GetBookListRequest_XML

    一个完成的 TestCase 如 图 4所示,用户可以在其中加入 Assertion 对运行结果进行验证,这也是自动化测试所必须的。


    图 4. REST TestCase
    REST Test Case

    使用变量

    soapUI 支持使用自定义变量(Property)在 Project 中存储和共享数据。Property 是一个命名的字符串可以被 Groovy Script,Property Transfer 或者 Property-Expansion 引用, 目前所有的变量均被处理为字符串。soapUI 允许在项目的各个层次中定义变量,常用的层次包括:Project,TestSuite,TestCase,Global 等。

    1. 使用 Property 编辑器定义变量。
    用户可以使用 soapUI 自带的 Property Editor 定义各个层次的变量。以 Project 变量为例,点击 BookStoreTest,在 Properties 面板中添加自定义变量,如下图所示:


    图 5. 使用 Property 编辑器定义项目变量
    Project Property

    2. 使用命令行指定变量。
    修改 soapUI.bat 文件中的 Java 参数如下:


    清单 x. 使用命令行指定变量
    				
    set JAVA_OPTS=%JAVA_OPTS% -Xms128m -Xmx256m -Dsoapui.properties=properties.txt

    其中,properties.txt 为指定的全局变量文件名字,可以通过添加如下代码到全局变量文件来设置 project/testsuite/testcase 等层次的变量:


    清单 x. 使用命令行指定变量
    				
    soapui.properties.<shortened-name-of-object>=pathtopropertiesfile

    <shortened-name-of-object> 为相应对象的名字。

    3. 使用变量
    soapUI 使用如下语法引用项目中定义的变量:


    清单 x. 使用命令行指定变量
    				
    ${[scope]propertyName[#xpath-expression]}

    其中,scope 可以为 #Project#,#TestSuite#,#TestCase# ,#Global#,#System#,#MockService#,#Env#,[TestStep name]#。

    验证结果

    测试用例建好之后,需要向测试用例中添加 Assertions 以便验证结果的正确性。soapUI 支持 Response SLA, Script. Assertion, Contains, XQuery Match, Schema Compliance, XPath Match 以及 Not Contains 等多种断言来对 response 进行判断来保证对 Web 服务高质量的测试。本文以 XPath Match 和 Script. Assertion 为例来对在线书店服务返回的 XML 和 JSON 格式的 response 进行判断。

    1. 使用 XPath Match 测试请求 GetBookListRequest_XML 返回的结果中,id 为 1234 的 book 的 price 为 29.0, response 参见 代码清单 2

    点击 TestCase 的添加 Assertions 按钮,如 图 4所示。 在弹出的 Select Assertion 窗口中选择 XPath Match 断言,点击 OK。配置 XPath 如下图所示:


    图 6. 使用 XPATH 测试 XML 格式的书籍列表
    XPath Match

    在 XPath Expression 面板中书写用于匹配 Response 的 XPath 表达式,Expected Result 面板中填写期望的值。soapUI 将使用 XPath Expression 面板中填写的 XPath 表达式与 Service 调用结果匹配, 将匹配结果与期望值比较,如果相同则测试通过,否则失败。

    2. 使用 Script. Assertion 测试请求 GetBookListRequest_JSON 返回的结果,response 参见 代码清单 1

    Assertion 添加过程与 XPath Match 类似,在 Select Assertion 窗口中选择 Script. Assertion,并在之后弹出的 Script. Assertion 窗口中书写如下代码:


    清单 5. 使用 Script. Assertion 测试 JSON 格式的书籍列表
    				
    //assert the response header
    assert messageExchange.responseHeaders["Content-Type"]=="application/json;charset=UTF-8";
    assert messageExchange.responseHeaders["Cache-Control"] == "no-cache";

    //assert the repsonse body
    def booksRoot = net.sf.json.JSONSerializer.toJSON(messageExchange.responseContent);
    def books = booksRoot.get("books");

    //assert book detail
    assert books[0].get("book").get("id") == "1234";
    assert books[0].get("book").get("name") == "book1";
    assert books[0].get("book").get("price") == 29;

    3. 使用 Property 测试请求 GetBookRequest_JSON 返回的结果, response 参见 代码清单 3

    在 Script. Assertion 窗口中写入如下代码:


    清单 6. 使用 Property 测试 JSON 格式的书籍详情
    				
    //get property
    def expectedID = context.expand('${#Project#book.id}');
    def expectedName = context.expand('${#Project#book.name}');

    //assert the response header
    assert messageExchange.responseHeaders["Cache-Control"] == "no-cache";

    //assert the response body
    def bookRoot = net.sf.json.JSONSerializer.toJSON(messageExchange.responseContent);
    assert bookRoot.get("id") == expectedID;
    assert bookRoot.get("name") == expectedName;
    assert bookRoot.get("price") == 29.0;

    上述使用 Groovy Script. 对 Service 调用结果进行验证,可用的 soapUI 对象包括 :messageExchange, context 以及 log。

    • messageExchange: 当前交互 request/response 的 MessageExchange,可以用来直接访问 message content, HTTP Headers,Attachment 等对象。
    • context: 运行当前 TestCase 的 TestRunContext 对象,具体使用方式请参见 soapUI API 文档。
    • log: 一个标准的 Log4j Logger 对象,可以用来输出日志。

    依照上述步骤定义好 TestCase 并添加适当的断言之后,就可以对在线书店 REST 服务进行测试。双击 BookStoreSerive_TestSuite, 点击 Run 按钮来运行所有的 TestCase,结果如下图所示:


    图 7. 运行测试用例
    Run Test




    回页首


    性能测试

    性能测试在 soapUI 中称为 Load Test, 针对一个 soapUI 的 TestCase, 可以建立一个或多个 LoadTest, 这些 LoadTest 会自动的 把 TestCase 中的所有步骤都添加到其中, 在运行的时候,soapUI 会自动的使用多个线程来运行这些 TestStep,同时也会监控 它们的运行时间, 例如最短时间,最长时间,平均时间等等。这样用户能够很直观的看到 REST 服务的响应时间,从而对性能进行调优。

    建立 LoadTest 非常简单,只需要在“Load Tests”上点击右键, 选择"New LoadTest", 然后输入名称即可,下图是一个针对 GetBookList 的 性能测试, 可以看到有两个 TestStep : "GetBookList_xml" 和"GetBookList_json" , 100 个线程并发执行, 时间限制是 60 秒。 最后的结果是,最短时间 4 毫秒,最长时间 1204 毫秒,平均时间 20.54 毫秒。


    图 8. 性能测试
    Load test

    性能测试还支持断言,用户可以对一个 TestStep 或 TestCase 设置运行时间要求,例如平均时间大于 2 秒就认为失败,点击 图 8 中中的 “LoadTest Assertions”就可以设置。 当然根据需要,用户也可以编写脚本来做一些准备工作,或者清除工作。 参见 图 8 中的"Setup Script"和“TearDown Script”。

    与 BuildForge 集成

    测试可以有效的保证代码的质量,但是仅仅手工的、在本机上运行的 REST 服务测试时远远不够的。实际上把测试作为软件构建的一部分,加入到持续集成中去是一个常见的敏捷开发实践,通过频繁的,自动化的测试, 可以更有效的发现缺陷,保证代码质量。

    IBM Rational BuildForge 是一个管理软件构建和发布的平台 , 它提供了一个框架来自动化整个构建流程,不仅仅自动化单独的任务,可以集成多种用户现有的脚本和工具, 最大限度的保护用户投资。作为一个框架,BuildForge 几乎可以调用操作系统上的任何脚本。本文重点不在介绍 BuildForge, 假定读者对 BuildForge 已经比较熟悉, 不熟悉的读者可以查看参考资料中的相关文章。

    对于 soapUI 来说,最简单的一种集成方式就是提供命令行脚本让 BuildForge 调用。在上文中 我们已经展示了通过 soapUI 的 GUI 运行 TestCase 的功能,那么 soapUI 可不可以通过命令行完成类似的功能呢? 答案是肯定的。

    soapUI 提供了一个命令行工具 testrunner.bat 来运行一个项目中的 TestSuite, 可以在 <soap_ui_home>/bin 下找到它, 它的使用非常简单,只需要设置下面的几个常用参数即可:

    • -s 指定要运行的 TestSuite
    • -f 指定运行结果的输出目录
    • -j 生成 junit 风格的 report
    • -r 运行完成以后打印一个简单的 summary

    下面这行命令就是运行 bookstore.xml 这个 soapUI 项目中的 BookstoreTestSuite, 把结果输出到 c:\temp\reports 中。
    testrunner.bat -sBookstoreTestSuite -r -j C:\developerWorks\soapui\bookstore.xml -f c:\temp\reports

    有了 testrunner,把 soapUI 的测试集成到 BuildForge 中就很简单了,只需要在 BuildForge 的项目中添加一个步骤,参见下图:


    图 9. BuildForge 中使用 testrunner
    BuildForge and testrunner




    回页首


    总结

    从 上文可以看出,soapUI 是一款强大的 Web 服务测试工具 , 它提供了便利的 GUI 界面帮助用户对 REST 服务进行快速的测试,使用简单,学习成本很低。 它也提供了可编程能力例如变量,Groovy 脚本等, 以便让用户应对一些复杂的情况。 总而言之,有效的实际项目中使用 soapUI,可以极大的减轻工作量,提供工作效率。当然作为开源的工具, soapUI 仍然在不断的发展完善过程中,本文所能介绍的也只是它的部分功能, 感兴趣的读者可以到 soapUI 网站上获取更多资料。

441/3123>
Open Toolbar