3. Groovy 语法-字符串学习

2022-12-07 18:21:34 浏览数 (2)

1. 介绍

本篇为Groovy学习第三篇,接着学习关于Groovy的语法规则知识。

本篇主要学习Groovy中的字符串操作。

了解字符串中的拼接,表达式与字符串的混合拼接。

字符串中的各种转义字符等等。

有很多脚本和开发工具中的插件是使用Groovy进行开发的,了解Groovy可以扩展我们的知识面。

2. Strings 字符串

Groovy中允许两种实例化的字符对象存在,分别为:java.lang.Stringgroovy.lang.GString

而针对不同的字符串,它支持的多种引号标注。例如单引号:''和双引号""

2.1 单引号标注

单引号字符串是由单引号包围的一系列字符串。实例效果如:

代码语言:javascript复制
def zinyan='这是一个单引号标注的字符串'

通过单引号标注的字符串是纯java.lang.String对象,不支持插值。

和java中的字符串拼接一样,所有的groovy中的字符串也可以通过 号进行拼接。示例:

代码语言:javascript复制
assert 'ab' == 'a'   'b'

2.2 三重单引号标注

三重单引号字符串是由单引号的三联体包围的一系列字符。示例如下:

代码语言:javascript复制
def zinyan='''这是一个多重单引号标注的字符串'''

通过三重单引号标注的字符串是纯java.lang.String对象,不支持插值。

和单引号标注的一样。但是相较于单引号标注,三重单引号字符串可以跨越多行。字符串的内容可以跨越行边界,而不需要将字符串分割成几部分,也不需要连接或换行转义字符。示例如下:

代码语言:javascript复制
class Zinyan {
    def static main(def args) {
        def aMultilineString = '''这是第一行zin
这是第二行yan
这是第三行com
'''
        println(aMultilineString)
    }
}

输出的结果:

代码语言:javascript复制
这是第一行zin
这是第二行yan
这是第三行com

我们如果代码中有缩进,例如在类的方法体中,您的字符串将包含缩进的空白。Groovy Development Kit包含了一些方法,它们可以通过String.stripIndent()方法去掉缩进,还可以通过String.stripMargin()方法去掉缩进,该方法接受一个分隔符来标识要从字符串开头删除的文本。

代码语言:javascript复制
    def static main(def args) {
        // 在 Groovy 中可以使用 Java 语法
        def aMultilineString = '''
    这是第一行zin
    这是第二行yan
    这是第三行com
'''
        println(aMultilineString)
        def stripString=aMultilineString.stripIndent()
        println(stripString)
    }

输出结果就是:

代码语言:javascript复制
> Task :TestLib:Zinyan.main()
    这是第一行zin
    这是第二行yan
    这是第三行com

这是第一行zin
这是第二行yan
这是第三行com

字符串包含一个换行符作为第一个字符。可以通过使用反斜杠转义换行符来删除该字符。 所以在开头的三个单引号后面我添加了一个否则的话,会多一个换行

2.3 转义字符

和java中的转义字符是一样的。我们如果要输入换行等等,为了避免显示就可以通过实现转义。

例如:

代码语言:javascript复制
    def static main(def args) {
        def strippedFirstNewline = '这是一个转义单引号: ' 通过斜杠才能显示的(zinyan.com)'
        println(strippedFirstNewline)
    }

输出的结果为:

代码语言:javascript复制
这是一个转义单引号: ' 通过斜杠才能显示的(zinyan.com)

就能够正常的显示单引号了。否则输出的是单引号无法输出。类似的还有符号的输出。

代码语言:javascript复制
 def strippedFirstNewline = '这是一个转义斜杠符号: \ 通过斜杠才能显示的(zinyan.com)'

输出内容为:

代码语言:javascript复制
这是一个转义斜杠符号:  通过斜杠才能显示的(zinyan.com)

转义序列

字符

b

退格

f

跳页

n

换行

r

回车

s

一个空白空间(空格键)

t

tab按键空间(制表符)

\

一个反斜杠显示

'

一个单引号显示

"

一个双引号显示

这里只是列一些基本的,后面学习过程中,将会看到更多的转义细节和字符串。

本质上来说,和其他各种语言中的转义字符定义差不多。如果你对转义字符的相关概念和知识不明白,建议专门了解和学习一下。

在平常使用过程中,转义字符可以说是一个高频使用的功能了。

2.3.1 unicode字符

