• Groovy字符串操作 - [自写]

    2008-02-14

    Groovy提供了GString,可以很方便地帮助我们完成字符串的拼装,例如生成大段的HTML代码。

    例如在Groovy中可以这样写代码:

    def s = "<div>${example.name}</div>"

    java中一般是这样的:

    String s = "<div>"+example.name+"</div>";

    或者使用StringBuffer(StringBuilder)拼装字符串。

    习惯了Java的写法,同时又会利用Groovy的方便的语法来构造字符串,不小心就会得到很不好的结果,例如如下的代码就是我以前一直用的办法,感觉这种写法太方便了:

    def sp = ""

    sp +="${content}add At ${i}"
     

    最近发现这种做法在文本较大的情况下会带来很坏的结果:

    下面是完成相同工作的两种写法:

    def gs = "";
    def sp = "";
    def content = "";
    for(i in 1..1024){
        content += "a";
    }

    //使用GString构造

    def f = System.currentTimeMillis();
    for(i in 1..200)
    {
        gs="${gs}${content}add At ${i}"
    }

    gs.toString()

    //使用GString和"+"构造

    def s = System.currentTimeMillis();
    for(i in 1..200)
    {
        sp +="${content}add At ${i}"
    }
    def e = System.currentTimeMillis();

    println("${s-f}ms [${gs.length()}] ${e-s}ms [${sp.length()}]")

    如果运行这个脚本,运行多次的结果如下:

    2ms [206692] 290ms [206692]
    3ms [206692] 298ms [206692]
    4ms [206692] 282ms [206692]
    3ms [206692] 305ms [206692]
    3ms [206692] 332ms [206692]

    可以看得出,第一种写法的性能要好很多,为什么呢?

    答案在GString的实现中,有兴趣的同学可以看一下GString的实现,它采用了一种延迟"拼装"的策略,它是在需要的时候才拼字符串得出结果;而第二种写法是在每一次循环的时候都要进行字符串的"+"操作,同时需要进行新的内存的分配,以及字符数组的拷贝,结果就是慢了。

    如果即要写程序省事,代码明了,还要保证性能,采用第一种写法是比较好的选择,呵呵。

  • 今天帮朋友写了一个小东西,主要是使用一个表达式,这个表达式支持逻辑运算,例如abc & def,这表示在一个字符串中如果同时有abc和def这两个子串,则返回true,否则返回false.

    最初想使用Antrl实现,不过Antrl没用过,得学习段时间才行,时间来不及了。

    我简单地用Groovy实现一个"DSL",可以利用Groovy语言本身的逻辑运算。

    先定义一下需要的表达式元素:
    & 与 
    | 或
    - 不存在 +存在
    () 逻辑分组
    '' 字符串用 ''引起来,如果字符串中有',需要转义一下,用\\'

    &,|,-,+可以使用操作符重载实现。

    一个复杂点儿的例子:

    ('abc' & 'def') | ('def' & ('fgh' | '123'))
    这个表达式表示:如果包括abc,那么就必须包括def;而如果有def,就必须有fgh或者123.

    实现如下:

    1.重载了 &,|,-和+ 这四个操作符

    2.使用categories(StringCategory)来限定上面那个操作符的作用范围

    3.PBinding,用于动态的查找被检测的字符串中是否包含表达式的子串

    class PBinding extends Binding{
            private String btext;
            public PBinding(String text){
                    btext = text;
            }
            public Object   getProperty(String name){
                    return btext.indexOf(name)>=0;
            }
            public Object   getVariable(String name){
                    return btext.indexOf(name)>=0;
            }
    }
    def isMatch(pattern,text){
    GroovyShell sc = new GroovyShell(new PBinding(text));
    def script="""
    class StringCategory {
        def static text = "${text}";
        def static check(def s){
            if(s instanceof String){
                    return (text.indexOf(s) >=0);  
            }else{
                    return s;
            }
        }  
        def static and(def s,def b){
            def left = check(s);
            def right = check(b);
            return left && right;
        }  
        def static or(def s,def b){
            def left = check(s);
            def right = check(b);
            return left ||  right;
        }  
        def static negative(def s){
     return !check(s)
        }

    def static positive(def s){
    return check(s)
    }

    }
    use(StringCategory){
    ${pattern} //在此执行表达式
    }
    """
    return sc.evaluate(script);
    }

    //test
    println "1. isMatch ${isMatch(+'abc','343/abc')}";
    println """2. isMatch ${isMatch("abc|\'def\'",'343/abc')}""";
    println """3. isMatch ${isMatch("'abc'&'def'",'defabc')}""";
    println """4. isMatch ${isMatch("('abc' & 'def') | ('def' & ('fgh' | '123'))
    ",'abcdef')}""";

    //called from java 

    //return isMatch(pattern,text)

    上面的代码可以嵌入到Java程序中去,在java代码里调用这个groovy脚本,并传入pattern和text两个参数就可以了。

    可以看得出,Groovy在写DSL的时候,还是很方便的。

    2007就要过去了,这也是我今年的最后一个blog了,祝大家新年快乐!:)

  • 对Groovy为什么比Java慢的一个比较好的分析Why is Groovy so slow?

    作者提到了MOP(猫扑?呵呵),以前一直觉得这个东西会降低一些性能,看了这个blog之后,确信不疑了。

     

  • Groovy 1.5发布 - [自写]

    2007-12-08

    早上起来,先上groovy的主页看了一下,因为我感觉这几天Groovy1.1的最终版将会发布了。

    结果看一下新闻的标题“Groovy 1.5 released” ,直接从1.0到1.5了,有点当年jdk从1.4到5.0的风格。

     公司的应用还在用Groovy1.0,下周测试一下准备升级到5.0 :)

  • 晚上用Gant写了一个构建脚本,在写的过程中用有一个功能需要在脚本里新启动一个进程来调用同一个脚本。

    这需要知道这个Gant脚本的名称,试了几回,发现每个Gant脚本编译后的类名规则是:name_gant,如build_03.gant,编译后类名就是build_03_gant,这就好办了:

    def shellname = this.getClass().getName().replace('_gant','.gant'),这就得到了原Gant脚本的名字了。

    而Groovy脚本,则是

    def shellname = this.getClass().getName()+".groovy"就可以了。

    当我写完这个脚本,启动vpnc,连接到公司的cvs服务器提交代码时,发现vpnc又把我的dns设置给改了,结果就导致除了公司内部的域名可以访问,其它的都不能访问了。

    这个问题很久了,一直没解决,每回都需要手工把配置恢复一下,今天我忍无可忍了,找了一下原因:

    我的系统是Kubunut7.10,其它的Linux发行版本可能会不一样。 

    在/etc/vpnc/vpnc-script 这个脚本中,每次启动vpnc时,都会先把原来的/etc/resolv.conf备份一个文件,之后再用自己的dns设置覆盖到/etc/resolv.conf

     解决办法:

    把/etc/vpnc/vpnc-script 这个文件中的

    echo "$NEW_RESOLVCONF" > /etc/resolv.conf 

    改为: 

    echo "$NEW_RESOLVCONF" >> /etc/resolv.conf

    就可以了。

    当vpnc停止时,又会把备份的resolv.conf恢复到/etc/resolv.conf中,可以不必理会。

     

  • Groovy,Jython,JRuby是JAVA平台用得比较多的动态语言,它们的代码简练,生产率高,经常在网上看到对它们的赞美。

    生产率高的代码,性能可不一定同样高。

    例如下面的Groovy代码,很简单的操作,将i从0增加到99,测试的时候你就会发现,它的执行速度比你想的要慢很多。因为每一次的i++;每是一个new Integer的操作,也就是说每一个i++至少要新建一个Integer对象,还不包括Groovy在执行这段代码时编译生成的其它“额外代码”。

    def test(count){
    def i =0;
    while(i<count){
            i++;
    }

    test(100); 

    同样的,JRuby也存在着潜在的问题。

    我想告诉大家的是,动态语言看起来很美,但是美丽的外表通常都有阴暗的角落,呵呵。

    如果在程序里有大量的数学计算或者数据处理,并且有一定的性能指标,我建议如果使用动态语言实现这些功能后,一定要做一下性能的测试,没有达标的,还是用Java实现吧, 再用动态语言作为“粘合剂”来调用对应的Java实现。

     

     

     

  • 今天想下载新歌,原来的程序不能用了,检查发现sogou的源代码变了,正则匹配不管用了,faint.

    观察了一下新的源代码格式,发现现在的信息都放在musicInfoList这个变量里了,正好顺便试用jdk6的ScriptEngineManager,呵呵 .

    基本想法是使用ScriptEngine执行一下musicInfoList变量定义,就可以通过得到的对象找到专辑的各种信息,余下的操作以上一版一样,只是代码更简单了.

    import java.io.CharArrayWriter;
    import java.util.regex.Matcher
    import java.util.regex.Pattern
    import javax.script.*;

    def download(address,outer){
      outer << address.toURL().openStream()
      outer.close()
    }

    class FileBinaryCategory
    {
      def static leftShift(File a_file, URL a_url)
      {
        def input
        def output

        try
        {
          input = new BufferedInputStream(a_url.openStream())
          output = new BufferedOutputStream(new FileOutputStream(a_file))
          output << input
        }
        finally
        {
           input?.close()
           output?.close()
        }
      }
    }
    if(args.length<2){
     println "Usage:groovy ${this.class.name}.groovy <store_dir> <url>"
     return;
    }
    def mp3dir = args[0]
    def mp3source = args[1]                
    def mp3source_outer = new CharArrayWriter();
    download(mp3source,mp3source_outer);
    def mp3html=mp3source_outer.toString();
    def mp3 = mp3html =~ /musicInfoList\s*=\s*\[(.+)\];/
    if(mp3.find()){
     def mp3MapStr = "[${mp3.group(1)}]";
     def manager = new ScriptEngineManager();
        def engine= manager.getEngineByName("js");
     def mp3Array = engine.eval(mp3MapStr);//得到一个NativeArray对象
     def itemLen = mp3Array.getLength();
     def i=0;
     while(i<itemLen){
      def mp3Item = mp3Array.get(i,mp3Array);
      def song_name = mp3Item.get("title",mp3Item);
      def song_url = mp3Item.get("url",mp3Item);
      def song_type = mp3Item.get("postfix",mp3Item);
         print "found ${song_name} from ${song_url}...."
         try{
          print "Begin downloading ..."
          def file = new File(mp3dir+File.separator+song_name+"."+song_type)
          def pdir = file.getParentFile()
          if(pdir == null){
           println "Error file path:${file},no parent dir."
           return
          }
          if(!pdir.exists()){
           pdir.mkdirs()
          }
          if(!file.exists()){
              use (FileBinaryCategory)
              {
                file << song_url.toURL()
              }  
          }
      println " end."
         }catch(Throwable te){
          println "Download mp3 ${song_name} error,${te}"; 
         }
      i++;
     }

     

    刚才试着下载了 不能说的秘密 ,使用这个链接 下载正常,sogou音乐盒的专辑功能真是不错,省得一首一首的找了,特别是一些老歌,热心的网友已经找得很全了,我就只能提供个小工具方便大家下载,呵呵.

  • 将Groovy作为一门业务用户语言?

    从InfoQ上看到的一个文章,讲得是在OpenOffice将使用Groovy作为他们面向用户的宏/脚本语言,令我兴奋。

    Groovy发展到今天,已经很不错了,支持的特性越来越多。

    总结一下我所知道的Groovy的用途:

    1.脚本语言

    我经常用Groovy写一些shell,完成一些小的需求。我以前和一个国外的Groovy用户在IRC上聊天,他向我介绍了他们主要是使用Groovy进行软件的自动化构建和测试工作的脚本编写.

    2."嵌入式"

    这个嵌入式是指将Groovy嵌入到Java平台(系统)中,利用Groovy强大的动态特征, 可以为系统提供灵活的扩展和插件机制,能够很好地增加系统的灵活性和功能。例如我们公司的系统中使用Groovy已经快一年多了,效果没得说,呵呵。OpenOffice使用Groovy作为宏的脚本语言,也是“嵌入式”的一种~_~。

     3.测试用例

    Groovy简洁的语法可以让测试用例的编写更加简单,目前可以用Groovy写TestNG和JUnit的测试用例.

     4.领域语言(DSL)

     Groovy的动态特性使得它很适合实现一个DSL,我一直想用Groovy把公司的一些业务封装成一个DSL,让对java不熟悉的同事能有效,安全地实现一些需求,不过一直没有动手,原因是想法还不成熟:(

    5.GSP

    可以用Groovy实现jsp相同的功能,不过这个功能我还没有试,等Groovy再成熟些。

    .........

    以上是我接触过的一些用途,以后随时补。

     

     

  • Groovy 1.1-beta-2  前天发布了,这回我不敢再冒然升级了(原因::Groovy1.1-beta-1的Bug:Groovy-1890和Groovy-1882)。

    令我感动的是1882这个BUG终于解决了,不过更让我激动的是:从Jira的日志上看,好像是我提交的Patch被采用了,我也很怀疑是不是看错了。下载了beta-2的源代码,仔细的检查了GroovyScriptEngine的代码,最终确认我提交的patch 被采用了,让我好不激动,我也终于为open source作了点贡献了

    按下激动的心,我得上火车站了,一老朋友要从北京转车,得给他送票去,否则他只能在北京过夜了,还得杀我几顿,先把他送走是当务之急。

     

  • 天气如此的热 - [自写]

    2007-06-10

    这几天北京的天气如此的热, 

    已经有三天没有启动家里的台式机了,天气太热了,只要一启动它,就感觉更热了,为了减少老婆的怨言,暂时先不用它了;

    昨天在学校上课,上午刚到教室听了一会儿,就睡着了,下午死撑着,最后还是睡了一会儿;

    今天下午的课是沈孝钧老师的算法,虽然教室里的空调没有开,但我竟然没打盹,沈老师的课讲得太帅了,能把算法讲到这种地步,我只能好好学习报答他老人家了~_~。

     写到这儿,还要说点题外话,晚上帮一朋友处理了一些他的email数据,要求是将mail1.txt中的数据从mail5.txt中删除,并将结果输出的一个新文件中。

    这些数据原本是在excel中的,先导出成文本格式,一行一个email,在使用Groovy完成这个任务时,尝试了一些以前没有用过的技巧:

    1.使用1.1中的Category, 实现的功能:方便地在代码中使用重载的操作符。

    2.重载 "|" ,实现的功能:模拟Unix/Linux中文本处理的方式 ,用"|"作为管道流。

    下面是全部的代码,一共只有21行!~_~ 

     class ShellPipe{
        def static or(File f,right){
            f.eachLine{line->
                right(line);
            }
        }
    }
    File file = new File("mail5.txt");
    File file2 = new File("mail1.txt");
    File file3 = new File("mail3.txt");
    def mainMailMap = new java.util.LinkedHashMap();
    use(ShellPipe){//在此使用use(ShellPipe)来完成Category的功能,并调用重载的"|" 完成文本的处理
        file  | {def mail = it.trim().toLowerCase();if(mail){mainMailMap.put(mail,mail)}}
        file2 | {def mail = it.trim().toLowerCase();if(mail){mainMailMap.remove(mail)}}
    }
    def newMails = mainMailMap.values();
    def fouter = new BufferedOutputStream(new FileOutputStream(file3))
    for (mail in newMails){
        fouter << mail+"\r\n";
    }
    fouter.close()

    以上可以看得到,Groovy很灵活,也很适合用它写一些常用的Shell脚本来帮助我们解决日常工作,生活中的一些问题。例如, 用groovy shell从sogou音乐盒下载专辑 里面所述的脚本也只有66行代码,省了我下载mp3的很多时间。~_~

    关于Groovy的使用技巧将来肯定会越来越多,我也会随时把自己的一些小东西发上来和大家分享。

    希望Groovy的新版本能够再稳定些,有了好的基础才能成长的更快,更高,更强嘛。