我的编程空间,编程开发者的网络收藏夹
学习永远不晚

解析Go 中的 rune 类型

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

解析Go 中的 rune 类型

刚接触 Go 语言时,就听说有一个叫rune的数据类型,即使查阅过一些资料,对它的理解依旧比较模糊,加之对陌生事物的天然排斥,在之后很长一段时间的编程工作中,我都没有让它出现在我的代码里。

逃避虽然有用,但是似乎有些可耻,想要成为一名成熟、优秀的 Go 语言开发工程师,必须要有直面陌生事物并且成功运用的勇气和能力,带着这样的觉悟,让我们一起走近rune,直视它!

了解一下,rune类型究竟是什么?

rune类型是 Go 语言的一种特殊数字类型。在builtin/builtin.go文件中,它的定义:type rune = int32;官方对它的解释是:rune是类型int32的别名,在所有方面都等价于它,用来区分字符值跟整数值。使用单引号定义 ,返回采用 UTF-8 编码的 Unicode 码点。Go 语言通过rune处理中文,支持国际化多语言。

众所周知,Go 语言有两种类型声明方式:一种叫类型定义声明,另一种叫类型别名声明。其中,别名的使用在大型项目重构中作用最为明显,它能解决代码升级或迁移过程中可能存在的类型兼容性问题。而runebyte是 Go 语言中仅有的两个类型别名,专门用来处理字符。当然,我们也可以通过type关键字加等号的方式声明更多的类型别名。

学习一下,rune类型怎么用?

我们知道,字符串由字符组成,字符的底层由字节组成,而一个字符串在底层的表示是一个字节序列。在 Go 语言中,字符可以被分成两种类型处理:对占 1 个字节的英文类字符,可以使用byte(或者unit8);对占 1 ~ 4 个字节的其他字符,可以使用rune(或者int32),如中文、特殊符号等。

下面,我们通过示例应用来具体感受一下。

统计带中文字符串长度

// 使用内置函数 len() 统计字符串长度
fmt.Println(len("Go语言编程"))  // 输出:14  

前面说到,字符串在底层的表示是一个字节序列。其中,英文字符占用 1 字节,中文字符占用 3 字节,所以得到的长度 14 显然是底层占用字节长度,而不是字符串长度,这时,便需要用到rune类型。

// 转换成 rune 数组后统计字符串长度
fmt.Println(len([]rune("Go语言编程")))  // 输出:6

这回对了。很容易,我们解锁了rune类型的第一个功能,即统计字符串长度。

  • 截取带中文字符串

如果想要截取字符串中 ”Go语言“ 这一段,考虑到底层是一个字节序列,或者说是一个数组,通常情况下,我们会这样:

s := "Go语言编程"
// 8=2*1+2*3
fmt.Println(s[0:8])  // 输出:Go语言

结果符合预期。但是,按照字节的方式进行截取,必须预先计算出需要截取字符串的字节数,如果字节数计算错误,就会显示乱码,比如这样:

s := "Go语言编程"
fmt.Println(s[0:7]) // 输出:Go语�

此外,如果截取的字符串较长,那通过字节的方式进行截取显然不是一个高效准确的办法。那有没有不用计算字节数,简单又不会出现乱码的方法呢?不妨试试这样:

s := "Go语言编程"
// 转成 rune 数组,需要几个字符,取几个字符
fmt.Println(string([]rune(s)[:4])) // 输出:Go语言    

到这里,我们解锁了rune类型的第二个功能,即截取字符串。

思考一下,为什么rune类型可以做到?

通过上面的示例,我们发现似乎在处理带中文的字符串时,都需要用到rune类型,这究竟是为什么呢?除了使用rune类型,还有其他方法吗?

在深入思考之前,我们需要首先弄清楚stringbyterune三者间的关系。

字符串在底层的表示是由单个字节组成的一个不可修改的字节序列,字节使用UTF-8[1]编码标识Unicode[2]文本。Unicode 文本意味着.go文件内可以包含世界上的任意语言或字符,该文件在任意系统上打开都不会乱码。UTF-8 是 Unicode 的一种实现方式,是一种针对 Unicode 可变长度的字符编码,它定义了字符串具体以何种方式存储在内存中。UFT-8 使用 1 ~ 4 为每个字符编码。

Go 语言把字符分byterune两种类型处理。byte是类型unit8的别名,用于存放占 1 字节的 ASCII 字符,如英文字符,返回的是字符原始字节。rune是类型int32的别名,用于存放多字节字符,如占 3 字节的中文字符,返回的是字符 Unicode 码点值。如下图所示:

s := "Go语言编程"
// byte
fmt.Println([]byte(s)) // 输出:[71 111 232 175 173 232 168 128 231 188 150 231 168 139]
// rune
fmt.Println([]rune(s)) // 输出:[71 111 35821 35328 32534 31243]

它们的对应关系如下图:

了解了这些,我们再回过来看看,刚才的问题是不是清楚明白很多?接下来,让我们再来看看源码中是如何处理的,以utf8.RuneCountInString()[3]函数为例。

示例:

// 统计字符串长度
fmt.Println(utf8.RuneCountInString("Go语言编程")) // 输出:6

源码:

