Fork me on GitHub

Go语言开发-字符串-字符串索引与切片

3.4字符串的索引和切片

正如表3.2所示,Go语言支持字符串切片语法,它是Python切片语法的一个子集。该语法可以用于任意类型的切片,我们将在第4章使用到它。

由于Go语言将字符串保存为UTF-8编码的字节,所以我们必须小心只在字符边界范围内进行切片。这在我们仅使用7位ASCII编码的情况下非常简单,因为一个字节表示一个字符,但是对于非ASCII编码的文本将更具挑战性,因为这些字符可能由一个或者多个字节表示。通常我们可以简单的使用for…range循环逐字符的遍历字符串,而不是将其切片,但是在某些情况下,我们确实需要使用切片来获得一个子字符串。一种能够确保切片索引在切片边界范围内的方法是使用Go语言strings包中的函数,如strings.Index() 或strings.LastIndex()。strings包中的函数请参见表3.6和表3.7。

我们将考虑使用不同的方式来理解字符串。索引位置,即字符串的UTF-8编码的字节位置,从0开始,最大为该字符串的长度减1。我们也可以使用len(s)-n这样的索引形式来从字符串切片的末尾往前索引,其中n为从末尾往前的字节的个数。例如,给定一个赋值语句s:=”naïve”,如图3.1展示了字符串s作为Unicode字符、码点、字节时的一些有效的索引位置和一对切片。

sdkjhfeu

图3.1 字符串剖析

图3.1中的每个索引位置都可以使用[]索引操作符来返回其对应的ASCII字符(以字节的形式),例如,s[0] == ‘n’ 和s[len(s)-1]==’e’。ï 字符的起始索引位置是2,但是如果我们使用s[2],我们只能简单的得到编码的ï (0xC3)字符的第一个UTF-8字节,这并不是我们想要的。

对于仅包含7位ASCII字符的字符串,我们可以使用语法s[0]来获取第一个字符(以字节的形式),同时可以使用s[len(s) -1]来获取最后一个字符。然而,在一般情况下我们应该使用utf8.DecodeRuneInString()来获取第一个字符(该函数返回一个rune和要表示的UTF-8字节的个数),使用utf8.DecodeLastRuneInString()来获取最后一个字符(参见表3.10)。

如果我们确实需要索引单个字符,有多个方法可供我们选择。对于只包含7位ASCII字符的字符串,我们可以简单地使用[]索引操作符,其具有非常快(O(1))的查找速度。对于包含非AsCll字符的字符串,我们可以将其转换成[]rune并使用[]索引操作符进行处理。同样其也具有非常快(O(1))的查找性能,但是其代价是一次转换同时消耗CPU和内存资源(O(n))。

在我们的例子中,如果我们使用chars := []rune(s)写法,chars变量将被创建为一个具有5个码点的rune(即int32)切片,而图3.1中使用的是6个字节。我们可以使用string(char)语法轻松地将任何rune(码点)类型转换成一个包含一个字符的字符串。

对于任意字符串(即那些可能含有非ASCII字符的),通过索引来获取其字符通常不是正确的做法。更好的做法是使用字符串切片,它可以很方便地返回一个字符串而不是一个字节。要安全地切片任意字符串,最好找到正确的索引位置或使用strings包中的函数,参见表3.6和表3.7。

下面的等式适用于字符串切片,实际上,适用于任意类型的切片:

现在让我们看一个实际的切片例子,第一种方法比较简单。假设我们有一行文本且想要从该文本中截取第一个和最后一个单词。下面是一种简单的写法:

firstWord(字符串类型)被赋值为字符串line从索引位置0(第一个字节)到索引位置i-1(第一个空格前的最后一个字节)之间的字节,因为字符串切片返回从开始到其结束位置但不包含该结束位置的字符串。类似地,lastWord被赋值为字符串line从索引位置j+1(空格后面的字节)到结尾处(索引位置为len(line)-1)的字节。

虽然本示例所用的方法适用于空格及其它7位ASCII字符,但是却不适用于任何Unicode空白字符如U+2028(行分隔符)或者+2029(段落分隔符)。

下面演示了如何获取字符串中的第一个和最后一个单词而无需关心用于分隔单词的空白字符。

图3.2给出了line字符串字符、码点及字节的表示方法。该图也给出了其字节索引位置及上面代码片段中使用到的切片。

strings.IndexFunc()函数返回作为第一个参数传入的字符串在作为第二个参数传入的函数(其签名为func(rune)bool)返回值为true时的第一个索引位置。strings.LastIndexFunc()函数与strings.IndexFunc()类似,只是它从字符串结尾处开始并返回最后一个索引位置。这里我们传入了unicode包中的IsSpace()函数作为第二个参数;该函数接受一个Unicode码点(rune类型)作为其唯一的参数并在当该码点是一个空白字符时返回true。(参见表3.11)一个函数的名称是对该函数的引用,所以在需要函数参数的任何地方都可以被传递,只要该函数的签名与指定的参数相匹配。

sajhaskdhakhd

图3.2带有空格的字符串刨析

使用strings.IndexFunc()函数来找到第一个空白符并在空白符处但不包括该空白符切片字符串可以很容易地得到第一个单词。但在查找最后一个空白符的时候,我们必须要加倍小心,因为有些空白符被编码为多个UTF-8字节。我们可以通过使用utf8.DecodeRuneInString()函数来解决这个问题,该函数可以提供给我们字符串切片中开始的第一个字符到最后一个空白符之间的字节所占用的字节数。然后我们将这个数字和最后一个空白符的索引位置相加来跳过最后一个空白符以便我们仅将最后一个单词切片出来,而无论其使用多少字节来表示。


目录


作者:Johnson
原创文章,版权所有,转载请保留原文链接。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注