Groovy使用字符串

来源:互联网 发布:淘宝卖家店铺id怎么看 编辑:程序博客网 时间:2024/06/05 05:50

字面常量与表达式

Groovy可以使用单引号创建字符串字面常量,比如’hello’。而java中,’a’是一个char,”a”才是一个String对象,Groovy中没有这样的分别。在Groovy中,二者都是String类的实例。如果想显式地创建一个字符,只需要输入’a’ as char。当然,如果有任何方法调用需要的话,Groovy可能隐式地创建Character对象。
对于字符串字面常量中可以放什么,Groovy也很灵活。只要想要,双引号都可以放到字符串中:

println 'He said,"That is Groovy"'

输出:He said,”That is Groovy”

现在来看看使用单引号创建的对象的类型:

str = 'A string'println str.getClass().name

输出:java.lang.String

Groovy会把使用单引号创建的String看做一个纯粹的字面常量。因此,如果在里面放了任何表达式,Groovy并不会计算他们,相反,它就是按照所提供的字面内容来使用他们。要对String中的表达式进行求值运算,则必须使用双引号。

value = 25println 'The value is ${value}'

输出结果:The value is ${value}
可以看到Groovy没有对value进行求值计算

Java的String是不可变的,Groovy也信守这种不可变性。一旦创建了一个String实例,就不能通过调用更改器等方法来修改其内容。可以使用[]操作符读取一个字符,不过不能修改,从下面代码中可以看到:

str = 'hello'println str[2]try{    str[2] = '!'}catch (Exception ex){    println ex}

尝试修改String导致一个错误:groovy.lang.MissingMethodException: No signature of method: java.lang.String.putAt() is applicable for argument types: (java.lang.Integer, java.lang.String) values: [2, !] Possible