对于键盘上没有的字符,可以使用unicode转义序列:一个反斜杠,后面跟着'u',然后是4个十六进制数字。

例如人民币符号:

代码语言:javascript复制
def strippedFirstNewline ='人民币符号: uFFE5'
println(strippedFirstNewline)

然后我们输出 人民币符号: ¥

人民币的unicode编码为:U FFE5 所以我们可以通过转义字符实现unicode字符码转图案。

更多的unicode字符对应值可以通过 https://unicode-table.com/cn/blocks/ 了解

或者通过浏览器搜索相关的unicode字符表吧。我们比较少用到的罗马字符等 无法通过输入输出时可以通过unicode字符进行编码显示。

2.4 双引号字符

双引号字符串是由双引号包围的一系列字符,实例如下:

代码语言:javascript复制
def ztongxue ="这是一个双引号定义的字符串"

如果没有插值表达式,双引号字符串是纯java.lang.String,但如果有插值,则是groovy.lang.GString实例。(因为Groovy可以动态确定数据类型。)

任何Groovy表达式都可以插入到所有字符串中,单引号和三单引号字符串除外。插补是在计算字符串时用它的值替换字符串中的占位符的行为。占位符表达式被${}包围。

对于明确的点式表达式,花括号可以省略,也就是说,在这种情况下,我们可以只使用$前缀。如果GString被传递给一个接受String的方法,那么占位符中的表达式值将被求值为它的字符串表示形式(通过对该表达式调用toString()),并将得到的String传递给该方法。

通过示例简单理解一下:

代码语言:javascript复制
    def static main(def args) {
        def name = 'zinyan.com'
        def greeting = "Hello ${name}"
       println(greeting)
    }

输出的内容为:

代码语言:javascript复制
Hello zinyan.com

我们还可以通过表达式进行输入:

代码语言:javascript复制
    def static main(def args) {
        def sum = "2 3的结果是: ${2   3}"
       println(sum)
    }

输出内容为:

代码语言:javascript复制
2 3的结果是: 5

在上面的字符串中,使用了${} 所以生成的字符串是GString类型。并不是String类型。

不仅允许表达式出现在{}占位符之间,语句也可以。然而,语句的值只是null。因此,如果在占位符中插入了几个语句,最后一个语句应该以某种方式返回要插入的有意义的值。例如,“1和2的和等于{def a = 1;Def b = 2;a b}”是受支持的,而且工作正常,但一个好的实践通常是坚持使用GString占位符中的简单表达式。

除了{}占位符之外,还可以在虚线表达式前面使用单独的符号,示例如下:

代码语言:javascript复制
    def static main(def args) {
        def person = [name: 'Z同学', age: 3]
        def str = "$person.name 的年龄是: $person.age 岁!"
        println(str)
    }

输出结果为:

代码语言:javascript复制
Z同学 的年龄是: 3 岁!

但只有a.ba.b.c等形式的虚线表达式是有效的。

包含括号(如方法调用)、花括号(用于闭包)、不是属性表达式一部分的圆点或算术运算符的表达式将是无效的(例如加减运算符,小数点)。例如:

代码语言:javascript复制
    def static main(def args) {
        def number = 3.14
        def str = "$number.toString()"
    }

这种写法就是非法的了。就会在运行的时候出现groovy.lang.MissingPropertyException 异常了。

因为上面的示例,“number.toString()”被解析器解释为“{number.toString}()”

那么我们就是想显示在字符串中拼接上带小数的数字怎么办?很简单,添加{}就可以了。

示例:

代码语言:javascript复制
        def number = 3.14
        def str = "${number}"

总而言之,如果表达式输出的结果是模糊的。我们加上花括号就对了。

我们如果要转义字符串中的符号或者{}符号。只需要使用反斜杠字符来转义美元符号就可以了。示例如下:

代码语言:javascript复制
class Zinyan {
    def static main(def args) {
        def str1= "$5"
        def str2= "${name}"
        println(str1)
        println(str2)
    }
}

输出的内容为:

代码语言:javascript复制
$5
${name}
2.4.1 插值闭包表示的特殊情况

到目前为止,我们已经知道可以在{}占位符中插入任意表达式,但是对于闭包表达式有一种特殊情况和符号。当占位符包含一个箭头{→}时,表达式实际上是一个闭包表达式——你可以把它想象成一个前面加了

