1. Overview
1.概述
In this tutorial, we’ll take a closer look at the several types of strings in Groovy, including single-quoted, double-quoted, triple-quoted, and slashy strings.
在本教程中,我们将仔细研究Groovy中的几种类型的字符串,包括单引号、双引号、三引号和slashy字符串。
We’ll also explore Groovy’s string support for special characters, multi-line, regex, escaping, and variable interpolation.
我们还将探讨Groovy的字符串对特殊字符、多行、regex、转义和变量插值的支持。
2. Enhancing java.lang.String
2.增强java.lang.String的功能
It’s probably good to begin by stating that since Groovy is based on Java, it has all of Java’s String capabilities like concatenation, the String API, and the inherent benefits of the String constant pool because of that.
首先要说明的是,由于Groovy是基于Java的,它拥有Java的所有String 能力,如连接、String API,以及String常量池的固有优势,这一点也许是不错的。
Let’s first see how Groovy extends some of these basics.
让我们先看看Groovy是如何扩展其中的一些基础知识的。
2.1. String Concatenation
2.1.字符串串联
String concatenation is just a combination of two strings:
字符串连接只是两个字符串的组合。
def first = 'first'
def second = "second"
def concatenation = first + second
assertEquals('firstsecond', concatenation)
Where Groovy builds on this is with its several other string types, which we’ll take a look at in a moment. Note that we can concatenate each type interchangeably.
Groovy在此基础上又增加了其他几种字符串类型,我们稍后会看一下这些类型。注意,我们可以互换地连接每种类型。
2.2. String Interpolation
2.2.字符串插值
Now, Java offers some very basic templating through printf, but Groovy goes deeper, offering string interpolation, the process of templating strings with variables:
现在,Java通过printf提供了一些非常基本的模板,但Groovy更深入,提供了字符串插值,用变量对字符串进行模板化的过程。
def name = "Kacper"
def result = "Hello ${name}!"
assertEquals("Hello Kacper!", result.toString())
While Groovy supports concatenation for all its string types, it only provides interpolation for certain types.
虽然Groovy支持所有字符串类型的连接,但它只为某些类型提供插值。
2.3. GString
2.3.GS字符串
But hidden in this example is a little wrinkle – why are we calling toString()?
但在这个例子中隐藏着一个小问题–我们为什么要调用toString()?。
Actually, result isn’t of type String, even if it looks like it.
实际上,result不是String类型,即使它看起来像。
Because the String class is final, Groovy’s string class that supports interpolation, GString, doesn’t subclass it. In other words, for Groovy to provide this enhancement, it has its own string class, GString, which can’t extend from String.
因为String类是final,Groovy的支持插值的字符串类GString并没有对它进行子类。换句话说,为了让Groovy提供这种增强功能,它有自己的字符串类,GString,它不能从String.延伸。
Simply put, if we did:
简单地说,如果我们这样做。
assertEquals("Hello Kacper!", result)
this invokes assertEquals(Object, Object), and we get:
这调用了assertEquals(Object, Object), ,我们得到了。
java.lang.AssertionError: expected: java.lang.String<Hello Kacper!>
but was: org.codehaus.groovy.runtime.GStringImpl<Hello Kacper!>
Expected :java.lang.String<Hello Kacper!>
Actual :org.codehaus.groovy.runtime.GStringImpl<Hello Kacper!>
3. Single-Quoted String
3.单引号字符串
Probably the simplest string in Groovy is one with single quotes:
Groovy中最简单的字符串可能是带有单引号的字符串:。
def example = 'Hello world'
Under the hood, these are just plain old Java Strings, and they come in handy when we need to have quotes inside of our string.
在引擎盖下,这些只是普通的老式Java 字符串,当我们需要在字符串中加入引号时,它们就会派上用场了。
Instead of:
而不是。
def hardToRead = "Kacper loves \"Lord of the Rings\""
We can easily concatenate one string with another:
我们可以很容易地将一个字符串与另一个字符串连接起来。
def easyToRead = 'Kacper loves "Lord of the Rings"'
Because we can interchange quote types like this, it reduces the need to escape quotes.
因为我们可以像这样互换引号类型,它减少了转义引号的需要。
4. Triple Single-Quote String
4.三个单引号的字符串
A triple single-quote string is helpful in the context of defining multi-line contents.
三重单引号字符串在定义多行内容的情况下是有帮助的。
For example, let’s say we have some JSON to represent as a string:
例如,假设我们有一些JSON要表示成一个字符串。
{
"name": "John",
"age": 20,
"birthDate": null
}
We don’t need to resort to concatenation and explicit newline characters to represent this.
我们不需要借助串联和明确的换行字符来表示这一点。
Instead, let’s use a triple single-quoted string:
相反,让我们使用一个三段式单引号的字符串。
def jsonContent = '''
{
"name": "John",
"age": 20,
"birthDate": null
}
'''
Groovy stores this as a simple Java String and adds the needed concatenation and newlines for us.
Groovy将其存储为一个简单的JavaString,并为我们添加所需的连接和换行。
There is one challenge yet to overcome, though.
不过,还有一个挑战有待克服。
Typically for code readability, we indent our code:
通常为了代码的可读性,我们缩进代码:。
def triple = '''
firstline
secondline
'''
But triple single-quote strings preserve whitespace. This means that the above string is really:
但是三个单引号的字符串保留了空格。这意味着上面的字符串实际上是。
(newline)
firstline(newline)
secondline(newline)
not:
不是。
1
2
|
firstline(newline) secondline(newline) |
like perhaps we intended.
也许像我们打算的那样。
Stay tuned to see how we get rid of them.
请继续关注,看看我们如何摆脱它们。
4.1. Newline Character
4.1.换行符
Let’s confirm that our previous string starts with a newline character:
让我们确认我们之前的字符串以换行符开始。
assertTrue(triple.startsWith("\n"))
It’s possible to strip that character. To prevent this, we need to put a single backslash \ as a first and last character:
有可能剥离这个字符。为了防止这种情况,我们需要把一个反斜杠作为第一个和最后一个字符。
def triple = '''\
firstline
secondline
'''
Now, we at least have:
现在,我们至少有。
1
2
|
firstline(newline) secondline(newline) |
One problem down, one more to go.
一个问题解决了,还有一个问题要解决。
4.2. Strip the Code Indentation
4.2.剥离代码缩进
Next, let’s take care of the indentation. We want to keep our formatting, but remove unnecessary whitespace characters.
接下来,让我们来处理缩进的问题。我们希望保留我们的格式,但删除不必要的空白字符。
The Groovy String API comes to the rescue!
Groovy 字符串API来拯救我们了!
To remove leading spaces on every line of our string, we can use one of the Groovy default methods, String#stripIndent():
为了去除字符串每一行的前导空格,我们可以使用Groovy的一个默认方法,String#stripIndent():。
def triple = '''\
firstline
secondline'''.stripIndent()
assertEquals("firstline\nsecondline", triple)
Please note, that by moving the ticks up a line, we’ve also removed a trailing newline character.
请注意,通过将刻度线上移一行,我们也删除了尾部的换行符。
4.3. Relative Indentation
4.3.相对缩进
We should remember that stripIndent is not called stripWhitespace.
我们应该记住,stripIndent并没有调用stripWhitespace.。
stripIndent determines the amount of indentation from the shortened, non-whitespace line in the string.
stripIndent 决定了从字符串中缩短的非空白行中缩进的数量。
So, let’s change the indentation quite a bit for our triple variable:
所以,让我们为我们的triple变量改变一下缩进的方式。
class TripleSingleQuotedString {
@Test
void 'triple single quoted with multiline string with last line with only whitespaces'() {
def triple = '''\
firstline
secondline\
'''.stripIndent()
// ... use triple
}
}
Printing triple would show us:
打印triple会让我们看到。
firstline
secondline
Since firstline is the least-indented non-whitespace line, it becomes zero-indented with secondline still indented relative to it.
由于第一行是缩进最少的非空白行,它成为零缩进,第二行仍然相对于它缩进。
Note also that this time, we are removing the trailing whitespace with a slash, like we saw earlier.
还要注意的是,这次我们是用斜线去除尾部的空白,就像我们之前看到的那样。
4.4. Strip with stripMargin()
4.4.用stripMargin()剥离
For even more control, we can tell Groovy right where to start the line by using a | and stripMargin:
为了实现更多的控制,我们可以通过使用|和stripMargin来告诉Groovy在哪里开始行:。
def triple = '''\
|firstline
|secondline'''.stripMargin()
Which would display:
这将显示。
firstline
secondline
The pipe states where that line of the string really starts.
管道说明了该行字符串的真正开始位置。。
Also, we can pass a Character or CharSequence as an argument to stripMargin with our custom delimiter character.
另外,我们可以传递一个字符或字符序列作为stripMargin的参数,其中有我们自定义的分隔符。
Great, we got rid of all unnecessary whitespace, and our string contains only what we want!
很好,我们摆脱了所有不必要的空白,我们的字符串只包含了我们想要的东西。
4.5. Escaping Special Characters
4.5.转移特殊字符
With all the upsides of the triple single-quote string, there is a natural consequence of needing to escape single quotes and backslashes that are part of our string.
在三重单引号字符串的所有优点中,有一个自然的结果是需要转义单引号和反斜线,这是我们字符串的一部分。
To represent special characters, we also need to escape them with a backslash. The most common special characters are a newline (\n) and tabulation (\t).
为了表示特殊字符,我们还需要用反斜杠来转义。最常见的特殊字符是换行(n)和制表(t)。
For example:
比如说。
def specialCharacters = '''hello \'John\'. This is backslash - \\ \nSecond line starts here'''
will result in:
将导致。
hello 'John'. This is backslash - \
Second line starts here
There are a few we need to remember, namely:
有几个我们需要记住,即。
- \t – tabulation
- \n – newline
- \b – backspace
- \r – carriage return
- \\ – backslash
- \f – formfeed
- \’ – single quote
5. Double-Quoted String
5.双引号字符串
While double-quoted strings are also just Java Strings, their special power is interpolation. When a double-quoted string contains interpolation characters, Groovy switches out the Java String for a GString.
虽然双引号字符串也只是Java Strings,但它们的特殊功能是插值。当一个双引号字符串包含插值字符时,Groovy会把Java String换成GString。
5.1. GString and Lazy Evaluation
5.1. GString和懒惰评估
We can interpolate a double-quoted string by surrounding expressions with ${} or with $ for dotted expressions.
我们可以通过用${}环绕表达式来插值双引号字符串,或者用$来插值带点表达式。
Its evaluation is lazy, though – it won’t be converted to a String until it is passed to a method that requires a String:
它的评估是懒惰的,但是 – 它不会被转换为String,直到它被传递到需要String的方法。
def string = "example"
def stringWithExpression = "example${2}"
assertTrue(string instanceof String)
assertTrue(stringWithExpression instanceof GString)
assertTrue(stringWithExpression.toString() instanceof String)
5.2. Placeholder with Reference to a Variable
5.2.参照变量的占位符
The first thing we probably want to do with interpolation is send it a variable reference:
我们可能想用插值法做的第一件事是给它发送一个变量引用。
def name = "John"
def helloName = "Hello $name!"
assertEquals("Hello John!", helloName.toString())
5.2. Placeholder with an Expression
5.2.带有表达式的占位符
But, we can also give it expressions:
但是,我们也可以赋予它表达方式。
def result = "result is ${2 * 2}"
assertEquals("result is 4", result.toString())
We can put even statements into placeholders, but it’s considered as bad practice.
我们甚至可以把语句放到占位符中,但这被认为是不好的做法。
5.3. Placeholders with the Dot Operator
5.3.使用点运算符的占位符
We can even walk object hierarchies in our strings:
我们甚至可以在我们的字符串中行走对象的层次结构。
def person = [name: 'John']
def myNameIs = "I'm $person.name, and you?"
assertEquals("I'm John, and you?", myNameIs.toString())
With getters, Groovy can usually infer the property name.
通过getters,Groovy通常可以推断出属性名称。
But if we call a method directly, we’ll need to use ${} because of the parentheses:
但是如果我们直接调用一个方法,我们就需要使用${} 因为有括号。
def name = 'John'
def result = "Uppercase name: ${name.toUpperCase()}".toString()
assertEquals("Uppercase name: JOHN", result)
5.4. hashCode in GString and String
5.4.hashCode在GString和String中
Interpolated strings are certainly godsends in comparison to plain java.util.String, but they differ in an important way.
与普通的java.util.String相比,插值字符串当然是天赐良机,但它们在一个重要的方面有所不同。
See, Java Strings are immutable, and so calling hashCode on a given string always returns the same value.
看,Java的字符串是不可变的,因此在给定的字符串上调用hashCode总是返回相同的值。
But, GString hashcodes can vary since the String representation depends on the interpolated values.
但是,GString哈希代码可能会有所不同,因为String的表示取决于插值。
And actually, even for the same resulting string, they won’t have the same hash codes:
而且实际上,即使是相同的结果字符串,它们也不会有相同的哈希代码:。
def string = "2+2 is 4"
def gstring = "2+2 is ${4}"
assertTrue(string.hashCode() != gstring.hashCode())
Thus, we should never use GString as a key in a Map!
因此,我们不应该使用GString作为Map中的键!。
6. Triple Double-Quote String
6.三重双引号字符串
So, we’ve seen triple single-quote strings, and we’ve seen double-quoted strings.
因此,我们已经看到了三重单引号的字符串,我们也看到了双引号的字符串。
Let’s combine the power of both to get the best of both worlds – multi-line string interpolation:
让我们结合两者的力量来获得两全其美的效果–多行字符串插值:。
def name = "John"
def multiLine = """
I'm $name.
"This is quotation from 'War and Peace'"
"""
Also, notice that we didn’t have to escape single or double-quotes!
此外,注意到我们不必转义单引号或双引号!
7. Slashy String
7.滑溜溜的绳子
Now, let’s say that we are doing something with a regular expression, and we are thus escaping backslashes all over the place:
现在,让我们假设我们正在用正则表达式做一些事情,因此我们正在到处转义反斜线。
def pattern = "\\d{1,3}\\s\\w+\\s\\w+\\\\\\w+"
It’s clearly a mess.
这显然是一个烂摊子。
To help with this, Groovy supports regex natively via slashy strings:
为了帮助解决这个问题,Groovy通过slashy字符串原生支持regex:。
def pattern = /\d{3}\s\w+\s\w+\\\w+/
assertTrue("3 Blind Mice\Men".matches(pattern))
Slashy strings may be both interpolated and multi-line:
Slashy字符串可以是插值的,也可以是多行的。
def name = 'John'
def example = /
Dear ([A-Z]+),
Love, $name
/
Of course, we have to escape forward slashes:
当然,我们必须逃避正斜线。
def pattern = /.*foobar.*\/hello.*/
And we can’t represent an empty string with Slashy String since the compiler understands // as a comment:
而且我们不能用Slashy String表示一个空字符串,因为编译器将//理解为一个注释。
// if ('' == //) {
// println("I can't compile")
// }
8. Dollar-Slashy String
8.美元-光滑的绳子
Slashy strings are great, though it’s a bummer to have to escape the forward slash. To avoid additional escaping of a forward slash, we can use a dollar-slashy string.
斜线型字符串很好,尽管不得不转义正斜线是件令人沮丧的事。为了避免额外转义正斜线,我们可以使用美元斜线字符串。
Let’s assume that we have a regex pattern: [0-3]+/[0-3]+. It’s a good candidate for dollar-slashy string because in a slashy string, we would have to write: [0-3]+//[0-3]+.
让我们假设我们有一个重码模式。[0-3]+/[0-3]+.这是一个很好的候选的$slashy字符串,因为在一个slashy字符串中,我们将不得不写。[0-3]+//[0-3]+.
Dollar-slashy strings are multiline GStrings that open with $/ and close with /$. To escape a dollar or forward slash, we can precede it with the dollar sign ($), but it’s not necessary.
美元斜线字符串是多行GStrings,以$/开头,以/$结尾。为了逃避美元或正斜线,我们可以在它前面加上美元符号($),但这并不是必须的。
We don’t need to escape $ in GString placeholder.
我们不需要在GString占位符中转义$。
For example:
比如说。
def name = "John"
def dollarSlashy = $/
Hello $name!,
I can show you a $ sign or an escaped dollar sign: $$
Both slashes work: \ or /, but we can still escape it: $/
We have to escape opening and closing delimiters:
- $$$/
- $/$$
/$
would output:
会输出。
Hello John!,
I can show you a $ sign or an escaped dollar sign: $
Both slashes work: \ or /, but we can still escape it: /
We have to escape opening and closing delimiter:
- $/
- /$
9. Character
9.性格
Those familiar with Java have already wondered what Groovy did with characters since it uses single quotes for strings.
熟悉Java的人已经想知道Groovy是如何处理字符的,因为它对字符串使用单引号。
Actually, Groovy doesn’t have an explicit character literal.
实际上,Groovy 没有一个明确的字符字面。
There are three ways to make a Groovy string an actual character:
有三种方法使Groovy字符串成为实际字符。
- explicit use of ‘char’ keyword when declaring a variable
- using ‘as’ operator
- by casting to ‘char’
Let’s take a look at them all:
让我们一起来看看。
char a = 'A'
char b = 'B' as char
char c = (char) 'C'
assertTrue(a instanceof Character)
assertTrue(b instanceof Character)
assertTrue(c instanceof Character)
The first way is very convenient when we want to keep the character as a variable. The other two methods are more interesting when we want to pass a character as an argument to a function.
当我们想把字符作为一个变量保留时,第一种方法非常方便。当我们想把一个字符作为参数传给一个函数时,另外两种方法更有意思。
10. Summary
10.总结
Obviously, that was a lot, so let’s quickly summarize some key points:
显然,那是一个很大的问题,所以让我们快速总结一些关键点。
- strings created with a single quote (‘) do not support interpolation
- slashy and tripled double-quote strings can be multi-line
- multi-line strings contain whitespace characters due to code indentation
- backslash (\) is used to escape special characters in every type, except dollar-slashy string, where we must use dollar ($) to escape
11. Conclusion
11.结语
In this article, we discussed many ways to create a string in Groovy and its support for multi-lines, interpolation, and regex.
在这篇文章中,我们讨论了在Groovy中创建字符串的许多方法,以及它对多行、插值和regex的支持。
All these snippets are available over on Github.
所有这些片段都可以在Github上找到over。
And for more information about features of the Groovy language itself, get a good start with our introduction to Groovy Language.
而关于Groovy语言本身特点的更多信息,请从我们的Groovy语言介绍中获得一个好的开始。