Julia 字符串
字符串是由零个或多个字符组成的有限序列。它是编程语言中表示文本的数据类型。
Julia 通常使用单引号 ' 创建单个字符,双引号 " 或三个双引号 """ 创建字符串:
c = 'x' # Char 字符 str = "RUNOOB" # String 字符串 runoob = """菜鸟教程 "RUNOOB",包含了引号""" # 三引号字符串
Julia 字符串类型特性:
- Julia 中用于字符串(和字符串字面量)的内置具体类型是 String。
- Julia 的字符串类型都是抽象类型 AbstractString 的子类型。
- Julia 有优秀的表示单字符的类型,即 AbstractChar。Char 是 AbstractChar 的内置子类型,它能表示任何 Unicode 字符的 32 位原始类型。
- Julia 字符串是不可更改的——任何 AbstractString 对象的值不可改变,拼接等操作总是生成新的字符串。
字符
单个字符用 Char 值表示,字符字面量用单引号包围。
Char 是 32 位原始类型,可以转换为其对应的整数值,即 Unicode 码点:
实例
'x': ASCII/Unicode U+0078 (category Ll: Letter, lowercase)
julia> typeof(c)
Char
julia> c = Int('x') # 字符转整数
120
julia> typeof(c)
Int64
我们也可以将一个整数值转换回 Char:
实例
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)
julia> Char(120)
'x': ASCII/Unicode U+0078 (category Ll: Letter, lowercase)
我们可以对 Char 的值进行比较和有限的算术运算:
实例
true
julia> 'A' <= 'X' <= 'Z'
true
julia> 'x' - 'a'
23
julia> 'A' + 1
'B': ASCII/Unicode U+0042 (category Lu: Letter, uppercase)
字符串基础
Julia 中的字符串可以使用双引号 " 或三个双引号 """ 来声明。如果需要在字符串中使用双引号,就可以使用三个双引号来声明:
实例
"RUNOOB"
julia> runoob = """菜鸟教程 "RUNOOB",包含了双引号"""
"菜鸟教程 \"RUNOOB\",包含了双引号"
如果字符串太长了,我们可以使用反斜杠 \ 来换行书写:
实例
line"
"This is a long line"
索引
Julia 字符串可以像数组一样使用索引来读取指定位置的字符,索引起始位置为 1 或者 begin,结束位置为 end:
实例
"RUNOOB"
julia> str[begin]
'R': ASCII/Unicode U+0052 (category Lu: Letter, uppercase)
julia> str[1]
'R': ASCII/Unicode U+0052 (category Lu: Letter, uppercase)
julia> str[2]
'U': ASCII/Unicode U+0055 (category Lu: Letter, uppercase)
julia> str[end]
'B': ASCII/Unicode U+0042 (category Lu: Letter, uppercase)
julia> str[end-1]
'O': ASCII/Unicode U+004F (category Lu: Letter, uppercase)
我们可以用范围索引来提取子字符串:
实例
"RUNOOB"
julia> str[begin:end-1]
"RUNOO"
julia> str[2:5]
"UNOO"
另外,表达式 str[k] 和 str[k:k] 给出的结果是不同的:前者通过索引获取指定位置的字符,类型是 Char;后者是通过给定的范围读取字符串,只是恰好包含一个字符的字符串:
实例
'B': ASCII/Unicode U+0042 (category Lu: Letter, uppercase)
julia> str[6:6]
"B"
范围截取也可以使用 SubString 方法来实现:
实例
"long string"
julia> substr = SubString(str, 1, 4)
"long"
julia> typeof(substr)
SubString{String}
多字节字符与字节索引
Julia 字符串使用 UTF-8 编码,一个中文字符占 3 个字节。字符串索引实际是字节索引,因此并非每个数字索引都有效:
实例
julia> s[1]
'R': ASCII/Unicode U+0052 (category Lu: Letter, uppercase)
# 第 7 个字节是"教"字的第一个字节,索引 5、6 落在字符中间,是无效索引
julia> s[7]
'教': Unicode U+6559 (category Lo: Letter, other)
julia> s[5]
ERROR: StringIndexError: invalid index [5], valid nearby indices [4]=>'B', [7]=>'教'
Stacktrace:
[...]
# length 计算字符数,ncodeunits 计算字节数
julia> length(s)
8
julia> ncodeunits(s)
12
# nextind / prevind 用于在有效索引间移动
julia> nextind(s, 1)
2
julia> collect(eachindex(s)) # 所有有效索引
8-element Vector{Int64}:
1
2
3
4
5
6
7
10
注意:遍历字符串时直接使用 for c in s 即可按字符迭代,不需要手动处理索引问题。
字符串拼接
拼接字符串有两种常用方式。第一种是 * 运算符,这是 Julia 的惯用写法:
实例
"Hello"
julia> whom = "world"
"world"
# * 号拼接字符串
julia> greet * ", " * whom * "!"
"Hello, world!"
Julia 用 * 而不是 + 拼接字符串,是因为字符串拼接不满足交换律("ab" 和 "ba" 不同),数学上非交换运算习惯用乘号表示。
第二种是 string() 函数,它可以拼接任意类型的值:
实例
"Hello, world.123"
^ 运算符可以重复字符串(等价于 repeat 函数):
实例
"RUNOOB RUNOOB RUNOOB "
julia> repeat("ab", 3)
"ababab"
插值
为了减少拼接的繁琐,Julia 允许像 Perl 中一样使用 $ 对字符串字面量进行插值:
实例
"Hello, world!"
在 $ 之后最短的完整表达式被视为插入其值于字符串中的表达式。因此,你可以用括号向字符串中插入任何表达式:
实例
"1 + 2 = 3"
# 对象会被转换为对应的字符串表示
julia> v = [1, 2, 3];
julia> "v: $v"
"v: [1, 2, 3]"
# Char 类型插入时不带引号
julia> c = 'x';
julia> "hi, $c"
"hi, x"
若要在字符串字面量中包含文本 $,就用反斜杠转义:
实例
I have $100 in my account.
三引号字符串字面量
三引号 """...""" 为我们创建更长更复杂的字符串提供了便利。
三引号字符串有一个特殊行为:自动去除缩进。它会以缩进最少的行(通常是结尾 """ 所在行)为基准去除公共前导空白:
实例
Hello,
world.
"""
" Hello,\n world.\n"
julia> println(str)
Hello,
world.
如果开头的 """ 后紧跟换行,这个换行会被去掉,这让多行字符串在代码中的书写更加整洁:
实例
Hello,
world."""
"Hello,\nworld."
Unicode 和 UTF-8
Julia 完全支持 Unicode 字符和字符串。
在字符字面量中,Unicode 码点可以用 \u(最多 4 位十六进制)和 \U(最多 8 位十六进制)转义序列表示:
实例
"∀ x ∃ y"
字符串字面量用 UTF-8 编码。UTF-8 是一种可变长度的编码,ASCII 字符(码点小于 128 的)使用单字节编码,而码点 0x80 及以上的字符使用最多 4 个字节编码。
字符串比较与查找
我们可以使用比较操作符按照字典顺序比较字符串:
实例
true
julia> "abracadabra" == "xylophone"
false
julia> "Hello, world." != "Goodbye, world."
true
julia> "1 + 2 = 3" == "1 + 2 = $(1 + 2)"
true
你可以使用 findfirst 与 findlast 函数搜索特定字符的索引:
实例
4
julia> findlast('o', "xylophone")
7
julia> findfirst('z', "xylophone") # 找不到时返回 nothing
你可以带上第三个参数,用 findnext 与 findprev 函数来在给定偏移量处搜索字符:
实例
7
julia> findprev('o', "xylophone", 5)
4
你可以用 occursin 函数检查在字符串中某子字符串可否找到:
实例
true
julia> occursin("RUNOOB", "Hello, world.")
false
julia> occursin('o', "Xylophon") # 也可以搜索单个字符
true
两个常用的转换函数 repeat 和 join:
实例
".:Z:..:Z:..:Z:."
julia> join(["apples", "bananas", "pineapples"], ", ", " and ")
"apples, bananas and pineapples"
其他常用的字符串函数
| 函数 | 描述 |
|---|---|
firstindex(str) | 可用来索引的最小索引(字符串总是 1) |
lastindex(str) | 可用来索引的最大索引 |
length(str) | str 中的字符个数 |
ncodeunits(str) | 字符串中代码单元(字节)的数目 |
codeunit(str, i) | 给出字符串 str 中索引为 i 的代码单元值 |
thisind(str, i) | 给定任意索引,查找所在字符的首个索引 |
nextind(str, i, n=1) | 查找索引 i 之后第 n 个字符的开头 |
prevind(str, i, n=1) | 查找索引 i 之前第 n 个字符的开始 |
startswith(s, prefix) | 判断 s 是否以 prefix 开头 |
endswith(s, suffix) | 判断 s 是否以 suffix 结尾 |
strip(s) | 去除两端空白字符 |
split(s, dlm) | 按分隔符拆分为子串数组 |
uppercase(s) / lowercase(s) | 转为大写 / 小写 |
实例
true
julia> endswith("runoob.com", ".com")
true
julia> strip(" RUNOOB ")
"RUNOOB"
julia> split("a,b,c", ",")
3-element Vector{SubString{String}}:
"a"
"b"
"c"
julia> uppercase("runoob")
"RUNOOB"
原始字符串字面量
无插值和非转义的原始字符串可用 raw"..." 形式的非标准字符串字面量表示,内容与输入完全一样。这对包含正则表达式、LaTeX、Windows 路径等内容很有用:
实例
julia> "runoob\nRUNOOB"
"runoob\nRUNOOB"
# raw 字符串保留原样
julia> raw"runoob\nRUNOOB"
"runoob\\nRUNOOB"
# raw 字符串中 $ 不插值
julia> x = 1;
julia> raw"x = $x"
"x = \$x"
例外的是,引号仍必须转义,例如 raw"\"" 等效于 "\""。
字节数组与版本号字面量
Julia 还有两种常用的非标准字符串字面量:
b"..." 字节数组字面量,用于表示 UInt8 数组:
实例
6-element Base.CodeUnits{UInt8, String}:
0x52
0x55
0x4e
0x4f
0x4f
0x42
v"..." 版本号字面量,创建 VersionNumber 对象,遵循语义化版本规范,常用于版本比较:
实例
v"1.10.4"
# 比较版本号
julia> v"1.10" < v"1.11"
true
# VERSION 是当前 Julia 的版本号
julia> VERSION >= v"1.6"
true