代码语言:javascript复制
    def static main(def args) {
        def sParameterLessClosure = "1   2 == ${-> 3}"
        def sOneParamClosure = "1   2 == ${ w -> w << 3}"
        println(sParameterLessClosure)
        println(sOneParamClosure)
    }

输出:

代码语言:javascript复制
1   2 == 3
1   2 == 3

闭包是一个不接受参数的无参数闭包。

在这里,闭包接受一个java.io.StringWriter参数,您可以使用<<操作符向其追加内容。在任何一种情况下,两个占位符都是嵌入式闭包。

从外观上看,它看起来像是定义要插值的表达式的一种更冗长的方式,但是闭包比单纯的表达式有一个有趣的优势:延迟求值

示例如下:

代码语言:javascript复制
    def static main(def args) {
        def number = 1
        def eagerGString = "value == ${number}"
        def lazyGString = "value == ${ -> number }"
        println(eagerGString)
        println(lazyGString)
        number = 2
        println(eagerGString)
        println(lazyGString)
    }

输出结果:

代码语言:javascript复制
value == 1
value == 1
value == 1
value == 2

可以看到,最后的lazyGString的值进行了变化。这就是闭包表达式的优势了。

对于普通插值表达式,值实际上是在创建GString时绑定的。

但是使用闭包表达式时,每次将GString强制转换为String时都会调用闭包,结果是一个包含新数字值的更新字符串。

请注意:接受多个参数的嵌入式闭包表达式将在运行时生成异常。只允许有零个或一个参数的闭包。

2.4.2 与java的互操性

当一个方法(无论用Java还是Groovy实现)需要java.lang.String。如果我们传递一个groovy.lang.GString实例,则GStringtoString()方法将被自动透明地调用。

也就是说,我们GString和String之间可以互相流畅的转换。

示例如下:

代码语言:javascript复制
    def static main(def args) {
        //我创建一个GString的对象。
        def message = "这个消息是 ${'zinyan.com'}"
        def result = takeString(message)
        println(result)
    }
    static String takeString(String message) {
        assert message instanceof String
        return message
    }

在上面的示例中,takeString要的入参是个String,而我们给它传的是一个GString对象。

Groovy会自动帮我们进行转换,调用GStringtoString方法将字符串转为String然后传递进去。

2.4.3 字符串的HashCodes

虽然插值字符串可以用来代替纯Java字符串,但它们与字符串有一个特殊的区别:它们的hashcode是不同的。普通Java字符串是不可变的,而GString的结果String表示可以根据其插入的值而变化。即使对于相同的结果字符串,GStrings和Strings也没有相同的hashCode。

所以,在Groovy中不能通过hashCode进行比较两个GString和String是否相同。

代码语言:javascript复制
"one: ${1}".hashCode() != "one: 1".hashCode()

例如上面的这种比较是不对的。

GString和String具有不同的hashCode值,应该避免使用GString作为Map键,特别是当我们试图检索与String而不是GString相关联的值时。例如:

代码语言:javascript复制
def key = "a"
def m = ["${key}": "letter ${key}"] 

不建议上面的这种写法。因为我们通过 m["a"]无法获取值,而会返回null

2.5 三重双引号标注

和单引号有三重一样。双引号也有三重样式。同时,也是定义多行字符串使用的。

代码语言:javascript复制
    def static main(def args) {
        def name = 'Zinyan'
        def template = """
    亲爱的 ${name} 同学:

    恭喜你,你的方案被采纳了。

    来自Z同学网站消息。
"""
        println(template)
    }

在三双引号的字符串中,双引号和单引号都不需要转义。我们可以直接使用。

其他的特性就和单引号是一样的。只是双引号可以插值而已。

2.6 斜杠字符串- Slashy String

除了通常的引号字符串,Groovy还提供斜杠字符串,它们使用/作为开始和结束分隔符。斜杠字符串对于定义正则表达式和模式特别有用,因为不需要转义反斜杠。斜杠字符串的例子:

代码语言:javascript复制
    def static main(def args) {
        def fooPattern = /.*zinyan.com.*/
        println(fooPattern)
    }

输出的内容为:

代码语言:javascript复制
.*zinyan.com.*

只有前斜杠需要用反斜杠转义:

代码语言:javascript复制
 def escapeSlash = /这是一个正斜杠: / 展示的效果(zinyan.com)/

输出结果为:

代码语言:javascript复制
这是一个正斜杠: / 展示的效果

