12 Groovy Substring Techniques Every Developer Needs

Groovy substring extraction with 12 examples covering substring(), subscript operator, take(), drop(), and negative indices. Tested on Groovy 5.x.

“Extracting the right piece of a string is like cutting a diamond – precision matters, and the right tool makes all the difference.”

Donald Knuth, The Art of Computer Programming

Last Updated: March 2026 | Tested on: Groovy 5.x, Java 17+ | Difficulty: Beginner to Intermediate | Reading Time: 16 minutes

When you’re working with strings in Groovy, one of the most common things you’ll need to do is extract a part of a string – a groovy substring. Maybe you need the domain from an email address, the file extension from a path, or just the first few characters of a long piece of text. Whatever the case, Groovy gives you several ways to do it, and most of them are far more elegant than what Java offers.

If you’ve used Java’s substring() method, you already know the basics. But Groovy takes string extraction to another level with its subscript operator, range-based indexing, negative indices, and GDK methods like take() and drop(). This post covers all of them with tested, working examples.

If you haven’t read the foundational post yet, start with our Groovy String Tutorial – The Complete Guide. For related operations, also check out Groovy take() and Groovy reverse().

What Is a Groovy Substring?

A groovy substring is simply a portion of an existing string. You specify where to start (and optionally where to stop), and Groovy hands you back a new string containing just those characters. Unlike some languages where substring operations are confusing, Groovy makes this surprisingly intuitive.

According to the official Groovy GDK documentation, Groovy enhances Java’s String class with additional methods and operator overloading that make substring operations cleaner and more readable.

Here’s what you can use for groovy string extract operations:

  • substring(beginIndex) – inherited from Java, returns everything from the given index to the end
  • substring(beginIndex, endIndex) – inherited from Java, returns characters between the two indices
  • str[index] – Groovy’s subscript operator for single character access
  • str[range] – Groovy’s subscript operator with ranges for slicing
  • take(n) – get the first N characters (safe, never throws)
  • drop(n) – skip the first N characters, return the rest
  • getAt(index) and getAt(range) – the method behind the subscript operator

Substring Methods at a Glance

MethodSyntaxReturnsThrows on Out-of-Bounds?
substring(begin)str.substring(5)From index 5 to endYes
substring(begin, end)str.substring(0, 5)From index 0 to 4Yes
Subscript [index]str[3]Single character at index 3Yes
Subscript [range]str[0..4]Characters 0 through 4 (inclusive)Yes
Negative indexstr[-3..-1]Last 3 charactersYes
take(n)str.take(5)First 5 charactersNo (safe)
drop(n)str.drop(3)Everything after first 3No (safe)
getAt(range)str.getAt(0..4)Same as str[0..4]Yes

Notice that take() and drop() are the only safe methods – they won’t throw an exception if you ask for more characters than the string has. We’ll see this in action shortly.

12 Practical Groovy Substring Examples

Example 1: Basic substring() – From Index to End

What we’re doing: Using Java’s substring(beginIndex) method to get everything from a given position to the end of the string.

Example 1: substring(beginIndex)

def text = "Hello, Groovy World!"

// Get everything from index 7 onwards
def result1 = text.substring(7)
println "From index 7: '${result1}'"

// Get everything from index 0 (the whole string)
def result2 = text.substring(0)
println "From index 0: '${result2}'"

// Get from the last word
def spaceIndex = text.lastIndexOf(' ')
def lastWord = text.substring(spaceIndex + 1)
println "Last word: '${lastWord}'"

Output

From index 7: 'Groovy World!'
From index 0: 'Hello, Groovy World!'
Last word: 'World!'

What happened here: substring(7) returns everything starting from index 7 (the ‘G’ in “Groovy”). The index is zero-based, so index 0 is ‘H’, index 1 is ‘e’, and so on. Combining lastIndexOf() with substring() is a common pattern for extracting the last word or segment.

Example 2: substring() – With Begin and End Index

What we’re doing: Using substring(beginIndex, endIndex) to extract a specific range of characters.

Example 2: substring(begin, end)

def text = "Hello, Groovy World!"

// Extract "Groovy" (index 7 to 13)
def groovy = text.substring(7, 13)
println "Extracted: '${groovy}'"

// Extract "Hello"
def hello = text.substring(0, 5)
println "Extracted: '${hello}'"

