2724 字
14 分钟
仓颉文档阅读-开发指南III: 基础数据类型(III) - 字符和字符串
NOTE

阅读文档版本:

语言规约 Cangjie-0.53.18-Spec

具体开发指南 Cangjie-LTS-1.0.3

在阅读 了解仓颉的语言规约时, 难免会涉及到一些仓颉的示例代码, 但 我们对仓颉并不熟悉, 所以可以用 仓颉在线体验 快速验证

有条件当然可以直接 配置Canjie-SDK

WARNING

博主在此之前, 基本只接触过C/C++语言, 对大多现代语言都没有了解, 所以在阅读过程中遇到相似的概念, 难免会与C/C++中的相似概念作类比, 见谅

且, 本系列是文档阅读, 而不是仓颉的零基础教学, 所以如果要跟着阅读的话最好有一门编程语言的开发经验

WARNING

在阅读仓颉编程语言的开发指南之前, 已经大概阅读了一遍 仓颉编程语言的语言规约, 已经对仓颉编程语言有了一个大概的了解

所以在阅读开发指南时, 不会对类似: 类、函数、结构体、接口等解释起来较为复杂名称 做出解释

此样式内容, 表示文档原文内容

基础数据类型#

字符类型#

字符类型使用Rune表示, 可以表示Unicode字符集中的所有字符

仓颉中的字符类型可以表示Unicode字符集中的所有字符, 用Rune作为类型名

Unicode字符集中的字符, 最大可以占用4字节的大小, 所以猜测Rune固定占用四字节

字符类型字面量#

字符类型字面量有三种形式: 单个字符、转义字符和通用字符

一个Rune字面量由字符r开头, 后跟一个由一对单引号或双引号包含的字符

单个字符的字符字面量举例:

let a: Rune = r'a'
let b: Rune = r"b"

转义字符是指在一个字符序列中对后面的字符进行另一种解释的字符

转义字符使用转义符号\开头, 后面加需要转义的字符

举例如下:

let slash: Rune = r'\\'
let newLine: Rune = r'\n'
let tab: Rune = r'\t'

通用字符以\u开头, 后面加上定义在一对花括号中的1~8个十六进制数, 即可表示对应的Unicode值代表的字符

举例如下:

main() {
let he: Rune = r'\u{4f60}'
let llo: Rune = r'\u{597d}'
print(he)
print(llo)
}

编译并执行上述代码, 输出结果为:

你好

仓颉的单个字符字面量其实挺简单:r'合法单个字符'``r'单个转义字符'``r'合法Unicode值'

仓颉中的Rune表示单个字符, 无论字符是什么, 字符本身实际占用字节是多少, 就只代表单个字符

字符类型支持的操作#

字符类型支持的操作符包括: 关系操作符, 即小于(<)、大于(>)、小于等于(<=)、大于等于(>=)、相等(==)、不等(!=)

比较的是字符的Unicode

Rune可以转换为UInt32, 整数类型可以转换为Rune, 具体的类型转换语法及规则请参见RuneUInt32和整数类型到Rune的转换

仓颉的RuneUInt32之间, 可以按照转换规则和语法进行转换

Rune只能被关系操作符操作, 可以与Unicode值进行比较

字符串类型#

字符串类型使用String表示, 用于表达文本数据, 由一串Unicode字符组合而成

仓颉中字符串由一串Unicode字符组合而成

字符串字面量#

字符串字面量分为三类: 单行字符串字面量, 多行字符串字面量, 多行原始字符串字面量

单行字符串字面量的内容定义在一对单引号或一对双引号之内, 引号中的内容可以是任意数量的(除了用于定义字符串字面量的非转义的引号和单独出现的\之外的)任意字符

单行字符串字面量只能写在同一行, 不能跨越多行

举例如下:

let s1: String = ""
let s2 = 'Hello Cangjie Lang'
let s3 = "\"Hello Cangjie Lang\""
let s4 = 'Hello Cangjie Lang\n'