斜杠字符串是通过一对斜杠来确定结束的。所以它默认是支持多行String的。示例:

代码语言:javascript复制
def multilineSlashy = /欢迎
    访问
    zinyan.com/

斜杠字符串也可以被认为是定义GString的另一种方式,但具有不同的转义规则。因此,它们支持插值,我们可以在斜杠定义的字符串中插入变量。示例如下:

代码语言:javascript复制
def color = 'blue'
def interpolatedSlashy = /a ${color} car/
2.6.1 特殊情况

空斜杠字符串不能用双正斜杠表示,因为Groovy解析器将其理解为行注释。这就是为什么下面的断言实际上不会编译,因为它看起来像一个非终止语句:

代码语言:javascript复制
def color = //
print(color)

所以,我们如果使用斜杠字符串。那么这个字符串必须不能为空。

因为斜杠字符串的设计主要是为了使regexp更容易,所以GString中的一些错误的东西,如()或5将与斜杠字符串一起工作。

记住,转义反斜杠不是必需的。斜杠转义的一个结果是斜杠字符串不能以反斜杠结束。否则将转义斜杠字符串结束符。您可以使用一个特殊的技巧:/这是我们的内容${''},添加有一个反斜杠,最后斜杠结尾/。但在这种情况下,最好避免使用斜杠字符串。

2.7 美元斜杠字符串

美元斜杠字符串是用开头/和结尾/分隔的多行GString。转义字符是符号,它可以转义另一个或向前斜杠。转义为和斜杠字符仅在与这些字符的特殊使用发生冲突时才需要。字符foo通常表示一个GString占位符,因此这四个字符可以通过转义美元输入到/字符串中,即

示例:

代码语言:javascript复制
    def static main(def args) {
        def name = "Zinyan.com"
        def date = "2022-11-05"

        def dollarSlashy = $/
    Hello $name,
    今天是 ${date}.
    $ 这是一个美元符号
    $$ 这是一个转义美元符号
     这是一个斜杠
    / 这是一个反斜杠
    $/ 这是一个转义反斜杠
    $$$/ 这是一个转义来的美元符号和反斜杠
    $/$$ 这是一个转义了结束语句
/$
        println(dollarSlashy)
    }

输出效果:

代码语言:javascript复制
    Hello Zinyan.com,
    今天是 2022-11-05.
    $ 这是一个美元符号
    $ 这是一个转义美元符号
     这是一个斜杠
    / 这是一个反斜杠
    / 这是一个转义反斜杠
    $/ 这是一个转义来的美元符号和反斜杠
    /$ 这是一个转义了结束语句

创建它是为了克服斜杠字符串转义规则的一些限制。当它的转义规则适合你的字符串内容时使用它。

简单来说,就是如果我们通过反斜杠转义不满足我们的需求的时候,可以试试$ /反斜杠的模式来转义。

2.8 字符串汇总

字符串名称

示例

插值

多行

转义字符

单引号字符串

'…'

三重单引号字符串

'''…'''

双引号字符串

"…"

三重双引号字符串

"""…"""

反斜杠字符串

/…/

美元反斜杠字符串

$/…/$

$

2.9 字符-characters

与Java不同,Groovy没有显式的字符文字。在Java中我们通过单引号创建字符Char对象。而在Groovy中默认单引号创建的对象是String。并不是Char对象。但是Groovy也支持Char对象。

可以通过三种不同的方式明确地将Groovy字符串变成实际的字符。示例如下:

代码语言:javascript复制
        char c1 = 'A' //通过在声明包含字符的变量时显式地指定字符类型
        def c2 = 'B' as char //通过使用as操作符的类型强制转换
        def c3 = (char)'C'  //通过使用转换到字符的操作

创建的对象都是Char类型。

当字符串保存在变量中使用的时候,可以使用第一种方式更合适。

而必须将Char值作为方法调用的参数进行传递时,使用第二种或第三种方式更合适。

官方说明文档:http://docs.groovy-lang.org/docs/groovy-4.0.6/html/documentation/#all-strings

3. 小结

到这里,我们针对Groovy中的String 可以说有一个很大的了解了。

会发现Groovy中的字符串定义和Kotlin和Python中有很多相识的地方。

这就是现在新的高级语言的一些特性了,很多新的高级语言大家的语法有很多相识之处。大家都是互相借鉴的结果。

0 人点赞