北京大学Julia语言入门讲义第5章: 字符串

Julia有单独的字符型Char,
'a', 'B'
以Unicode编码表示。

对字符型变量ch,
Int(ch)获得其编码,如:

'中'
## '中': Unicode U+4E2D (category Lo: Letter, other)
Int('中')
## 20013

20013是十六进制数4E2D的十进制值。

适用于字符的函数:

  • isdigit, isuppercase, islowercase, isletter,
    isascii, isspace, ispunct: 判断字符是否属于某一类型。
  • uppercase, lowercase: 转换为大写或小写。

5.2 字符串下标与遍历

length()求字符串中字符个数,
每个汉字算一个字符。
对字符串用下标访问,是按字节计算的,
因为一个汉字可能占据多个字节,
所以有些下标位置能返回字符,
有些下标会出错。

s = "汉字123"
length(s)
## 5
s[1]
## '汉': Unicode U+6C49 (category Lo: Letter, other)
StringIndexError: invalid index [2], valid nearby indices [1]=>'汉', [4]=>'字'

Stacktrace:
......
s[7]
## '1': ASCII/Unicode U+0031 (category Nd: Number, decimal digit)

用范围下标,返回字符串结果,但是下标的计算仍是字节,如:

s[1:1]
## "汉"
s[4:4]
## "字"

这种设计是为了能够高效地访问字符串中的字符,
因为使用UTF-8编码,每个字符所用字节个数可能会不同。

函数firstindex(s)返回s中第一个字符的下标,
lastindex(s)返回s中最后一个字符的下标,
thisind(s, i)返回字节i所在字符的开始字节位置,
nextind(s, i)返回s中跟随在下标i后面的合法字符字节下标,
prevtind(s, i)返回s中位于下标i前面的合法字符字节下标。

这些函数是“遍历器”(iterator)的代表,
很多的复合数据结构在遍历元素时,
并不提供下标遍历,
而是提供开始位置、结束位置、下一个元素和上一个元素的函数。
如:

i = firstindex(s)
while i <= lastindex(s)
    println(i, ": ", s[i])
    i = nextind(s, i)
end
1: 汉
4: 字
7: 1
8: 2
9: 3

对字符串s
collect(s)可以将字符串s转换成一个字符数组,
每个元素是字符串中的一个字符,
但这样存储会损失效率。

5-element Vector{Char}:
 '汉': Unicode U+6C49 (category Lo: Letter, other)
 '字': Unicode U+5B57 (category Lo: Letter, other)
 '1': ASCII/Unicode U+0031 (category Nd: Number, decimal digit)
 '2': ASCII/Unicode U+0032 (category Nd: Number, decimal digit)
 '3': ASCII/Unicode U+0033 (category Nd: Number, decimal digit)

对字符串中每个字符也可以用for循环遍历,如

for ch in "汉字123"
    print(ch)
end
println()
## 汉字123

或用eachindex遍历:

s = "汉字123"
for i in eachindex(s)
    println(i, ": ", s[i])
end
1: 汉
4: 字
7: 1
8: 2
9: 3

也可以用enumerate获得下标和字符:

s = "汉字123"
for (i,ch) in enumerate(s)
    println(i, ": ", ch)
end
1: 汉
4: 字
7: 1
8: 2
9: 3

5.3 字符串是不可变类型

字符串是字符的序列,
但不允许修改其中的字符或者子串。如:

MethodError: no method matching setindex!(::String, ::Char, ::Int64)
......

5.4 读写字符串

函数readlines(filename)指定一个输入的文本文件名,
将文件的各行读入为一个字符串数组,
每个元素保存一行,默认不带有换行标志。

可以用如下方式对输入文件的每行循环:

for line in eachline(filename)
    ## 对line进行一些操作
end

Julia没有提供一个writelines()函数,
自定义及测试如下:

function writelines(lines::Array{AbstractString, 1}, filename::AbstractString)
    open(filename, "w") do io
        for line in lines
            println(io, line)
        end
    end
    return
end

lines = map(string, 1:3)
writelines(lines, "tmp1.txt")

5.5 字符串函数

Julia有很多与字符串处理的函数。
length, sizeof, ^repeat
lpad, rpad, strip, lstrip, rstrip, chop, chomp,
uppercase, lowercase, titlecase, uppercasefirst, lowercasefirst,
reverse

join(x, dlm)将字符串数组x的元素用指定的分隔符分隔后连接成一个长字符串,如

join(["abc", "汉字", "1"], "-+-")
## "abc-+-汉字-+-1"

省略分隔符时就不分隔地连接起来。

first(s, n), last(s, n)取出头或者尾部指定个数的字符。

collect(s)将字符串s转换成一个字符数组。
反过来,join(x)将字符型数组x中的字符连接成一个字符串。

string(x)将数值x转换成字符串表示,
string(x, y, z)x, y, z都转换成字符串然后连接起来。
repr(x)将表达式x的值表示成字符串。
parse(Int64, s)将字符串s中的数值转换成整数类型,
parse(Float64, s)将字符串s中的数值转换成Float64类型。