多行字符串字面量开头结尾需各存在三个双引号(""")或三个单引号(''')

字面量的内容从开头的三个引号换行后的第一行开始, 到遇到的第一个非转义的三个引号为止, 之间的内容可以是任意数量的(除单独出现的\之外的)任意字符

不同于单行字符串字面量, 多行字符串字面量可以跨越多行

举例如下:

let s1: String = """
"""
let s2 = '''
Hello,
Cangjie Lang'''

多行原始字符串字面量以一个或多个井号(#)和一个单引号(')或双引号(")开头, 后跟任意数量的合法字符, 直到出现与字符串开头相同的引号和与字符串开头相同数量的井号为止. 在当前文件结束之前, 如果还没遇到匹配的双引号和相同个数的井号, 则编译报错

与多行字符串字面量一样, 原始多行字符串字面量可以跨越多行

不同之处在于, 转义规则不适用于多行原始字符串字面量, 字面量中的内容会维持原样(转义字符不会被转义, 如下例中s2中的\n不是换行符, 而是由\n组成的字符串\n)

举例如下:

let s1: String = #""#
let s2 = ##'#'\n'## // 输出结果为: #'\n
let s3 = ###"
Hello,
Cangjie
Lang"### // 该变量当中的换行、缩进等也会被保留

对于形如left = right的赋值操作

  1. 如果左操作数的类型是Byte(内置类型UInt8的别名), 并且右操作数是一个表示ASCII字符的字符串字面量, 那么右操作数的字符串将分别被强制转换为Byte类型, 再进行赋值

  2. 如果左操作数的类型是Rune, 并且右操作数是一个单字符的字符串字面量, 那么右操作数的字符串将分别被强制转换为Rune类型, 再进行赋值

main() {
var b: Byte = "0"
print(b)
b = "1"
print(b)
var r: Rune = "0"
print(r)
r = "1"
print(r)
}

编译并执行上述代码, 输出结果为:

484901

仓颉的字符串字面量也并不复杂, 有支持转义语义的 单行字符串和多行字符串字面量 和 不支持转义语义的多行原始字符串字面量

单行字符串字面量的语法为:"两个双引号括住"'两个单引号括住', 此时 被括住的内容中 不能出现单个\和无转义的"', 且不允许跨行

多行字符串字面量的语法为:"""三对双引号括住"""'''三对单引号括住''', 此时 被括住的内容中 不能出现单个\和无转义的"', 但允许跨行

多行原始字符串字面量的语法为:#"N对井号 和 一对双引号括住"##'N对井号 和 一对单引号括住'#, 井号数量不限但必须成对出现, 被括住的内容中不存在转义语义, 即\可以单独存在, 就表示一个\符号, 允许跨行

仓颉中字符串字面量可以直接尝试赋值给ByteRune类型变量, 但 此时字面量只能拥有一个单字符, 且 如果赋值给Byte类型变量, 此单字符只能是ASCII字符

插值字符串#

插值字符串是一种包含一个或多个插值表达式的字符串字面量(不适用于多行原始字符串字面量), 通过将表达式插入到字符串中, 可以有效避免字符串拼接的问题

插值字符串经常出现在println函数中输出非字符串类型的变量值, 例如println("${x}")

插值表达式必须用花括号{}包起来, 并在{}之前加上$前缀

{}中可以包含一个或者多个声明或表达式

当插值字符串求值时, 每个插值表达式所在位置会被{}中的最后一项的值替换, 整个插值字符串最终仍是一个字符串

下面是插值字符串的简单示例:

main() {
let fruit = "apples"
let count = 10
let s = "There are ${count * count} ${fruit}"
println(s)
let r = 2.4
let area = "The area of a circle with radius ${r} is ${let PI = 3.141592; PI * r * r}"
println(area)
}

编译并执行上述代码, 输出结果为:

There are 100 apples
The area of a circle with radius 2.400000 is 18.095570

插值字符串, 是一种更现代的字符串格式化方式

插值字符串是含有插值表达式的非原始字符串字面量, 语法格式为:"这是一个插值表达式${expr1}, 这也是一个插值表达式${expr2}, ..."

其中${expr}就是插值表达式, 插值表达式中可以存在任意数量的表达式, 且{}内的表达式会依次执行, 最终的最后一个表达式的值, 作为插值表达式的值, 且此值的类型必须实现ToString接口

举个简单的自定义的例子:

class ToStringTest <: ToString {
let val: Int64
ToStringTest(val!: Int64 = 20) {
this.val = val
}
public func toString(): String {
return "${val}"
}
}
main() {
let val = ToStringTest()
println("${val}")
}

这段代码的执行结果为:

截图

如果你没有实现ToString接口, 就不能作为插值表达式的最终值

时至今日, C++官方标准还没有插值表达式

字符串类型支持的操作#

字符串类型支持使用关系操作符进行比较, 支持使用+进行拼接

下面的例子展示了字符串类型的判等和拼接:

main() {
let s1 = "abc"
var s2 = "ABC"
let r1 = s1 == s2
println("The result of 'abc' == 'ABC' is: ${r1}")
let r2 = s1 + s2
println("The result of 'abc' + 'ABC' is: ${r2}")
}

编译并执行上述代码, 输出结果为:

The result of 'abc' == 'ABC' is: false
The result of 'abc' + 'ABC' is: abcABC

字符串还支持其他常见操作, 例如拆分、替换等

具体操作可以参考《仓颉编程语言标准库 API 》的String介绍, 下面给出部分常见操作:

main() {
var s1 = "abc"
var s2 = "ABCabc"
var s3 = "abcyyabcqqabcbc"
let r1 = s2.contains(s1) // 判断s2中是否包含字符串s1
println(r1) // true
let r2 = s3.split(s1) //对原字符串 s3 按照字符串 s1 分隔符分割, 指定是否删除空串
println(r2[1]) // yy
s1 = s2
println(s1) // ABCabc
}

字符串最常用的就是 关系比较 以及+进行拼接

仓颉标准库中对String类型还提供有各式各样的方法, 具体之后再分析