// Extract "World"
def world = text.substring(14, 19)
println "Extracted: '${world}'"

// Length of substring = endIndex - beginIndex
println "Length check: ${groovy.length()} == ${13 - 7}"

Output

Extracted: 'Groovy'
Extracted: 'Hello'
Extracted: 'World'
Length check: 6 == 6

What happened here: The two-argument substring(begin, end) returns characters from begin (inclusive) to end (exclusive). This is the same behavior as Java – the end index is NOT included in the result. The resulting string’s length is always endIndex - beginIndex.

Example 3: Groovy Subscript Operator – Single Character

What we’re doing: Using Groovy’s [] subscript operator to access individual characters – much cleaner than Java’s charAt().

Example 3: Subscript Operator – Single Character

def text = "Groovy"

// Access individual characters
println "First character: ${text[0]}"
println "Third character: ${text[2]}"
println "Last character: ${text[5]}"

// Negative indices count from the end
println "Last character (negative): ${text[-1]}"
println "Second to last: ${text[-2]}"
println "Third to last: ${text[-3]}"

// Type check - returns a String, not a char!
println "Type: ${text[0].getClass().name}"

Output

First character: G
Third character: o
Last character: y
Last character (negative): y
Second to last: v
Third to last: o
Type: java.lang.String

What happened here: Groovy’s subscript operator [] is syntactic sugar for the getAt() method. Unlike Java’s charAt() which returns a char, Groovy’s [] returns a String. And here’s the fun part – negative indices count backwards from the end, so text[-1] gives you the last character without knowing the string’s length.

Example 4: Subscript Operator with Ranges – Groovy Substring Slicing

What we’re doing: Using Groovy’s range operator inside subscript brackets for flexible groovy substring slicing.

Example 4: Subscript with Ranges

def text = "Hello, Groovy World!"

// Inclusive range: characters 0 through 4
println "text[0..4]: '${text[0..4]}'"

// Extract "Groovy" using range
println "text[7..12]: '${text[7..12]}'"

// Negative range: last 6 characters
println "text[-6..-1]: '${text[-6..-1]}'"

// Mixed: from index 7 to 3rd from end
println "text[7..-8]: '${text[7..-8]}'"

// Reverse range: reverses the substring!
println "text[4..0]: '${text[4..0]}'"

// Exclusive range with ..<
println "text[0..<5]: '${text[0..<5]}'"

Output

text[0..4]: 'Hello'
text[7..12]: 'Groovy'
text[-6..-1]: 'World!'
text[7..-8]: 'Groovy'
text[4..0]: 'olleH'
text[0..<5]: 'Hello'

What happened here: This is where Groovy really shines for groovy string extract operations. The range 0..4 is inclusive on both ends (unlike substring() where end is exclusive). You can use ..< for an exclusive end. Negative indices work inside ranges too. And if you reverse the range (like 4..0), Groovy reverses the result – a neat trick.

Example 5: take() – Get First N Characters Safely

What we’re doing: Using the GDK’s take() method to safely extract the first N characters without worrying about index bounds. For more details, see our dedicated Groovy take() post.

Example 5: take() – Safe First N Characters

def text = "Groovy Programming"

// Take first 6 characters
println "take(6): '${text.take(6)}'"

// Take first 3 characters
println "take(3): '${text.take(3)}'"

// Take more than string length - no exception!
println "take(100): '${text.take(100)}'"

// Take 0 characters
println "take(0): '${text.take(0)}'"

// Compare with substring (which would throw)
try {
    text.substring(0, 100)
} catch (StringIndexOutOfBoundsException e) {
    println "substring(0, 100) threw: ${e.class.simpleName}"
}

// take() is safe - returns what it can
println "Safe take: '${text.take(100)}'"

Output

take(6): 'Groovy'
take(3): 'Gro'
take(100): 'Groovy Programming'
take(0): ''
substring(0, 100) threw: StringIndexOutOfBoundsException
Safe take: 'Groovy Programming'

What happened here: take(n) grabs the first N characters, but if N is larger than the string’s length, it just returns the entire string instead of throwing an exception. This makes it perfect for situations where you don’t know the input length ahead of time – truncating user display names, preview text, or log output.

Example 6: drop() – Skip First N Characters

What we’re doing: Using drop() to remove the first N characters and return the rest. Think of it as the opposite of take().