findfirst(pattern, string)string中查找并返回pattern子串首次出现的下标范围。
findnext(pattern, string, start)则是从start下标位置开始查找。
不存在时返回特殊的nothing,表示不存在,
而且在命令行不自动显示,可以用x== nothing或者isnothing(x)检测。

findfirst("ing", "Begining things")
## 6:8

findnext("ing", "Begining things", 9)
## 12:14

findfirst("ght", "Begining things") # 结果为nothing, 在命令行不显示

startswith(s, prefix), endswith(s, suffix)检查字符串前缀和后缀。

startswith("Photo", "Ph")
## true
endswith("weight", "ght")
## true

”判断一个字符是否在某个字符串中,
输入方法是“\in”+TAB键,
同于“in”。
'a' ∈ "can"结果为true

”为“不属于”,输入方法是“\notin”+TAB键。

occursin(needle, haystack)返回needle是否出现在haystack中,

occursin("oar", "board")
## true

split()函数将分隔的字符串拆分成字符串数组,如

split("1 2 3") |> show
## SubString{String}["1", "2", "3"]
split("1, 2, 3", ",") |> show
## SubString{String}["1", " 2", " 3"]

5.6 字符串比较

两个字符串(包括变量)之间的比较仍用“==”操作符;
其它比较都可以用,
使用字典序比较,
字符之间的次序依赖于所用编码(应为UTF-8编码)。

5.7 字符串插值

在字符串中可以用$变量名$(表达式)的格式插入变量值或者表达式值。
如:

x = 123
"x = $x"
## "x = 123"

上面的写法与如下语句等效:

x = 123
"x = " * string(x)
## "x = 123"

其中string()将其它类型转换成字符串类型。
显然,插值方法更方便。

插入表达式如:

"123 + 100 = $(123+100)"
## "123 + 100 = 223"

5.8 转换为字符串

string()函数可以将其它类型转换为字符串,如:

string(1 + 2)
## "3"
string([[1,2,3], [4,5,6]])
## "[[1, 2, 3], [4, 5, 6]]"

string()是基于函数print()进行转换的。

repr()也可以将其它类型转换为字符串,
它是基于函数show()进行转换的。
如:

repr(1 + 2)
## "3"
repr([[1,2,3], [4,5,6]])
## "[[1, 2, 3], [4, 5, 6]]"

5.9 格式化转换

Printf库提供了按指定格式将数值转换为字符串的功能。
例:

using Printf
s = @sprintf "%.40f" big(π)
## "3.1415926535897932384626433832795028841972"

宏命令@sprintf按照指定格式将数值转换为字符串并返回字符串结果。
其中"%.40f"称为格式字符串,
这个格式中的f表示使用定点实数表示,
.40表示小数点后有40位数字。
可以用%w.df格式表示定点实数宽度为w个字符,
小数点后有d位小数。

%wd格式用来显示w位整数,
位数不足时左填空格;
%0wd格式则在位数不足时左填0。

常用的格式还有"%s"格式,
表示原样显示字符串。
如果用"%ws"格式,
其中w是整数值,
则表示左对齐右填充空格到w个字符宽度。

这些格式使用C语言的printf格式约定,
可参考C语言的printf格式说明。

宏命令@printf则直接显示转换结果而不返回值。

5.10 正则表达式

Julia支持正则表达式功能。
Julia的正则表达式采用Perl规则。

正则表达式的写法对初学者比较困难,
这里不进行详细讲解,
读者可以找一本专门讲正则表达式的书,
或者其它编程语言中讲到正则表达式的书。
例如,
本文作者讲R语言的书中有一章讲文本处理,
其中比较详细地讲解了正则表达式的语法,
见:
http://www.math.pku.edu.cn/teachers/lidf/docs/Rbook/html/_Rbook/text.html

Julia中正则表达式模式字符串是双撇号界定的特殊字符串开始双撇号之前加r字母作为前缀,
还可以在字符串结尾的双撇号之后增加一些表示选项的字母,
遵从Perl语言的约定,
i表示不区分大小写,
m表示行首和行尾的匹配是针对每一行进行的,
s表示句点可以匹配换行符,等等。
比如,

会不区分大小写地匹配John单词。

加了r前缀的正则表达式中的特殊字符\不需要写成两个,
r"\w"就表示一个字母、数字、下划线,
而不是写成r"\\w"

可以用Regex()函数将一个字符串型的表达式转换成正则表达式类型,
这样可以动态地构造正则表达式。

下面的正则表达式对电子邮箱地址进行简单地匹配:

pat = r".+@.+"
occursin(pat, "jason@abc.com")
## true

occursin()仅返回是否匹配。
match()函数返回匹配结果,
设结果为m
m.match()返回匹配的整个字符串,
m.offset()返回匹配的开始字符下标,
m.offsets()返回匹配的各个子模式的开始字符下标,
m.captures()返回匹配的各个子模式。

pat = r"([a-zA-Z0-9_.]+)@([a-zA-Z0-9_.]+)"
m = match(pat, "==jason@abc.com==")
println(m.match)
## jason@abc.com
println(m.offset)
## 3
println(m.offsets)
## [3, 9]
println(m.captures)
## Union{Nothing, SubString{String}}["jason", "abc.com"]

