golang学习随便记x[2,3]-字符串处理与正则表达式

这部分为个人参考网上资料的增补内容。根据遇到的情况不断整理。

字符串基本概念

golang字符串用双引号表示,字符用单引号,这一点和C/C++语言一样。golang没有和C/C++那样搞两套字符(char,wchar_t),golang字符串存放的是UTF8编码的Unicode rune(码点)序列。要获取字符串长度,需要使用系统函数len(返回的其实是字节数)。

golang可以类似JavaScript之类的语言那样直接用+(加号)拼接字符串,用==和<比较字符串。golang字符串是immutable(不可变)的,因此,拼接本质上是产生新对象的过程。

golang可以和多数语言那样用下标来引用字符串中的单个字节 (不一定对应字符!但总存在一个字节值),还可以用Python类似的切片方式来引用子字符串(其实对应子字节串) ------注意,切片包含数据起始指针data、长度len、容量cap ,它差不多就是一个结构体,用字段指明引用了原始数据的哪些部分,它代表变长序列。切片语法定义**起始位置、结束位置(左闭右开区间)**这两项信息,如果没有提供,就用默认值。即便是引用字符串中所有字符的切片,它的性质也和原始字符串是不同的。

因为方括号下标和切片,都有可能"切中"非码点起始的位置,所以,以下代码最后一段可能乱码。用 for......range遍历字符串,它是按码点遍历的,以下代码前面部分。golang用UTF8码点存放字符串,而UTF8和Unicode码点顺序是相同的,前缀编码方式保证了中间位置也能很容易确定码点边界,又兼容ASCII码,所以,golang字符串可以方便转换成字节切片或码点切片,见下面代码中间部分。

Go 复制代码
package main

import "fmt"

func main() {
	str := "Hello, 世界!这里后面都是中文字符。"
	for i, r := range str {
		fmt.Printf("Character %d: %c\n", i, r)
	}
	fmt.Println("Length of string:", len(str))
	fmt.Println("Length of runes:", len([]rune(str)))
	fmt.Println("Length of bytes:", len([]byte(str)))
	fmt.Println(str[14:17])
}

字符串输出除了上面的 fmt.Println 、fmt.Printf 向控制台输出,还可以用 fmt.Sprintf 格式化输出存放到新的字符串变量

strings标准库

判断字符串a中是否包含子字符串b,可以用 strings.Contains(a,b) ,其中 a 、b 可以是变量,也可以是字面量。如果要判断的包含状态是前缀、后缀判断,可以换成 strings.HasPrefixstrings.HasSuffix

将字符串str中的内容x替换成y,替换对前n次查找执行,最终返回新字符串,使用 newStr := strings.Replace(str, x, y, n)。当旧内容x为空字符串时,查找中会匹配每个码点的边界位置。当执行次数n<0时,含义就是"无穷大"。例如,下面的代码,字符串起始和结束位置都会添加==

Go 复制代码
	str := "ok世界!"
	newStr := strings.Replace(str, "", "==", -1)
	fmt.Println(newStr) // 输出: ==o==k==世==界==!==

虽然我们可以用上述 strings.Replace 来去除字符串中所有空白,但经常遇到的是去掉左右两端或一端的空白,我们可以用 newStr := strings.TrimSpace(str) 去掉两端空白,另外,还有 strings.TrimLeft、strings.TrimRight、strings.TrimPrefix、strings.TrimSuffix 。对于需要左边或者右边定制化去掉某些字符,还可以用带回调的strings.TrimLeftFunc、strings.TrimRightFunc

字符串分割可以用 parts := strings.Split(str, sep),返回的是一个个切片对象。如果 sep 是空字符串,那么类似上面的 strings.Replace,sep会匹配每个码点之后的位置,相当于会将字符串按码点分割成一串切片对象,每个对象就一个字符,起到打散效果。

strconv标准库

对于字符串和整数之间的相互转换,strconv包提供了和C语言类似的 strconv.Atoistrconv.Itoa,因为字符串转换成整数是解析过程,可能存在无法转换的情况,所以,返回值是包含 error 的。事实上,strconv.Atoi 是通过 strconv.parseInt 实现的,后者更通用,可以指定基数和位数。

Go 复制代码
	str := "-123"
	n := -456
	num, err := strconv.Atoi(str)
	if err != nil {
		fmt.Println("Error converting string to int:", err)
		return
	}
	sn := strconv.Itoa(n)

strconv.parseXxx 形式的字符串转数值的函数使用例子如下:

Go 复制代码
b, err := strconv.ParseBool("true")
f, err := strconv.ParseFloat("3.1415", 64)
i, err := strconv.ParseInt("-42", 10, 64)
u, err := strconv.ParseUint("42", 10, 64)

相应地,也有数值转换为字符串的函数strconv.FormatXxx,官网例子如下:

Go 复制代码
s := strconv.FormatBool(true)
s := strconv.FormatFloat(3.1415, 'E', -1, 64)
s := strconv.FormatInt(-42, 16)
s := strconv.FormatUint(42, 16)

regexp标准库

和 strings.Contains 对标的用来判断是否存在某个子串,用 regexp.MatchString 。因为正则表达式存在本身解析是否正确的问题,所以,该函数返回参数中包含 error。同类的还有 regexp.Matchregexp.MatchReader,差异是被匹配的对象分别是字节切片[]byte和io.RuneReader接口类型。

Go 复制代码
matched, err := regexp.MatchString(`foo.*`, "seafood")