Example 6: drop() – Skip First N Characters

def text = "Groovy Programming"

// Drop first 7 characters
println "drop(7): '${text.drop(7)}'"

// Drop first character
println "drop(1): '${text.drop(1)}'"

// Drop more than string length - returns empty, no exception
println "drop(100): '${text.drop(100)}'"

// Drop 0 - returns entire string
println "drop(0): '${text.drop(0)}'"

// Combine take and drop to extract middle
def middle = text.drop(3).take(3)
println "drop(3).take(3): '${middle}'"

// take + drop always reconstructs the original
def first = text.take(7)
def rest = text.drop(7)
println "Reconstructed: '${first + rest}'"

Output

drop(7): 'Programming'
drop(1): 'roovy Programming'
drop(100): ''
drop(0): 'Groovy Programming'
drop(3).take(3): 'ovy'
Reconstructed: 'Groovy Programming'

What happened here: drop(n) removes the first N characters. Just like take(), it’s safe – dropping more characters than available just returns an empty string. The combo of drop(n).take(m) is a really handy pattern for extracting a substring from the middle without doing index math.

Example 7: Negative Indices – Count from the End

What we’re doing: Using negative indices to access characters and extract substrings from the end of a string.

Example 7: Negative Indices

def filename = "report_2026_final.pdf"

// Last 4 characters (file extension with dot)
println "Extension: '${filename[-4..-1]}'"

// Last 3 characters (extension without dot)
println "Ext only: '${filename[-3..-1]}'"

// Everything except last 4 characters
println "Without ext: '${filename[0..-5]}'"

// Last character
println "Last char: '${filename[-1]}'"

// Negative indices on a URL
def url = "https://technoscripts.com/groovy-substring"
println "Last segment: '${url[url.lastIndexOf('/')..-1]}'"

// Get last N characters using a method
def lastN = { str, n -> str[-n..-1] }
println "Last 3 of 'Groovy': '${lastN('Groovy', 3)}'"

Output

Extension: '.pdf'
Ext only: 'pdf'
Without ext: 'report_2026_final'
Last char: f
Last segment: '/groovy-substring'
Last 3 of 'Groovy': 'ovy'

What happened here: Negative indices are one of Groovy’s best features for groovy substring work. -1 is the last character, -2 is second to last, and so on. The range [-4..-1] gives you the last 4 characters. You can even mix positive and negative: [0..-5] means “from the beginning to 5th from the end.”

Example 8: getAt() – The Method Behind the Subscript

What we’re doing: Using getAt() directly – the method that Groovy calls when you use the [] operator.

Example 8: getAt() Method

def text = "Groovy Rocks!"

// getAt with single index - same as text[0]
println "getAt(0): '${text.getAt(0)}'"

// getAt with range - same as text[0..5]
println "getAt(0..5): '${text.getAt(0..5)}'"

// getAt with negative index
println "getAt(-1): '${text.getAt(-1)}'"

// getAt with a collection of indices
println "getAt([0, 2, 4, 6]): '${text.getAt([0, 2, 4, 6])}'"

// Useful for programmatic access
def indices = [0, 7, 8, 9, 10, 11]
def extracted = indices.collect { text.getAt(it) }.join('')
println "Collected: '${extracted}'"

Output

getAt(0): 'G'
getAt(0..5): 'Groovy'
getAt(-1): '!'
getAt([0, 2, 4, 6]): 'Gov '
Collected: 'GRocks'

What happened here: Every time you write text[0..5], Groovy translates it to text.getAt(0..5). You can also pass a list of individual indices to getAt(), which is useful when you need characters at non-contiguous positions. This is handy for extracting characters based on a computed pattern.

Example 9: Extract First and Last N Characters

What we’re doing: Comparing different approaches to get the first and last N characters of a groovy substring.

Example 9: First and Last N Characters

def text = "Apache Groovy"

// First 6 characters - three ways
println "substring: '${text.substring(0, 6)}'"
println "range:     '${text[0..5]}'"
println "take:      '${text.take(6)}'"

// Last 6 characters - three ways
println "substring: '${text.substring(text.length() - 6)}'"
println "range:     '${text[-6..-1]}'"
println "drop:      '${text.drop(text.length() - 6)}'"

// First character
println "First: '${text[0]}'"

