字符串值和字符串文字之间的区别

时间:2018-09-02 07:57:02

标签: go

根据此golang博客文章(https://blog.golang.org/strings)中关于字符串的内容,有一行指出“ ... 字符串值可以包含任意字节;如本例所示, 字符串文字只要不包含字节级转义符,就始终包含UTF-8文本。“

有什么区别?

2 个答案:

答案 0 :(得分:5)

这种区别取决于字符串 literal 和字符串 value Go language specification中的定义:

  

字符串文字:字符串文字表示string constant,该字符串是通过连接一系列字符而获得的。

     

字符串值:字符串值是一个字节序列(可能为空)。

以及以编码为UTF-8文本的源文件中表示源代码的要求:

  

源代码表示:源代码是采用UTF-8编码的Unicode文本。

最关键的是,请注意,字符串文字被定义为常量,因此其值完全在编译时定义。


将有效的Go源文件集视为符合语言规范的那些文件。根据定义,此类源文件必须使用有效的UTF-8进行编码。由于在编译时从源文件的内容中知道字符串文字的值,因此我们可以通过构造看到任何字符串文字必须包含有效的UTF-8文本。

如果我们忽略字节级转义序列,则可以进一步看到,字符串二进制文字在所得二进制文件中的编译输出也必须包含有效的UTF-8,因为它是从源文件中的UTF-8值编译而来的


字节顺序?概括

但是,字符串defined,是字节序列, 1 ,因此不要求它们本身包含有效的UTF-8。此类非UTF-8文本可能来自于程序在编译时定义的字符串文字以外的输入值,例如通过套接字或本地管道接收的数据,从文件中读取的数据或包含字节级转义的字符串文字序列。

1 如博客文章所述,在大多数情况下,此字节序列可被视为字节片([]byte)。但是,这并不是严格正确的,因为基础实现不使用切片。字符串是不可变的,不需要单独跟踪其容量。调用cap(str),其中str的类型为string is an error

字节级转义序列

字节级转义序列提供了一种使用UTF-8表示形式对非UTF-8值进行编码的机制。这样的序列允许以UTF-8格式表示任意字节,以生成有效的Go源文件并满足语言规范。

例如,字节序列b2 bd(此处的字节表示为以空格分隔的两位十六进制数字)不是有效的UTF-8。尝试使用UTF-8解码器解码此字节序列将产生错误。因此,尽管可以将这样的字节序列存储在Go的字符串中,但无法直接在Go源文件中表示。任何这样做的源文件都是无效的Go,而词法分析器将拒绝它(请参见下文)。

反斜杠转义序列提供了一种机制,用于将该字节字符串分解为满足工具链的有效UTF-8表示形式。解释后的Go字符串文字"\xb2\xbd"使用ASCII字符序列表示该字符串,可以使用UTF-8表示。编译后,将对该序列进行解析以在编译后的输出中生成一个字符串,其中包含字节序列b2 bd


示例

我将提供一个工作示例来更具体地说明这一点。由于我们将生成无效的源文件,因此我不能随便使用Go游乐场。我在机器上使用了go工具链。工具链为go version go1.11 darwin/amd64

考虑以下简单的Go程序,其中可以为字符串myStr指定值。为方便起见,我们用所需的字节替换空白(字符串20 20 20之后的字节序列my value:)。这样一来,我们以后拆卸二进制文件时,就可以轻松找到编译后的输出。

package main

import "fmt"

func main() {
    myStr := "my value:   "
    fmt.Println(myStr)
}

我使用了十六进制编辑器,将无效的UTF-8字节序列b2 bd插入myStr中空白的最后字节中。下面的表示形式;为简洁起见,省略了其他内容:

00000030: 203a 3d20 22b2 bd20 220a 0966 6d74 2e50   := ".. "..fmt.P

尝试构建此文件会导致错误:

$ go build ./test.go
# command-line-arguments
./test.go:6:12: invalid UTF-8 encoding

如果我们在编辑器中打开文件,它将以自己的方式解释字节(可能是特定于平台的),并在保存时发出有效的UTF-8。在此基于OS X的系统上,使用vim将该序列解释为Unicode U + 00B2 U + 00BD或²½。这可以满足编译器的要求,但是源文件和因此编译的二进制文件现在包含的字节序列与最初的字节序列不同。下面的代码转储显示序列c2 b2 c2 bd,源文件的十六进制转储也显示序列。 (请参阅第1行的最后两个字节和第2行的前两个字节。)

$ gobjdump -s -j __TEXT.__rodata test | grep -A1 "my value"                                                                                         
 10c4c50 63746f72 796d7920 76616c75 653ac2b2  ctorymy value:..
 10c4c60 c2bd206e 696c2065 6c656d20 74797065  .. nil elem type

要恢复原始字节序列,我们可以这样更改字符串定义:

myStr := "my value: \xb2\xbd"

转储由编辑器生成的源文件将产生有效的UTF-8序列5c 78 62 32 5c 78 62 64,ASCII字符\xb2\xbd的UTF-8编码:

00000030: 203a 3d20 226d 7920 7661 6c75 653a 205c   := "my value: \
00000040: 7862 325c 7862 6422 0a09 666d 742e 5072  xb2\xbd"..fmt.Pr

然而,通过构建此源文件生成的二进制文件演示了编译器已将该字符串文字转换为包含所需的b2 bd序列(第四列的最后两个字节):

10c48b0 6d792076 616c7565 3a20b2bd 6e6f7420  my value: ..not

答案 1 :(得分:-3)

通常,字符串值和文字之间没有区别。字符串文字只是初始化新字符串变量(按原样的字符串)的方法之一。