多数正则表达式操作,需要先解析正则表达式。完成正则表达式解析(称为"编译")过程的函数有:CompileMustCompileCompilePOSIXMustCompilePOSIX 。后面两个只是为了符合POSIX ERE语法,我们这里略过。Compile和MustCompile的差别是对待解析错误的方式不同,前者解析返回*RegExp指针的同时返回错误error,后者如果解析错误直接panic------如果正则表达式本身是静态的字符串,没必要用前者。编译后,也存在"实例方法" MatchString/Match/MatchReader,效果和前面的"类型方法"等价,或许需要多次使用同一个pattern时会显得有优势。

Go 复制代码
re, err := regexp.Compile(".even")
var validID = regexp.MustCompile(`^[a-z]+\[[0-9]+\]$`)
fmt.Println(validID.MatchString("adam[23]"))

用正则来查找的方法很多,这些方法的名字是有特点的,它们可以用正则表达式来表达!这些方法模式为 Find(All)?(String)?(Submatch)?(Index)?------如果名字带有All,就会不断匹配互不重叠的子串,此时,有一个额外的参数n,用来指示最多返回多少个匹配的结果,n<0相当于最多无穷次(一般用n=-1)。如果带有String,那么参数是字符串类型,反之参数是字节切片类型,返回值也会和参数类型相应。如果名字带有Submatch,返回值是一个切片,可以指出每个子匹配(用于捕获组)。如果名字带有Index,匹配和子匹配通过字节索引位置来相互区分,即返回的不是被匹配的子字符串,而是它们的位置。

Go 复制代码
	re := regexp.MustCompile(`a(x*)b(y|z)c`)
	fmt.Printf("%q\n", re.FindStringSubmatch("-axxxbyc-"))
	fmt.Printf("%q\n", re.FindStringSubmatch("-abzc-"))

//  ["axxxbyc" "xxx" "y"]
//  ["abzc" "" "z"]

ExpandExpandString 函数实现动态扩展生成字节切片:可以提供模板(以下代码中template)和原始数据(以下代码中content),原始数据被正则匹配(以下代码中pattern)解析出需要的加工后数据(以下代码中submatches),加工后数据按正则匹配填充模板并附加到结果(以下代码中result)中实现动态扩展,最后返回结果。golang正则表达式语法概览参考 syntax package - regexp/syntax - Go Packages。下面的代码中,模式中的 (?m) 用来设置flag,表示使用多行模式(^分别匹配行首行尾而非文首文尾),这个分组不会被捕获。模式中 (?P\\\w+) 属于格式为 **(?P\<*name* \>*re*)**的语法,即捕获分组并对分组命名,名称为 name。从原始数据正则匹配提取数据时,必须用 Find(All)?(String)?SubmatchIndex 形式的方法,即返回结果是反映起止位置的切片。例如,下面代码第一个 submatches = \[18 33 18 25 27 33\] 对应行 option1: value1,其中 18 对应 option1 的开头位置,33 对应 value1的结束1的后一个位置(左闭右开),\[18,25) 对应 options1,\[27,33)对应value1。模板中,开头的是变量,对应同名分组,其语法是 name 或 {name},也可以用序号 1,2 来对应分组(分组未命名只能用该方式)。所以,下述代码for循环部分的逻辑是:根据正则匹配找出所有定位信息,从原始内容根据定位信息找出数据,根据正则匹配将数据替换到模板,替换后结果追加到目标。

Go 复制代码
content := `
	# comment line
	option1: value1
	option2: value2

	# another comment line
	option3: value3
`
	pattern := regexp.MustCompile(`(?m)(?P<key>\w+):\s+(?P<value>\w+)$`)
	template := "$key=$value\n"

	result := []byte{}
	for _, submatches := range pattern.FindAllStringSubmatchIndex(content, -1) {
		result = pattern.ExpandString(result, template, content, submatches)
	}
	fmt.Println(string(result))

对于正则替换,同时有 ReplaceAll、ReplaceAllString、ReplaceAllLiteralString、ReplaceAllStringFunc 类型方法和实例方法(带有 Literal,待替换子串中的$符号不会作为子匹配引用记号被使用,它用作字符本身)。对于正则分割,也同时有 Split 类型方法和实例方法。

Go 复制代码
	re := regexp.MustCompile(`[^aeiou]`)
	fmt.Println(re.ReplaceAllStringFunc("seafood fool", strings.ToUpper))
//  SeaFooD FooL

    s := regexp.MustCompile("a*").Split("abaabaccadaaae", 5)
//  s: ["", "b", "b", "c", "cadaaae"]

标准库 regexp 地址:regexp package - regexp - Go Packages

相关推荐
一只小小汤圆3 分钟前
如何xml序列化 和反序列化类中包含的类
xml·开发语言·c#
南枝异客10 分钟前
电话号码的字母组合
开发语言·javascript·算法
未来并未来34 分钟前
Sentinel 流量控制安装与使用
开发语言·python·sentinel
饭碗、碗碗香1 小时前
【开发常用命令】:docker常用命令
linux·运维·笔记·学习·docker·容器
Halo_tjn1 小时前
Java IO
java·开发语言
我命由我123451 小时前
STM32 开发 - 中断案例(中断概述、STM32 的中断、NVIC 嵌套向量中断控制器、外部中断配置寄存器组、EXTI 外部中断控制器、实例实操)
c语言·开发语言·c++·stm32·单片机·嵌入式硬件·嵌入式
东皇太星1 小时前
Python 100个常用函数全面解析
开发语言·python
宋一平工作室2 小时前
单片机队列功能模块的实战和应用
c语言·开发语言·stm32·单片机·嵌入式硬件
豆豆(设计前端)2 小时前
在 JavaScript 中,你可以使用 Date 对象来获取 当前日期 和 当前时间、当前年份。
开发语言·javascript·ecmascript
freyazzr2 小时前
TCP/IP 网络编程 | Reactor事件处理模式
开发语言·网络·c++·网络协议·tcp/ip