Start learning free

LESSON 58 OF 414 · STRINGS I

String Slicing in Scala: substring and slice

Lesson Read the full explanation

Not only we can select a single character from a string, but we can also select a sequence of consecutive characters (e.g. substring) from a string.

To achieve this, we use the substring() method like this:

string.substring(startIndex, endIndex)

This is called slicing a string. It returns the substring from startIndex to endIndex.

Here is an example:

var language = "Scala"
var substring = language.substring(1, 4)

println(substring) // Output: cal
println(language) // Output: Scala

The startIndex is a zero-based index at which we start extraction and the endIndex is also zero-based index before which we end the extraction.

The substring will not include the character at endIndex. You can see in our example that the character at index 1 (c) was included while the character at index 4 (a) was excluded.

Also notice that language preserved its value. The slicing does not affect the original string. It just creates a brand new one representing the sliced substring.


If you omit the endIndex, substring() extracts to the end of the string:

var language = "Scala"
var substring = language.substring(2)

println(substring) // Output: ala

Assignment
Follow the Coding Tutorial and let's slice some strings!


Hint
Look at the examples above if you get stuck.


STEP 1 OF 3
    Ctrl + Enter to check

    SCALA · STRINGS I

    String Slicing in Scala, explained

    Cutting a piece out of a string

    Besides one character, you can take a run of consecutive characters, called a substring. Scala strings are Java strings, so substring is available:

    string.substring(startIndex, endIndex)
    

    startIndex is the zero-based index of the first character you keep. endIndex is the index where the cut stops, and that character is not included. So the result has endIndex - startIndex characters.

    val language = "Scala"
    val part = language.substring(1, 4)
    
    println(part)     // cal
    println(language) // Scala
    
    cal
    Scala
    

    Indexes 1, 2 and 3 are kept (c, a, l), and index 4 is excluded. The original string is untouched: slicing builds a new string.

    Leaving out the end

    With one argument, substring runs to the end of the string:

    val language = "Scala"
    println(language.substring(3)) // la
    

    The exercise, step by step

    The lesson gives you this string. Counting indexes helps: Coding is indexes 0 to 5, a space is at 6, feels is 7 to 11, a space is at 12, and great is 13 to 17.

    val message = "Coding feels great"
    
    1. firstWord: the first 6 characters. They are indexes 0 to 5. The end index is excluded, so the call is substring(0, 6).
    2. middleWord: the 8th to 12th characters. Counting from 1, those are indexes 7 to 11. The end is excluded, so the call is substring(7, 12).
    3. lastWord: the last 5 characters. The string has 18 characters, so they start at index 13. Run to the end with substring(13).
    val message = "Coding feels great"
    
    val firstWord = message.substring(0, 6)
    val middleWord = message.substring(7, 12)
    val lastWord = message.substring(13)
    

    These values are Coding, feels and great.

    Pitfalls

    • Bad bounds throw. "abc".substring(0, 10) throws a StringIndexOutOfBoundsException with the message Range [0, 10) out of bounds for length 3. The same happens when the start is larger than the end: "abc".substring(2, 1) fails with Range [2, 1) out of bounds for length 3.
    • The edge is allowed. "abc".substring(3) returns the empty string, because a start equal to the length is valid. substring(4) throws.
    • Length of the result. It is end minus start. A slice of 5 characters starting at 7 ends at 12, not 11.

    A forgiving alternative: slice

    Scala's own slice method takes the same two arguments but clamps them instead of throwing:

    println("abc".slice(1, 10)) // bc
    println("abc".slice(2, 1))  // prints an empty line
    println("abc".take(10))     // abc
    println("abc".drop(1))      // bc
    

    take(n) keeps the first n characters and drop(n) removes them. Use substring when an out-of-range index would be a bug you want to hear about, and slice, take or drop when short strings are normal.

    Slicing with indexOf

    Hard-coded numbers only work for one string. Combining substring with indexOf finds the cut point for you:

    val sentence = "Hello World"
    val space = sentence.indexOf(" ")
    
    println(sentence.substring(0, space))  // Hello
    println(sentence.substring(space + 1)) // World
    

    The first slice stops at the space, which is excluded. The second starts one past the space. The same pattern works for the last n characters: s.substring(s.length - n). To drop the final character, use s.substring(0, s.length - 1), or s.dropRight(1).

    Reading a slice in your head

    A reliable habit is to write the index under each character before choosing the numbers. For "Coding feels great" the word feels starts at the f (index 7) and the next character after it is the space at index 12. Start at the first character you want; end at the first character you don't. This "half-open" convention also explains why s.substring(0, k) always has exactly k characters, and why s.substring(0, k) followed by s.substring(k) rebuilds the whole string.

    Key takeaways

    • s.substring(a, b) keeps indexes a up to b - 1.
    • s.substring(a) keeps everything from a to the end.
    • The original string never changes.
    • Out-of-range bounds throw with substring but are clamped by slice.
    Keep going with ScalaAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free