// Last character
println "Last: '${text[-1]}'"

// First 3 and last 3 combined
def preview = "${text.take(3)}...${text[-3..-1]}"
println "Preview: '${preview}'"

Output

substring: 'Apache'
range:     'Apache'
take:      'Apache'
substring: 'Groovy'
range:     'Groovy'
drop:      'Groovy'
First: 'A'
Last: 'y'
Preview: 'Apa...ovy'

What happened here: There are multiple ways to get the first or last N characters in Groovy. The take() method is safest for first-N, and the negative range [-n..-1] is the cleanest for last-N. The preview pattern ("${text.take(3)}...${text[-3..-1]}") is great for creating truncated displays.

Example 10: Extract Between Delimiters

What we’re doing: Extracting text between specific delimiters – brackets, tags, quotes, or custom markers.

Example 10: Extract Between Delimiters

// Extract between parentheses
def text1 = "Hello (World) Groovy"
def start1 = text1.indexOf('(') + 1
def end1 = text1.indexOf(')')
println "Between parens: '${text1.substring(start1, end1)}'"

// Extract between square brackets
def text2 = "Error [CODE_404] occurred"
def start2 = text2.indexOf('[') + 1
def end2 = text2.indexOf(']')
println "Between brackets: '${text2[start2..<end2]}'"

// Extract between two markers
def text3 = "START>>>payload data<<<END"
def startMarker = "START>>>"
def endMarker = "<<<"
def payload = text3.substring(
    text3.indexOf(startMarker) + startMarker.length(),
    text3.indexOf(endMarker)
)
println "Payload: '${payload}'"

// Extract all values between curly braces using findAll
def template = "Hello {name}, your {item} is ready at {location}"
def placeholders = template.findAll(/\{(\w+)\}/) { full, group -> group }
println "Placeholders: ${placeholders}"

// Reusable closure for between extraction
def between = { str, left, right ->
    def s = str.indexOf(left)
    def e = str.indexOf(right, s + left.length())
    (s >= 0 && e >= 0) ? str.substring(s + left.length(), e) : null
}
println "Reusable: '${between('Say [hello] now', '[', ']')}'"

Output

Between parens: 'World'
Between brackets: 'CODE_404'
Payload: 'payload data'
Placeholders: [name, item, location]
Reusable: 'hello'

What happened here: Extracting between delimiters is one of the most common real-world substring operations. The pattern is always the same: find the start delimiter, find the end delimiter, then grab everything in between. The reusable closure version adds safety by checking if both delimiters exist before extracting. The findAll() with regex is perfect when you have multiple occurrences.

Example 11: Parsing URLs and Email Addresses

What we’re doing: Real-world groovy string extract operations on URLs and email addresses.

Example 11: Parsing URLs and Emails

// Parse URL components
def url = "https://technoscripts.com/groovy-substring-examples/?ref=home"

// Protocol
def protocol = url.substring(0, url.indexOf('://'))
println "Protocol: ${protocol}"

// Domain
def afterProtocol = url.substring(url.indexOf('://') + 3)
def domain = afterProtocol.substring(0, afterProtocol.indexOf('/'))
println "Domain: ${domain}"

// Path (without query string)
def path = afterProtocol.substring(afterProtocol.indexOf('/'))
if (path.contains('?')) {
    path = path.substring(0, path.indexOf('?'))
}
println "Path: ${path}"

// Query string
def query = url.contains('?') ? url.substring(url.indexOf('?') + 1) : ''
println "Query: ${query}"

println "---"

// Parse email address
def email = "developer@technoscripts.com"

def username = email.substring(0, email.indexOf('@'))
def emailDomain = email.substring(email.indexOf('@') + 1)
def tld = emailDomain[emailDomain.lastIndexOf('.') + 1 .. -1]

println "Username: ${username}"
println "Domain: ${emailDomain}"
println "TLD: ${tld}"

Output

Protocol: https
Domain: technoscripts.com
Path: /groovy-substring-examples/
Query: ref=home
---
Username: developer
Domain: technoscripts.com
TLD: com

What happened here: This is where groovy substring skills pay off in daily work. Parsing URLs and emails requires chaining indexOf() and substring() calls together. For production code, you’d probably use java.net.URI for URLs, but understanding how to do it with substrings is essential for custom parsing tasks where standard libraries don’t fit.