可以使用双引号(”“)或者正斜杠(//)创建一个表达式。不过,双引号经常用于定义字符串表达式,而正斜杠则用于正则表达式。下面是一个创建表达式的例子:

value = 12println "He paid \$${value} for that"

Groovy会计算该表达式,我们在输出中会看到:

He paid $12 for that

变量value在字符串内被计算求值了。这里使用了转义字符(\)来打印$符号,因为Groovy会将$符号用于嵌入表达式。如果定义字符串时使用的是正斜杠,而非双引号,则不必转义$.如果表达式是一个像value这样的简单变量名,或者是一个简单的属性存取器(accessor),则包围表达式的{}是可选的。因此,我们可以把语句println "He paid \$${value} for that."写作println "He paid \\$value for that."或者println (/He paid $$value for that/)尝试去掉表达式中的{}.看看Groovy是否会报错,需要的时候我们总是可以加上的。

Groovy支持惰性求值,即把一个表达式保存在一个字符串中,稍后在打印。来看一个例子:

what = new StringBuilder('fence')text = "The cow jumped over the $what"println textwhat.replace(0,5,"moon")println text

输出结果:
The cow jumped over the fence
The cow jumped over the moon

当打印text中的字符串表达式时,使用的是what所指对象的当前值。因此,第一次打印text时,得到的是“The cow jumped over the fence”。在修改了StringBuilder中的值之后,在打印该字符串表达式的时候,我们并没有修改text的内容,但是得到的输出不同。从这种行为可以看出,使用单引号创建的字符串和使用双引号或正斜杠创建的字符串不同。前者是普通的java.lang.String,而后者有些特殊,我们称其为Gtring,就是Groovy字符串的简称。下面看一下使用不同语法创建的对象的类型:

def printClassInfo(obj){    println "class:${obj.getClass().name}"    println "superclass:${obj.getClass().superclass.name}"}val = 125printClassInfo("The Stock close at ${val}")printClassInfo(/The Stocl close at ${val}/)printClassInfo("This is a simple String")

输出的结果:
class:org.codehaus.groovy.runtime.GStringImpl
superclass:groovy.lang.GString
class:org.codehaus.groovy.runtime.GStringImpl
superclass:groovy.lang.GString
class:java.lang.String
superclass:java.lang.Object

Groovy并不会简单地因为使用双引号或正斜杠就创建一个GString实例。它会智能地分析字符串,以确定该字符串是否可以使用一个简单的普通String蒙混过关,在这个例子中,最后一次调用printClassInfo()时,即使我们使用了双引号来创建字符串,但改参数还是一个Stirng实例。

GString的惰性求值问题

what = new StringBuilder('fence')text = "The cow jumped over the $what"println textwhat.replace(0,5,"moon")println text

这段代码的输出看上去想当合理:
The cow jumped over the fence
The cow jumped over the moon

text这个GString实例中包含了变量what。该表达式会在每次被打印时,也就是在其上调用toString()方法时求值。如果修改了what所指向的StringBuilder对象的值,在打印时会有所体现,这看上去很合理,然而,如果修改的是引用what,而不是被引用对象的属性,结果将出乎意料,但如果对象是不可变的,修改引用就是很自然的做法了。下面的例子说明了这个问题:

price = 684.71company = 'Google'qutoe = "Today $company stock closed at $price"println qutoestocks = [Apple : 663.01 ,Microsoft : 30.95]stocks.each{key,value->    company = key    price = value    println qutoe}

输出:
Today Google stock closed at 684.71
Today Google stock closed at 684.71
Today Google stock closed at 684.71

第一次打印输出是我们想要的,然而后面的输出不是我们想要的。
我们先弄清楚它为什么没有按预期方式工作,才能找出解决方案。这里在定义quote这个GString时,使用了company和price两个变量,前者绑定的是值为“Google”的一个String,后者绑定的是一个Integer,其中保存高的惊人的股价,可以将company和price引用(它们指向的均为不可变的对象)修改为任何想指向的其他对象,但是不能修改GString实例所绑定的内容。
“The cow jumping over…”可以工作,是因为修改的是GString所绑定的对象。然而这个例子中却不可行。因为不可变,所以无法修改。那如何解决?——让GString重新计算引用。
在修复该问题之前,先花点时间理解一下GString表达式是如何求值的。当对一个GString实例求值时,如果其中包含一个变量,该变量的值会被简单地打印到一个Writer,通常是一个StringWriter。然而,如果GString中包含的是一个闭包,而非变量,该闭包就会被调用。如果闭包接受一个参数,GString会把Writer对象当做一个参数发送给它。如果闭包不接受任何参数,GString会简单地调用该闭包,并打印我们想返回Writer的结果。如果闭包接受的参数不止一个,调用则会失败,并抛出一个异常,所以别这么做。

price = 684.71companyClosure = {it.write(company)}priceClosure = {it.write("$price")}quote = "Today ${companyClosure} stock closed at ${priceClosure}"stocks = [Apple : 663.01 ,Microsoft : 30.95]stocks.each{key,value->    company = key    price = value    println quote}

输出结果:
Today Apple stock closed at 663.01
Today Microsoft stock closed at 30.95

输出合乎预期。但这段代码看上去还不够出色,即使最终版本不想采用这种方式,但是通过观察这个例子,还是有两方面收获。首先可以看到实际发生了什么:当表达式需要求值/打印时,GString会调用闭包,其次,如果想做这些计算,而不是仅仅先试一下属性的值,也知道了该怎么做。
如前文所述,如果闭包没有任何参数,可以去掉it参数,GString会使用我们返回的内容。我们已经知道如何创建一个没有参数的闭包——使用{->语法来定义。现在重构前面的代码:

price = 684.71companyClosure = {-> company}priceClosure = {-> price}quote = "Today ${companyClosure} stock closed at ${priceClosure}"stocks = [Apple : 663.01 ,Microsoft : 30.95]stocks.each{key,value->    company = key    price = value    println quote}

输出结果:
Today Apple stock closed at 663.01
Today Microsoft stock closed at 30.95

quote = "Today ${-> company} stock closed at ${-> price}"stocks = [Apple : 663.01 ,Microsoft : 30.95]stocks.each{key,value->    company = key    price = value    println quote}

输出结果:
Today Apple stock closed at 663.01
Today Microsoft stock closed at 30.95

这个版本更加简洁。
如果希望改变表达式中使用的引用,而且希望它们的当前值被用于惰性求值中,请必须记住,不要再表达式中直接替换他们,而要使用一个无参闭包。

多行字符串

使'''......'''来定义多行字面量,包含在一对三个单引号内的字符串可以作为一个多行的字符串。

字符串便捷方法

str = "It's a rainy day in Seattle"println strstr -= "rainy"println str

输出的结果为:
It’s a rainy day in Seattle
It’s a day in Seattle

-=操作符对于操纵字符串很有用,它会将左侧的字符串与右侧字符串相匹配的部分去掉。Groovy还有类似的plus()(+),multiply()(*),next()(++),replaceAll()和tokenize()等。

for(str in 'held'..'helm'){    print "${str} "}println ""

输出:held hele helf helg helh heli helj helk hell helm

正则表达式

Groovy添加了一些操作符和符号,为使编程使用RegEx更加容易。

obj = ~"hello"println obj.getClass().name

输出结果:java.util.regex.Pattern

前面的例子说明,将~应用于String,会创建一个Pattern实例。我们可以使用正斜杠,单引号或双引号来创建RegEx。正斜杠有个优势:不必对反斜杠进行转义。因此,/\d*\w*/"\\d*\\w*"等价,但是更加优雅。

为方便匹配正则表达式,Groovy提供了一对操作符:=~==~

pattern = ~"(G|g)roovy"text = 'Groovy is Hip'if(text =~ pattern)    println "match"else    println "no match"if(text ==~ pattern)    println "match"else    println "no match"

输出结果:
match
no match

这个结果就可以看出两个操作符之间的差别。
=~执行RegEx部分匹配,而==~执行RegEx精确匹配。
=~操作符会返回一个Matcher对象,他是一个java.util.regex.Matcher实例。Groovy对Matcher的布尔求值处理不同于Java,只要至少有一个匹配,他就会返回true。如果多个匹配,则matcher会包含一个匹配的数组,这有助于快速获得匹配给定RegEx的文本中的部分内容。

matcher = 'Groovy is groovy' =~ /(G|g)roovy/print "Size of matcher is ${matcher.size()}"println "with elements ${matcher[0] and ${matcher[1]}}."

结果如下:
Caught: groovy.lang.MissingPropertyException: No such property: and for class: groovy.Demo
Size of matcher is 2

可以使用replaceFirst()方法或者replaceAll()方法方便地替换匹配的文本。(前者仅替换第一个匹配,后者会替换所有匹配)

str = 'Groovy is groovy , really groovy'println strresult = (str =~ /groovy/).replaceAll('hip')println result

输出结果:
Groovy is groovy , really groovy
Groovy is hip , really hip

总结RegEx相关的Groovy操作符
1.要从字符串创建一个模式,使用~操作符
2.要定义一个RegEx,使用正斜杠,像/[G|g]roovy/中这样。
3.要确定是否存在匹配,使用=~
4.对于精确匹配,使用==~

原创粉丝点击