一文详解Golang中字符串的常见错误

 更新时间:2023年10月24日 10:45:26   作者:洛天枫  
这篇文章主要来和大家深入讨论一下Golang 中的字符串,并查看一些不同的场景,以避免常见错误,对大家掌握golang有一定的帮助,需要的可以了解下

1. 字符串是否可以为 nil

我们已经对 Golang 中的字符串有了基本的了解,但我们可以从 Golang 字符串不能为 nil 开始,除非您使用指向字符串的指针。

如下代码所示,当我们创建一个字符串变量时,默认值必须是空的""。如果我们用 nil 值初始化字符串变量,我们将面临在变量声明中不能使用 nil 作为字符串值的错误。例如:

func main() {
	var s string
	s = nil // Cannot use 'nil' as the type string
	fmt.Println(s)
}

编译器会提示我们不能使用 nil 赋予 string 类型。因此,我们可以只是定义变量,或者使用""作为默认值:

func main() {
	var s string
	var ss = ""
	fmt.Println(s, ss)
}

如果我们坚持在字符串类型变量中使用 nil 值,则应使用指针,如下所示:

func main() {
	var s *string
	fmt.Println(s)
}

这个时候输出则为:

<nil>

但是,我们必须谨慎使用这种方法。每次要为变量赋值时,我们都必须编写更多的代码,而且在赋新值之前还要检查是否有零值或前一个值。

func main() {
	var s *string
	tmp := "hello"
	s = &tmp
	fmt.Printf("address: %+v, value: %s", s, *s)
}

这个时候打印出来 s 的地址以及所指向的值:

address: 0xc00008a030, value: hello

2. 字符串是不可变的

Golang 中的字符串是不可变的,这意味着我们不能更改每个字符的值。例如:

func main() {
	tmp := "hello"
	tmp[0] = 'J'
	fmt.Println(tmp)
}

上述代码会导致编译时错误,因为无法赋值给 tmp[0]

更改字符串中单个字符的常见错误如下:

func main() {
	tmp := "hello"
	tbs := []byte(tmp)
	tbs[0] = 'J'
	fmt.Println(string(tbs))

	chi := "你好"
	chiTBS := []byte(chi)
	chiTBS[0] = 'J'
	fmt.Println(string(chiTBS))
}

输出为:

Jello
J��好

虽然第一个输出显示的结果符合我们的预期,但这并不是更改某个字符的正确方法。

这是因为我们打算修改的单个部分可能存储在多个字节中,即使你想将变量转换为符文类型并更改你想要的部分,我也不得不说,这是不可能做到的,因为它可能被放置在多个符文中,我们需要谨慎行事!

3. 字符串是字节数组

在 Golang 中,字符串由字节(字节的片段)组成,某些字符需要存储在多个字节中,例如:"♥"。

因此,当需要确定一个字符串类型变量的长度时,我们必须谨慎编码。例如

func main() {
	tmp := "¥"
	fmt.Println("bytes: ", len(tmp))
	fmt.Println("runes: ", utf8.RuneCountInString(tmp))
}

len 函数返回的是字符串的字节数,而不是字符数。当我们需要找出字符串的符文数时,可以使用 uft8.RuneCountIntString() 函数。

另一个常见的误解是使用 uft8.RuneCountIntString() 来确定字符数,但这并不是在任何情况下都正确,因为一个字符串变量可能跨越多个符文。请看这个例子:

func main() {
	tmp := "❤️"
	fmt.Println("bytes: ", len(tmp))
	fmt.Println("runes: ", utf8.RuneCountInString(tmp))
}

输出为:

bytes:  6
runes:  2

4. 字符串索引和forrange

在 Golang 中,使用索引检索字符串的单个部分将为我们提供字符的 uint 值,并且只能检索第一个字节。但在字符串变量的 for 循环中,我们可以访问每个字符的符值:

func main() {
	tmp := "❤¥%……&*"
	fmt.Printf("char at 0 index, has type %T and value is %+v\n", tmp[0], tmp[0])

	for _, t := range tmp {
		fmt.Printf("value is %+v type is %T\n", t, t)
	}
}

输出:

char at 0 index, has type uint8 and value is 226
value is 10084 type is int32
value is 65509 type is int32
value is 37 type is int32
value is 8230 type is int32
value is 8230 type is int32
value is 38 type is int32
value is 42 type is int32

在对字符串进行迭代时,还要注意变量中可能存在的非 UTF8 字符,如果 Golang 无法将其理解为 UTF8,则会使用 unicode 替换而非实际值。

5. 字符串平等

在 Golang 中,我们总是可以使用 == 来检查简单的字符串是否相等,但如果我们的变量存在隐藏点,则应在比较两个字符串变量之前使用 unicode 规范包将其规范化:

func main() {
	cafe1 := "Café"
	cafe2 := "Cafe\u0301"

	normalizeCafe1 := norm.NFC.String(cafe1)
	normalizeCafe2 := norm.NFC.String(cafe2)
	fmt.Println(cafe1 == cafe2)
	fmt.Println(normalizeCafe1 == normalizeCafe2)
}

6. 高效字符串构建

使用“+”连接大量字符串的效率可能非常低。使用 strings.Builder 是高效构建字符串的最佳方法之一:

func main() {
	sb := strings.Builder{}
	for i := 0; i < 1000; i++ {
		sb.WriteString("hello ")
	}
	result := sb.String()
	fmt.Println(result)
}

与传统的 + 连接方法相比,这种方法速度更快,内存消耗更少,而且可以避免创建不必要的中间字符串。我们还可以使用 bytes.Buffer 软件包来实现这一目标。

总结

  • 字符串的默认值是""
  • lenRuneCountIntString 函数具有不同的行为
  • 我们应该小心 for 循环和字符串
  • 字符串相等是我们需要更精确的地方

到此这篇关于一文详解Golang中字符串的常见错误的文章就介绍到这了,更多相关go字符串内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go语言二维数组的传参方式

    Go语言二维数组的传参方式

    这篇文章主要介绍了Go语言二维数组的传参方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • 深入浅出go依赖注入工具Wire的使用

    深入浅出go依赖注入工具Wire的使用

    但随着项目规模的增长,组件之间的依赖关系变得复杂,手动管理可能会很繁琐,所以本文将深入探讨一个备受欢迎的 Go 语言依赖注入工具—— Wire,感兴趣的可以了解下
    2023-09-09
  • 聊聊golang中多个defer的执行顺序

    聊聊golang中多个defer的执行顺序

    这篇文章主要介绍了golang中多个defer的执行顺序,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-05-05
  • Golang中for循环的用法示例详解

    Golang中for循环的用法示例详解

    for循环就是让一段代码循环的执行,接下来通过本文给大家讲解Golang中for循环的用法,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-12-12
  • 浅谈Go语言中的次方用法

    浅谈Go语言中的次方用法

    这篇文章主要介绍了浅谈Go语言中的次方用法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Go语言常见设计模式之装饰模式详解

    Go语言常见设计模式之装饰模式详解

    在 Go 语言中,虽然装饰模式没有像 Python 中应用那么广泛,但也有其用武之地,这篇文章我们就来一起看下装饰模式在 Go 语言中的应用吧
    2023-07-07
  • 线上golang grpc服务资源泄露问题排查

    线上golang grpc服务资源泄露问题排查

    这篇文章主要介绍了线上golang grpc服务资源泄露问题排查,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Golang设计模式之适配器模式介绍和代码示例

    Golang设计模式之适配器模式介绍和代码示例

    适配器是一种结构型设计模式, 它能使不兼容的对象能够相互合作,可担任两个对象间的封装器, 它会接收对于一个对象的调用, 并将其转换为另一个对象可识别的格式和接口,本文将通过代码示例详细给大家介绍Golang的适配器模式
    2023-06-06
  • Go语言基础入门应用简介及常用命令

    Go语言基础入门应用简介及常用命令

    这篇文章主要为大家介绍了Go语言基础入门应用简介及常用命令,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2021-11-11
  • golang使用mapstructure解析json

    golang使用mapstructure解析json

    mapstructure 是一个 Go 库,用于将通用映射值解码为结构,这篇文章主要来和大家介绍一下golang如何使用mapstructure解析json,需要的可以参考下
    2023-12-12

最新评论