Example 12: Combining Substring Techniques

What we’re doing: Combining everything we’ve learned into practical, compound substring operations.

Example 12: Combined Techniques

// Truncate with ellipsis
def truncate = { str, max ->
    str.length() <= max ? str : "${str.take(max - 3)}..."
}
println truncate("Groovy is amazing for string manipulation", 25)
println truncate("Short", 25)

// Mask credit card number
def card = "4532-1234-5678-9012"
def masked = "${'*' * 14}${card[-4..-1]}"
println "Masked: ${masked}"

// Extract file info from path
def filepath = "/home/user/documents/report_final_v2.pdf"
def filename = filepath[filepath.lastIndexOf('/') + 1 .. -1]
def name = filename[0 ..< filename.lastIndexOf('.')]
def ext = filename[filename.lastIndexOf('.') + 1 .. -1]
println "File: ${filename}"
println "Name: ${name}"
println "Extension: ${ext}"

// Pad and truncate for fixed-width formatting
def fixedWidth = { str, width ->
    str.take(width).padRight(width)
}
println "|${fixedWidth('Groovy', 10)}|"
println "|${fixedWidth('A very long language name', 10)}|"

// Extract all words longer than 4 chars
def sentence = "The quick brown fox jumps over the lazy dog"
def longWords = sentence.split(' ').findAll { it.length() > 4 }
println "Long words: ${longWords}"

Output

Groovy is amazing for ...
Short
Masked: **************9012
File: report_final_v2.pdf
Name: report_final_v2
Extension: pdf
|Groovy    |
|A very lon|
Long words: [quick, brown, jumps]

What happened here: These are patterns you’ll use in real projects. The truncate function uses take() for safety. Credit card masking uses string multiplication and negative indexing. File path parsing chains lastIndexOf() with the subscript operator. The fixed-width formatter combines take() with padRight(). All of these are bread-and-butter substring operations that come up regularly.

Safe Substring Operations

One of the biggest headaches with Java’s substring() is StringIndexOutOfBoundsException. Groovy provides several ways to avoid this pain.

Safe Substring Approaches

def text = "Groovy"

// UNSAFE: Java substring throws on bad indices
try {
    text.substring(0, 100)
} catch (e) {
    println "substring(0, 100): ${e.class.simpleName}"
}

// UNSAFE: subscript operator throws too
try {
    text[0..100]
} catch (e) {
    println "text[0..100]: ${e.class.simpleName}"
}

// SAFE: take() never throws
println "take(100): '${text.take(100)}'"

// SAFE: drop() never throws
println "drop(100): '${text.drop(100)}'"

// SAFE: Custom safe substring
def safeSubstring = { str, start, end = str.length() ->
    def s = Math.max(0, Math.min(start, str.length()))
    def e = Math.max(s, Math.min(end, str.length()))
    str.substring(s, e)
}
println "safeSubstring(0, 100): '${safeSubstring(text, 0, 100)}'"
println "safeSubstring(-5, 3): '${safeSubstring(text, -5, 3)}'"

// SAFE: Null-safe with ?. operator
String nullStr = null
println "null?.take(5): '${nullStr?.take(5)}'"

Output

substring(0, 100): StringIndexOutOfBoundsException
text[0..100]: StringIndexOutOfBoundsException
take(100): 'Groovy'
drop(100): ''
safeSubstring(0, 100): 'Groovy'
safeSubstring(-5, 3): 'Gro'
null?.take(5): 'null'

The rule of thumb: if you’re not 100% sure about your input string’s length, use take() and drop() instead of substring() or the subscript operator. They’re designed to be forgiving.

Best Practice: Prefer take(n) over substring(0, n) and drop(n) over substring(n) when the input length is uncertain. Your code won’t crash at 2 AM because someone passed an empty string.

Real-World Substring Use Cases

Here are some additional patterns you’ll find useful in production Groovy code:

Real-World Substring Patterns

// 1. Strip prefix/suffix
def logLine = "[INFO] Server started on port 8080"
def message = logLine.drop(logLine.indexOf('] ') + 2)
println "Log message: ${message}"

// 2. Extract version number
def userAgent = "Mozilla/5.0 (compatible; Groovy/4.0.15)"
def version = userAgent[userAgent.indexOf('Groovy/') + 7 ..< userAgent.indexOf(')')]
println "Groovy version: ${version}"

