29

What is the suggested method to remove the first character of a string?

I've looked through the documentation for string methods but I don't see anything that works like javascript's String.slice().

Grant Bowman
  • 307
  • 6
  • 16
Quentin Gibson
  • 442
  • 1
  • 5
  • 15

6 Answers6

24

Assuming that the question uses "character" to refer to what Go calls a rune, then use utf8.DecodeRuneInString to get the size of the first rune and then slice:

func trimFirstRune(s string) string {
    _, i := utf8.DecodeRuneInString(s)
    return s[i:]
}

playground example

As peterSO demonstrates in the playground example linked from his comment, range on a string can also be used to find where the first rune ends:

func trimFirstRune(s string) string {
    for i := range s {
        if i > 0 {
            // The value i is the index in s of the second 
            // rune.  Slice to remove the first rune.
            return s[i:]
        }
    }
    // There are 0 or 1 runes in the string. 
    return ""
}
Charlie Tumahai
  • 113,709
  • 12
  • 249
  • 242
  • 1
    While `trimFirstRune` is correct, it appears to be less efficient than `trimLeftChar` and `trimLeftChars`: https://play.golang.org/p/ZOZyRORkK82. See the source code for `utf8.DecodeRuneInString`. – peterSO Feb 15 '18 at 09:28
  • Good question. Rather than number of runes, if the characters to trim are known [strings](https://golang.org/pkg/strings/) TrimLeft() worked best for me. – Grant Bowman May 10 '19 at 21:59
  • Downvoting because this requires you to read the entire CSV into memory as a string ... It's much much more efficient to pipe readers one to another and do filtering inline. Imagine you're retrieving a 1GB CSV across the network and you have to wait for the entire 1GB to be in ram before starting to process? – Mike Graf Jun 16 '20 at 16:02
  • @MikeGraf The question asks how to trim the first character from a `string` and that's what I answered. To trim the first character from a large CSV file retrieved across the network, read [UTFMax](https://godoc.org/unicode/utf8#UTFMax) bytes, trim the first character using the code above, and create a [multireader](https://godoc.org/io#MultiReader) with the remaining bytes and the original io.Reader. – Charlie Tumahai Jun 16 '20 at 21:59
19

This works for me:

package main

import "fmt"

func main() {
    input := "abcd"
    fmt.Println(input[1:])    
}

Output is:

bcd

Code on Go Playground: https://play.golang.org/p/iTv7RpML3LO

Vikram Hosakote
  • 3,528
  • 12
  • 23
17

In Go, character strings are UTF-8 encoded Unicode code points. UTF-8 is a variable-length encoding.

The Go Programming Language Specification

For statements

For statements with range clause

For a string value, the "range" clause iterates over the Unicode code points in the string starting at byte index 0. On successive iterations, the index value will be the index of the first byte of successive UTF-8-encoded code points in the string, and the second value, of type rune, will be the value of the corresponding code point. If the iteration encounters an invalid UTF-8 sequence, the second value will be 0xFFFD, the Unicode replacement character, and the next iteration will advance a single byte in the string.

For example,

package main

import "fmt"

func trimLeftChar(s string) string {
    for i := range s {
        if i > 0 {
            return s[i:]
        }
    }
    return s[:0]
}

func main() {
    fmt.Printf("%q\n", "Hello, 世界")
    fmt.Printf("%q\n", trimLeftChar(""))
    fmt.Printf("%q\n", trimLeftChar("H"))
    fmt.Printf("%q\n", trimLeftChar("世"))
    fmt.Printf("%q\n", trimLeftChar("Hello"))
    fmt.Printf("%q\n", trimLeftChar("世界"))
}

Playground: https://play.golang.org/p/t93M8keTQP_I

Output:

"Hello, 世界"
""
""
""
"ello"
"界"

Or, for a more general function,

package main

import "fmt"

func trimLeftChars(s string, n int) string {
    m := 0
    for i := range s {
        if m >= n {
            return s[i:]
        }
        m++
    }
    return s[:0]
}

func main() {
    fmt.Printf("%q\n", trimLeftChars("", 1))
    fmt.Printf("%q\n", trimLeftChars("H", 1))
    fmt.Printf("%q\n", trimLeftChars("世", 1))
    fmt.Printf("%q\n", trimLeftChars("Hello", 1))
    fmt.Printf("%q\n", trimLeftChars("世界", 1))
    fmt.Println()
    fmt.Printf("%q\n", "Hello, 世界")
    fmt.Printf("%q\n", trimLeftChars("Hello, 世界", 0))
    fmt.Printf("%q\n", trimLeftChars("Hello, 世界", 1))
    fmt.Printf("%q\n", trimLeftChars("Hello, 世界", 7))
    fmt.Printf("%q\n", trimLeftChars("Hello, 世界", 8))
    fmt.Printf("%q\n", trimLeftChars("Hello, 世界", 9))
    fmt.Printf("%q\n", trimLeftChars("Hello, 世界", 10))
}

Playground: https://play.golang.org/p/ECAHl2FqdhR

Output:

""
""
""
"ello"
"界"

"Hello, 世界"
"Hello, 世界"
"ello, 世界"
"世界"
"界"
""
""

References:

The Go Programming Language Specification

Unicode UTF-8 FAQ

The Unicode Consortium

peterSO
  • 158,998
  • 31
  • 281
  • 276
  • 3
    (disclaimer: I'm not nitpicking (and I've +1d you), I understand it's a complicated problem, not sure it's possible to solve it with less than few dozens/hundreds of kilobytes of code) Given the OP did not explain what they mean by `character` it they actually meant "a glyph" then something like `‍⚕️` would be really hard to handle right. – zerkms Feb 15 '18 at 02:41
  • @zerkms: ‍⚕️ Man Health Worker: https://play.golang.org/p/qgloqE1a4az – peterSO Feb 15 '18 at 03:27
  • That's what I meant: it's a single glyph, so `trimLeftChars(s, 1)` should entirely remove it. (if you're on linux - try mobile phone with a recent OS or other OS (windows/mac) to see that if properly rendered - those 4 codepoints are joined into a single glyph) – zerkms Feb 15 '18 at 03:33
  • 1
    @zerkms: I was careful to use the word character (code point), not glyph. I only promised `trimLeftChars`, not `trimLeftGlyphs`. – peterSO Feb 15 '18 at 03:47
  • 1
    A code point need not be a character. Think of combining characters, RLM or the dead ugly/stupid BOM. – Volker Feb 15 '18 at 05:07
  • @Volker: This is all explained in the Unicode Standard: http://www.unicode.org/versions/Unicode10.0.0/UnicodeStandard-10.0.pdf – peterSO Feb 15 '18 at 05:12
2

Another option is the utf8string package:

package main
import "golang.org/x/exp/utf8string"

func main() {
   s := utf8string.NewString("")
   t := s.Slice(1, s.RuneCount())
   println(t == "")
}

https://pkg.go.dev/golang.org/x/exp/utf8string

Zombo
  • 1
  • 62
  • 391
  • 407
2

This is the best oneLiner solution I had come across for this useCase using strings package

package main

import (
    "fmt"
    "strings"
)

func main() {
    myString1 := "/abc/def"
    myString2 := "Hello World"
    myString3 := "HHello World"

    fmt.Println(strings.TrimPrefix(myString1, "/"))
    fmt.Println(strings.TrimPrefix(myString2, "/"))
    fmt.Println(strings.TrimPrefix(myString3, "H"))
}

Output for the above:

abc/def
Hello World
Hello World

Go Playground link: https://go.dev/play/p/tt3GgDjHXFg?v=goprev

Abhishek
  • 69
  • 5
1

You can convert the string into an array of runes, pop the first rune and then convert the array back into a string. Here's the one-liner:

str = string([]rune(str)[1:])
Nadav
  • 1,055
  • 1
  • 10
  • 23