Strings and GStrings
6.1 String Literal Types
GString (if it has $) '''triple single''' """triple double"""
✓ (regex-friendly: no escaping backslashes) $/dollar slashy/$ ✓ (escape with $)
GString ≠ String: "$x".hashCode() differs from the String's hashCode. Don't use GStrings as map keys. Call .toString() when needed.
def name = "Ana"
def age = 30
println "Hello, $name! Next year: ${age + 1}"
println "Map access: ${[a: 1].a}, method: ${name.toUpperCase()}"
def lazy = "Time: ${-> new Date()}" // closure GString: evaluated when converted
def sql = """
SELECT *
FROM users
WHERE name = '$name'
""".stripIndent()
def path = /C:\Users\name\docs/ // no escaping needed
def regex = /\d{3}-\d{4}/
def dollar = $/Price: $$5 and path \dir/$6.2 String Operations
def s = "Hello, Groovy"
println s.size(), s.length(), s[0], s[-1], s[0..4], s[7..-1], s[-6..-1]
println s * 2 // repeat
println s - "Hello, " // remove the first occurrence
println s.reverse(), s.toUpperCase(), s.toLowerCase(), s.capitalize()
println s.contains("Groovy"), s.startsWith("He"), s.endsWith("vy")
println s.indexOf("o"), s.count("o"), s.replace("o", "0")
println "a,b,,c".split(","), "a,b,,c".tokenize(",") // split keeps empties; tokenize doesn't
println " pad ".trim(), "x".padLeft(5, '.'), "x".padRight(5), "x".center(5, '*')
println "line1\nline2".readLines(), "line1\nline2".eachLine { println it }
println "groovy".toList(), "groovy".toSet(), "a-b".tr('-', '_')
println "hello world".split().collect { it.capitalize() }.join(" ")
println "42".isNumber(), "abc".isAllWhitespace(), "Hi".matches(/H./)
println "%s is %d".format("Groovy", 4), String.format("%.2f", 3.14159)
println "abc".next(), "abc".getAt([0, 2])
println "Groovy".takeWhile { it != 'v' }, "Groovy".drop(2), "Groovy".take(3)
println "camelCaseString".replaceAll(/([A-Z])/) { "_" + it[0].toLowerCase() }6.3 Building Strings
def sb = new StringBuilder()
sb << "Hello" << ", " << "World" // leftShift appends
println sb
def text = ["a", "b", "c"].join(", ")
def w = new StringWriter()
w << "written"