// 3. CSV field extraction
def csv = "John,Doe,35,Engineer,New York"
def fields = csv.split(',')
println "Name: ${fields[0]} ${fields[1]}, Age: ${fields[2]}"

// 4. Sanitize input - keep only first 50 chars
def userInput = "This is a really long user input that could potentially be very very long and cause issues in the database"
def sanitized = userInput.take(50)
println "Sanitized: '${sanitized}'"

// 5. Extract domain from various URL formats
def urls = [
    "https://www.example.com/page",
    "http://blog.example.org",
    "ftp://files.example.net/data"
]
urls.each { u ->
    def d = u.substring(u.indexOf('://') + 3)
    if (d.contains('/')) d = d.substring(0, d.indexOf('/'))
    println "URL: ${u} → Domain: ${d}"
}

Output

Log message: Server started on port 8080
Groovy version: 4.0.15
Name: John Doe, Age: 35
Sanitized: 'This is a really long user input that could poten'
URL: https://www.example.com/page → Domain: www.example.com
URL: http://blog.example.org → Domain: blog.example.org
URL: ftp://files.example.net/data → Domain: files.example.net

These patterns cover probably 90% of the substring work you’ll do in real Groovy projects. Log parsing, version extraction, input sanitization, and URL decomposition are things developers deal with daily.

Performance Considerations

For most cases, substring performance is not something you need to worry about. All of these methods run in essentially O(n) time where n is the length of the result. But there are a few things worth knowing:

  • substring() creates a new String object in modern JVMs (Java 7u6+). It doesn’t share the original’s char array anymore.
  • take() and drop() have a tiny overhead from the GDK method dispatch, but it’s negligible for normal usage.
  • The subscript operator [] calls getAt() which adds one method call of overhead compared to direct substring().
  • For tight loops over millions of strings, substring() is marginally faster. For everything else, use whichever method reads best.
  • Avoid repeated substring operations on the same string in a loop – extract once and store the result.

Common Pitfalls

Pitfall 1: Inclusive vs Exclusive End Index

Inclusive vs Exclusive

def text = "Groovy"

// substring() end is EXCLUSIVE
println "substring(0, 3): '${text.substring(0, 3)}'"   // Gro

// Range with .. is INCLUSIVE
println "text[0..3]: '${text[0..3]}'"                    // Groo

// Range with ..< is EXCLUSIVE (matches substring behavior)
println "text[0..<3]: '${text[0..<3]}'"                  // Gro

Output

substring(0, 3): 'Gro'
text[0..3]: 'Groo'
text[0..<3]: 'Gro'

This trips up a lot of developers. substring(0, 3) returns 3 characters (indices 0, 1, 2). But text[0..3] returns 4 characters (indices 0, 1, 2, 3) because the range is inclusive. Use ..< if you want the range to behave like substring().

Pitfall 2: Empty String Edge Cases

Empty String Edge Cases

def empty = ""

// take() is safe with empty strings
println "empty.take(5): '${empty.take(5)}'"

// drop() is safe too
println "empty.drop(5): '${empty.drop(5)}'"

// But subscript operator fails
try {
    empty[0]
} catch (e) {
    println "empty[0]: ${e.class.simpleName}"
}

// And substring fails
try {
    empty.substring(0, 1)
} catch (e) {
    println "empty.substring(0,1): ${e.class.simpleName}"
}

// Always check for empty first, or use take/drop
def safeFirst = { str -> str ? str[0] : '' }
println "safeFirst(''): '${safeFirst('')}'"
println "safeFirst('Hello'): '${safeFirst('Hello')}'"

Output

empty.take(5): ''
empty.drop(5): ''
empty[0]: StringIndexOutOfBoundsException
empty.substring(0,1): StringIndexOutOfBoundsException
safeFirst(''): ''
safeFirst('Hello'): 'H'

Empty strings are the silent killer in substring operations. Always either validate your input or use the safe methods (take()/drop()) when the input might be empty. Groovy’s truthiness check (if (str)) covers both null and empty strings.

Pitfall 3: Off-by-One with indexOf() and Substring

A common mistake when combining indexOf() with substring():

Off-by-One Errors

def text = "name=John"

// WRONG: includes the '=' sign
def wrong = text.substring(text.indexOf('='))
println "Wrong: '${wrong}'"