// RuneCountInString is like RuneCount but its input is a string.
func RuneCountInString(s string) (n int) {
 // 调用 len() 函数得到字节数
 ns := len(s)
 for i := 0; i < ns; n++ {
  c := s[i]
  // 如码点值小于 128,则为占 1 字节的 ASCII 字符(或者说英文字符),长度 + 1
  if c < RuneSelf { // RuneSelf = 128
   // ASCII fast path
   i++
   continue
  }
  // 查询首字节信息表,得到中文占 3 字节,所以这里的 x = 3
  x := first[c]
  // 判断 x = 3,xx = 241(0xF1)
  if x == xx {
   i++ // invalid.
   continue
  }
  // 提取有效的 UTF-8 字节长度编码信息,size = 3
  size := int(x & 7)
  if i+size > ns {
   i++ // Short or invalid.
   continue
  }
  // 提取有效字节范围
  accept := acceptRanges[x>>4]
  // accept.lo,accept.hi,表示 UTF-8 中第二字节的有效范围
  // locb = 0b10000000,表示 UTF-8 编码非首字节的数值下限
  // hicb = 0b10111111,表示 UTF-8 编码非首字节的数值上限
  if c := s[i+1]; c < accept.lo || accept.hi < c {
   size = 1
  } else if size == 2 {
  } else if c := s[i+2]; c < locb || hicb < c {
   size = 1
  } else if size == 3 {
  } else if c := s[i+3]; c < locb || hicb < c {
   size = 1
  }
  i += size
 }
 return n
}

调用该函数时,传入一个原始的字符串,代码会根据每个字符的码点大小判断是否为 ASCII 字符,如果是,则算做 1 位;如果不是,则查询首字节表,明确字符占用的字节数,验证有效性后再进行计数。

小小总结

在我看来,rune类型只是一种名称叫法,表示用来处理长度大于 1 字节( 8 位)、不超过 4 字节( 32 位)的字符类型。但万变不离其宗,我们使用函数时,无论传入参数的是原始字符串还是rune,最终都是对字节进行处理。看似陌生的事物,沉下心了解到其本质以后,才发现原来并不陌生,缺少的只是正视它的勇气!

[1]

UTF-8:https://zh.wikipedia.org/wiki/UTF-8

[2]

Unicode:https://zh.wikipedia.org/wiki/Unicode

[3]

utf8.RuneCountInString():https://golang.org/class="lazy" data-src/unicode/utf8/utf8.go

到此这篇关于Go 中的 rune 类型的文章就介绍到这了,更多相关Go  rune 类型内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

解析Go 中的 rune 类型

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

go语言字符类型byte与rune源码分析

这篇文章主要介绍“go语言字符类型byte与rune源码分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“go语言字符类型byte与rune源码分析”文章能帮助大家解决问题。一、byte类型介绍by
2023-07-05

go语言字符类型byte与rune案例详解

这篇文章主要介绍了go语言字符类型byte与rune案例详解,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
2023-03-01

golang中rune类型怎么使用

本篇内容介绍了“golang中rune类型怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!golang中rune类型在golang中r
2023-06-21

Go 语言的静态类型解析

go 语言通过在编译时检查类型一致性来进行静态类型解析,有助于在运行时防止错误。具体实现包括:类型定义:使用 type 关键字,例如定义 person 结构体类型。变量声明:使用 var 关键字,例如声明 p 为 person 类型变量。类
Go 语言的静态类型解析
2024-04-08

Go语言中的复合数据类型解析

go语言复合数据类型包括:数组:固定大小元素集合,索引访问。切片:动态大小数据结构,可增长/缩小。映射:无序键值对集合。结构体:组织相关数据的复合类型。Go 语言中的复合数据类型详解Go 语言中提供了多种复合数据类型,用于存储和组织复杂数
Go语言中的复合数据类型解析
2024-04-03

Go语言中rune方法使用详解

本文主要介绍了Go语言中rune方法使用详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-03-19

如何理解Go中的类型别名

这篇文章主要介绍“如何理解Go中的类型别名”,在日常操作中,相信很多人在如何理解Go中的类型别名问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何理解Go中的类型别名”的疑惑有所帮助!接下来,请跟着小编一起来
2023-06-15

解析Go语言泛型对不同数据类型的适配性

go 语言泛型允许开发者使用类型参数定义通用函数和类型,从而提高代码适应不同数据类型的能力。具体来说,它提供了以下好处:类型安全:类型约束确保泛型代码中的类型兼容。代码可重用性:泛型函数和类型可以针对不同类型数据重用,减少代码重复。可维护性
解析Go语言泛型对不同数据类型的适配性
2024-04-03

Go数据类型实例分析

本篇内容介绍了“Go数据类型实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!每一个变量都有数据类型,Go中的数据类型有:简单数据类型:
2023-06-30

TypeScript中的递归类型示例解析

这篇文章主要为大家介绍了TypeScript中的递归类型示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-05-14

深入浅析Java8中的类型注解

这篇文章将为大家详细讲解有关深入浅析Java8中的类型注解,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。什么是类型注解在java 8之前,注解只能是在声明的地方所使用,比如类,方法,属性;j
2023-05-31

深入了解Go语言中的引用类型

引用类型在Go语言中是一种特殊的数据类型,它们的值并非直接存储数据本身,而是存储数据的地址。在Go语言中,引用类型包括slices、maps、channels和指针。深入了解引用类型对于理解Go语言的内存管理和数据传递方式至关重要。本文将结
深入了解Go语言中的引用类型
2024-02-22

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录