如何删除字符串的第一个字符?

29

如何移除字符串的第一个字符是什么建议方法?

我查看了字符串方法的文档,但没有发现类似JavaScript中的String.slice()的任何方法适用。


3
每个问题都没有“最佳”解决方案。str[1:] 就可以解决问题。 - zerkms
请查看此处 https://www.dotnetperls.com/substring-go - Victor
3
如果您的字符串是ASCII编码,@zerkms的方法可以解决问题,而"µñ"[1:]不能完全按预期工作。 - mu is too short
@muistooshort 确实。而且在使用zjw复合字形时,这甚至变得更加困难(几乎是不可能的?) - zerkms
6个回答

24

假设问题中使用“字符”来指代Go语言中的rune,那么可以使用utf8.DecodeRuneInString获取第一个符文的大小,然后进行切片:

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

游乐场示例

正如peterSO在他的评论中提到的链接游乐场示例所示,字符串范围也可以用于找到第一个符文结束的位置:

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 ""
}

1
虽然 trimFirstRune 是正确的,但它似乎比 trimLeftChartrimLeftChars 效率低:https://play.golang.org/p/ZOZyRORkK82。请参阅 utf8.DecodeRuneInString 的源代码。 - peterSO
好问题。如果要修剪的字符已知,与其使用符文数量,strings TrimLeft() 对我来说效果最佳。 - Grant Bowman
因为这需要你将整个CSV作为字符串读入内存,所以会被downvote...更加高效的方法是将读取器一级一级地连接起来,并在内联中进行过滤。想象一下,如果你从网络上检索一个1GB的CSV文件,并且必须等待整个1GB的数据都加载到内存中才能开始处理,那会是多么低效? - Mike Graf
@MikeGraf 这个问题询问如何从一个字符串中删除第一个字符,这就是我回答的内容。如果要从通过网络检索的大型CSV文件中删除第一个字符,请阅读UTFMax字节,使用上面的代码删除第一个字符,并使用剩余的字节和原始io.Reader创建一个multireader - Charlie Tumahai

19

这个方法对我有效:

package main

import "fmt"

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

输出结果为:

bcd

在 Go Playground 上的代码: https://play.golang.org/p/iTv7RpML3LO


5
但是一般情况下它不起作用,考虑当 input := "µabcd" 时会发生什么。 - mu is too short
7
@muistooshort说得很好,我没有测试像µ这样的Unicode字符。下次我会记住的。 - Vikram Hosakote
这回答了所提出的问题,并且在我的情况下,所有字符都是ASCII码(由我控制而非用户)。感谢您的贡献。 - harperville

17

在Go语言中,字符 string 是以UTF-8编码的Unicode代码点表示的。UTF-8是一种可变长度的编码方式。

Go语言规范

For语句

带有range子句的For语句:

对于字符串值,"range"子句从字节索引0开始迭代字符串中的Unicode代码点。在后续迭代中,索引值将是字符串中连续的UTF-8编码代码点的第一个字节的索引值,而第二个值,类型为rune,将是相应代码点的值。如果迭代遇到无效的UTF-8序列,则第二个值将是0xFFFD(Unicode替换字符),并且下一次迭代将在字符串中前进一个字节。

例如,

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("世界"))
}

游乐场:https://play.golang.org/p/t93M8keTQP_I

输出:

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

或者,对于一个更一般的函数:

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))
}

游乐场: https://play.golang.org/p/ECAHl2FqdhR

输出:

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

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

参考文献:

Go编程语言规范

Unicode UTF-8 常见问题解答

Unicode 联盟


3
鉴于原作者没有解释他们所说的“character”是什么意思,如果他们实际上指的是“字形”,那么像‍⚕️这样的符号会很难处理。请注意,我不是吹毛求疵,我知道这是一个复杂的问题,不确定是否能用少于几十/几百千字节的代码来解决它。 - zerkms
@zerkms: 男性医护人员:https://play.golang.org/p/qgloqE1a4az - peterSO
这就是我的意思:它是一个单一的字形,所以 trimLeftChars(s, 1) 应该完全将其删除。(如果您使用 Linux,请尝试使用最新操作系统的手机或其他操作系统(Windows/Mac)查看是否正确呈现-这些4个代码点将合并为单个字形) - zerkms
1
@zerkms:我小心地使用了“字符(代码点)”这个词,而不是“字形”。我只承诺了“trimLeftChars”,而不是“trimLeftGlyphs”。 - peterSO
1
一个代码点不一定是一个字符。考虑组合字符、RLM或丑陋/愚蠢的BOM。 - Volker
@Volker:这一切都在Unicode标准中有详细解释:http://www.unicode.org/versions/Unicode10.0.0/UnicodeStandard-10.0.pdf - peterSO

3
另一个选择是使用utf8string包:
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


2

这是我找到的最佳的一行解决方案,使用了strings包,适用于这个使用场合。

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"))
}

上面的输出:
abc/def
Hello World
Hello World

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


非常好的东西,简单明了,正是我想要的。 - GoonerKP

1

您可以将字符串转换为字符数组,弹出第一个字符并将数组转换回字符串。这是一行代码:

str = string([]rune(str)[1:])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接