// RIGHT: skip past the '=' sign
def right = text.substring(text.indexOf('=') + 1)
println "Right: '${right}'"

// ALWAYS check if indexOf returns -1
def noEquals = "nameJohn"
def idx = noEquals.indexOf('=')
if (idx >= 0) {
    println noEquals.substring(idx + 1)
} else {
    println "No '=' found in '${noEquals}'"
}

Output

Wrong: '=John'
Right: 'John'
No '=' found in 'nameJohn'

Always remember that indexOf() returns the position of the delimiter itself. If you want the text after the delimiter, you need + 1 (or + delimiter.length() for multi-character delimiters). And always check for -1 to handle the case where the delimiter doesn’t exist.

Conclusion

We covered a lot of ground on groovy substring operations in this post – from the basic substring() inherited from Java, through Groovy’s elegant subscript operator and range-based slicing, to the safe take() and drop() methods that won’t blow up on bad input.

The Groovy way is almost always cleaner than the Java way. Where Java forces you to calculate indices carefully and handle exceptions, Groovy gives you negative indices, inclusive ranges, and safe extraction methods. Once you get comfortable with the subscript operator and ranges, you’ll rarely reach for substring() again.

If you want to go deeper into individual methods, check out the dedicated posts on take() and reverse().

Summary

  • Use str[0..4] instead of str.substring(0, 5) – it’s more readable and Groovy-idiomatic
  • Negative indices (str[-3..-1]) let you grab from the end without knowing the length
  • take(n) and drop(n) are safe – they never throw on out-of-bounds
  • Remember: .. is inclusive, ..< is exclusive – don’t mix them up with substring()
  • Always check indexOf() for -1 before using its result in a substring call

If you also work with build tools, CI/CD pipelines, or cloud CLIs, check out Command Playground to practice 105+ CLI tools directly in your browser — no install needed.

Up next: Groovy String take() – Get First N Characters

Frequently Asked Questions

How do I get a substring in Groovy?

You can use several methods: substring(beginIndex, endIndex) inherited from Java, the subscript operator str[0..4] for range-based extraction, take(n) to get the first N characters safely, or drop(n) to skip the first N characters. The subscript operator with ranges is the most Groovy-idiomatic approach.

What is the difference between substring() and the subscript operator in Groovy?

The main difference is how the end index works. substring(0, 5) treats the end index as exclusive (returns 5 characters). The range operator str[0..4] treats the end as inclusive (also returns 5 characters). Use str[0..<5] for exclusive-end behavior matching substring(). Also, substring() throws on invalid indices while take()/drop() are safe alternatives.

How do I extract the last N characters of a string in Groovy?

Use negative indices with the subscript operator: str[-3..-1] gives you the last 3 characters. This is cleaner than the Java approach of str.substring(str.length() - 3). Negative index -1 always refers to the last character, -2 to the second-to-last, and so on.

What is the safest way to extract a substring in Groovy without exceptions?

Use the take(n) and drop(n) methods from the Groovy GDK. take(n) returns the first N characters and never throws – if n is larger than the string length, it returns the entire string. drop(n) skips the first N characters and also never throws. Combine them as drop(start).take(length) for safe mid-string extraction.

Can I use negative indices with Groovy strings?

Yes! Groovy supports negative indices for string access. str[-1] returns the last character, str[-2] returns the second-to-last, and str[-3..-1] returns the last 3 characters. You can also mix positive and negative indices in ranges, like str[2..-3] to get characters from index 2 to the third-from-last position.

Previous in Series: Groovy String To Integer – All Conversion Methods

Next in Series: Groovy String take() – Get First N Characters

Related Topics You Might Like:

This post is part of the Groovy & Grails Cookbook series on TechnoScripts.com

RahulAuthor posts

Avatar for Rahul

Rahul is a passionate IT professional who loves to sharing his knowledge with others and inspiring them to expand their technical knowledge. Rahul's current objective is to write informative and easy-to-understand articles to help people avoid day-to-day technical issues altogether. Follow Rahul's blog to stay informed on the latest trends in IT and gain insights into how to tackle complex technical issues. Whether you're a beginner or an expert in the field, Rahul's articles are sure to leave you feeling inspired and informed.

No comment

Leave a Reply

Your email address will not be published. Required fields are marked *