Groovy from the ground up

Working with text

The String methods you will actually use: trimming, splitting, joining, replacing, padding, and formatting a line so the columns line up.

Part 10 of 12

Intermediate18 min readBack to Courses

Cleaning text up

Text arrives with baggage: trailing spaces from a form, a header row that needs trimming before it is compared. trim() removes whitespace from both ends and answers a new string; toUpperCase() and toLowerCase() change the case; capitalize() raises the first letter and leaves the rest of the text as it was. The rule is broader than the space bar: trim() removes every character at or below a space, so a tab or a newline at the edge goes too.

Every one of these answers a new string. Strings in Groovy are immutable — nothing you call changes the value you called it on — so a chain like raw.trim().toUpperCase() builds the answer in steps and leaves raw exactly as it was.

Cutting text into a list

'a-b-c'.split('-') cuts on a separator. Its argument is a regular expression, which is power and a trap: a dot means "any character", so splitting a file name on '.' needs '\\.'. tokenize() is the forgiving one, cutting on any run of whitespace.

Both answer arrays; toList() turns one into a list, which matters because the closure methods belong to lists. Joining goes the other way: names.join(' & ') puts the separator back in. substring(start, end) takes a piece by position, reverse() turns it round, and count(text) counts occurrences. An array and a list print the same way, so the difference only shows when a method insists on one or the other; toList() is the conversion that settles it, and it costs one call.

Formatting a line

sprintf fills in a pattern: %s is text, %d is a whole number, %.2f is a number with two decimals, and %-9s pads text to nine characters on the left of the value. A width on a number pads it on the left, which is how columns of figures line up in a report.

The arguments come last, as a list: sprintf('%-9s %6d', [name, count]). String.format does the same job with the arguments unlisted — both are fine, and both use the same patterns, so the skill transfers. A report is mostly this: a header, a rule, and one formatted line per row. sprintf("%-6s|%6.2f", ["ab", 1.5]) prints ab | 1.50, and widening either number moves the padding and the bar together.

Trim, split, join, format

def csv = 'ada,grace,linus'
def names = csv.split(',').toList()
println names
println names.join(' & ')

def line = '  total: 42  '
println '[' + line.trim() + ']'
println line.trim().tokenize()

println 'Groovy'.startsWith('Gr')
println 'Groovy'.contains('oo')
println 'Groovy'.indexOf('o')
println 'banana'.count('an')
println 'abc'.toList()
println String.format('%s=%d', 'id', 7)

Splitting is pattern matching

split and replaceAll take a regular expression, not a literal. A dot matches any character, \d matches a digit, + means one or more, and ^ and $ anchor to the ends. That is why 'a.b.c'.split('.') answers nothing useful: every character is a separator.

Two escapes stack. The regex for a literal dot is \., and in a Groovy string that backslash must itself be escaped, so the source says '\\.'. Use tokenize() for whitespace, split with a separator that has no regex meaning, or Pattern.quote(separator) when the separator comes from data.

Widths, padding and the shape of a line

sprintf is a stencil: %s places text, %d a whole number, %.2f a decimal with two places. A number before the letter is a minimum width, and a minus sign pads on the right. %-10s holds ten columns with the text at the left; %8.2f holds eight with the number at the right, which is what makes a column of money line up.

Formatting is presentation, so do it at the last moment, when you print. Keep the value as a number while you calculate, then format once. A string that has already been formatted is hard to add up, and parsing it back is work you did not need to do.

Three text mistakes: substring(1, 3) excludes the end index, so it answers two characters; replace and replaceAll answer a new string, because strings are immutable; and split drops trailing empty fields, so 'a,b,,'.split(',') has two elements, not four. Ask for split(',', -1) when the empty fields matter.

Try these next

  • Split a sentence with tokenize() and join the words with a hyphen.
  • Print a three-row table with %-8s and %6.2f columns.
  • Replace every space in a line with an underscore, then trim the result.

From a messy string to tidy columns

Run it and watch the same text take four shapes. Then split a sentence of your own into words, join them with a hyphen, and print the result in capitals.

Output
Click "Run" to execute your code

A log line, taken apart

Run it and follow each field of the line. Then change the level to `ERROR`, and add a line that prints the year and month from the date with two `substring` calls.

Output
Click "Run" to execute your code

split takes a regular expression, not plain text. Splitting on '.' gives an empty list because every character is a separator; the literal dot is '\\.'.

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

What does raw.trim() do to raw?