findfirst找到某个模式首次出现,
findlast找到某个模式最后一次出现,
findnext找到某个模式在指定位置之后的首次出现,
findprev找到某个模式在指定位置之前的首次出现。
返回值为匹配的字节位置范围。
如:

pat = r"([a-zA-Z0-9_.]+)@([a-zA-Z0-9_.]+)"
s = "张三:jason@abc.com; 李四: tom@bde.com"
findfirst(pat, s)
## 10:22
s[10:22]
## "jason@abc.com"
findnext(pat, s, 23)
## 33:43
s[33:43]
## "tom@bde.com"
findlast(".com", s)
## 40:43

注意findlast(".com", s)中的.不是正则表达式的通配符,
因为第一个自变量并不是正则表达式。

replace()函数可以用来从字符串中替换某个指定的模式为另外的替换值,
替换字符串使用s前缀的字符串,
也可以用SubstitutionString()将一个字符串型表达式转换成替换字符串类型。

pat = r"([a-zA-Z0-9_.]+)@([a-zA-Z0-9_.]+)"
reppat = s"\1-nospam@\2"
replace("==jason@abc.com==", pat => reppat)
## "==jason-nospam@abc.com=="

其中包含s前缀的字符串是替换字符串,
\1表示查找到的模式中第一个子模式(第一个左圆括号的分组匹配的内容),
\1等的反斜杠不需要重复。

可以用eachmatch()函数提供对对多处匹配的循环,
其结果是一个迭代器(iterator)。如

pat = r"\w+"
for imatch in eachmatch(pat, "It is raining.")
    println("\"$(imatch.match)\"")
end
## "It"
## "is"
## "raining"

正则表达式中\w代表字母、数字、下划线。

韭菜热线原创版权所有,发布者:风生水起,转载请注明出处:https://www.9crx.com/75093.html

(0)
打赏
风生水起的头像风生水起普通用户
上一篇 2023年8月18日 00:37
下一篇 2023年8月18日 23:13

相关推荐

  • 投资可以帮助您的 5 种方式

    如果亚伯拉罕·马斯洛教授个人理财课程,他会把收入和储蓄描述为任何个人财务成功计划的基础。收入满足我们的生理需求——食物、住所、衣服——而储蓄是安全的基石。收入和储蓄将是他著名的金字塔的最底层两层。 正如我们的幸福不仅仅取决于满足生理需求和安全感一样,我们的财务成功也不仅仅取决于赚钱和储蓄。 两者(我们的福祉和我们的财务成功)紧密相连。投资是达到个人财务较高水…

    2023年11月13日
    8100
  • 投机性兴奋和对错失的恐惧

    作者:John Hussman,2024 年 2 月 27 日 特别临时更新 “过去一年整体市场在脆弱性明显的情况下并未出现下滑,以及新的投资特征的出现,让投资者相信美国已经进入了一个新的投资时代,旧的指导方针不再适用。” 。许多人现在开始相信,市场风险不再是一个现实的考虑因素,而投资不足或现金以及错失机会的风险超过了其他任何风险。” ——《巴伦周刊》,19…

    2024年4月3日
    3400
  • 北京大学Julia语言入门讲义第16章: 随机模拟

    介绍 在用数学模型, 包括概率统计模型处理实际应用中的问题时,我们希望建立的模型能够尽可能地符合实际情况。但是,实际情况是错综复杂的, 如果一味地要求模型与实际完全相符,会导致模型过于复杂, 以至于不能进行严格理论分析,结果导致模型不能使用。所以,实际建模时会忽略许多细节, 增加一些可能很难验证的理论假设,使得模型比较简单,可以用数学理论进行分析研究。 这样…

    2023年8月28日
    15300
  • 你应该签订婚前协议吗? 这是您需要了解的内容

    ‍ 结婚是人生中最重大的决定之一。您已经找到了灵魂伴侣,并期待着共同的美好未来。但在步入婚姻殿堂之前,您需要问自己一个问题:您应该签订婚前协议吗? ‍ 了解婚前协议 ‍ 婚姻是两个相爱并希望共同度过余生的人之间的美好结合。然而,尽管我们多么希望从此过上幸福的生活,但有时事情并不会按计划进行。离婚率很高,最好为最坏的情况做好准备。这就是婚前协议的用武之地。 ‍…

    2023年11月18日
    9000
  • 长期投资10到15年会发生什么?

    人们很容易忘记,我们正在为长期目标进行投资,其中许多目标需要十年或更长时间才能实现。即使是年长的投资者也常常试图将部分财富留给下一代或可以从他们的慷慨中受益的组织或实体。 希望你看看下面的图表,观察美国股市投资历史的方式,并注意当你停止关注短期、一年回报并开始关注时,事情看起来有多么不同。 从左下到右上角,该图报告了从 1928 年到 2022 年每年美国股…

    2023年11月22日
    11600

发表回复

登录后才能评论
客服
客服
关注订阅号
关注订阅号
分享本页
返回顶部