Julia 字符串

2022-02-25 16:45 更新

Julia 中處理 ASCII 文本簡(jiǎn)潔高效,也可以處理 Unicode 。使用 C 風(fēng)格的字符串代碼來處理 ASCII 字符串,性能和語義都沒問題。如果這種代碼遇到非 ASCII 文本,會(huì)提示錯(cuò)誤,而不是顯示亂碼。這時(shí),修改代碼以兼容非 ASCII 數(shù)據(jù)也很簡(jiǎn)單。

關(guān)于 Julia 字符串,有一些值得注意的高級(jí)特性:

  • String 是個(gè)抽象類型,不是具體類型
  • Julia 的 Char 類型代表單字符,是由 32 位整數(shù)表示的 Unicode 碼位
  • 與 Java 中一樣,字符串不可更改:String 對(duì)象的值不能改變。要得到不同的字符串,需要構(gòu)造新的字符串
  • 概念上,字符串是從索引值映射到字符的部分函數(shù),對(duì)某些索引值,如果不是字符,會(huì)拋出異常
  • Julia 支持全部 Unicode 字符: 文本字符通常都是 ASCII 或 UTF-8 的,但也支持其它編碼

字符

Char 表示單個(gè)字符:它是 32 位整數(shù),值參見 Unicode 碼位 。 Char 必須使用單引號(hào):

julia> 'x'
'x'

julia> typeof(ans)
Char

可以把 Char 轉(zhuǎn)換為對(duì)應(yīng)整數(shù)值:

julia> int('x')
120

julia> typeof(ans)
Int64

在 32 位架構(gòu)上, typeof(ans) 的類型為 Int32 。也可以把整數(shù)值轉(zhuǎn)換為 Char

julia> char(120)
'x'

并非所有的整數(shù)值都是有效的 Unicode 碼位,但為了性能, char 一般不檢查其是否有效。如果你想要確保其有效,使用 is_valid_char 函數(shù):

julia> char(0x110000)
'\U110000'

julia> is_valid_char(0x110000)
false

目前,有效的 Unicode 碼位為,從 U+00U+d7ff,以及從 U+e000U+10ffff

可以用單引號(hào)包住 \u 及跟著的最多四位十六進(jìn)制數(shù),或者 \U 及跟著的最多八位(有效的字符,最多需要六位)十六進(jìn)制數(shù),來輸入 Unicode 字符:

julia> '\u0'
'\0'

julia> '\u78'
'x'

julia> '\u2200'
'?'

julia> '\U10ffff'
'\U10ffff'

Julia 使用系統(tǒng)默認(rèn)的區(qū)域和語言設(shè)置來確定,哪些字符可以被正確顯示,哪些需要用 \u\U 的轉(zhuǎn)義來顯示。除 Unicode 轉(zhuǎn)義格式之外,所有 C 語言轉(zhuǎn)義的輸入格式都能使:

julia> int('\0')
0

julia> int('\t')
9

julia> int('\n')
10

julia> int('\e')
27

julia> int('\x7f')
127

julia> int('\177')
127

julia> int('\xff')
255

可以對(duì) Char 值比較大小,也可以做少量算術(shù)運(yùn)算:

julia> 'A' < 'a'
true

julia> 'A' <= 'a' <= 'Z'
false

julia> 'A' <= 'X' <= 'Z'
true

julia> 'x' - 'a'
23

julia> 'A' + 1
'B'

字符串基礎(chǔ)

字符串文本應(yīng)放在雙引號(hào) "..." 或三個(gè)雙引號(hào) """...""" 中間:

julia> str = "Hello, world.\n"
"Hello, world.\n"

julia> """Contains "quote" characters"""
"Contains \"quote\" characters"

使用索引從字符串提取字符:

julia> str[1]
'H'

julia> str[6]
','

julia> str[end]
'\n'

Julia 中的索引都是從 1 開始的,最后一個(gè)元素的索引與字符串長度相同,都是 n 。

在任何索引表達(dá)式中,關(guān)鍵詞 end 都是最后一個(gè)索引值(由 endof(str) 計(jì)算得到)的縮寫。可以對(duì)字符串做 end 算術(shù)或其它運(yùn)算:

julia> str[end-1]
'.'

julia> str[end/2]
' '

julia> str[end/3]
ERROR: InexactError()
 in getindex at string.jl:59

julia> str[end/4]
ERROR: InexactError()
 in getindex at string.jl:59

索引小于 1 或者大于 end ,會(huì)提示錯(cuò)誤:

julia> str[0]
ERROR: BoundsError()

julia> str[end+1]
ERROR: BoundsError()

使用范圍索引來提取子字符串:

julia> str[4:9]
"lo, wo"
str[k] 和 str[k:k] 的結(jié)果不同:

julia> str[6]
','

julia> str[6:6]
","

前者是類型為 Char 的單個(gè)字符,后者為僅有一個(gè)字符的字符串。在 Julia 中這兩者完全不同。

Unicode 和 UTF-8

Julia 完整支持 Unicode 字符和字符串。正如上文所討論的 ,在字符文本中, Unicode 碼位可以由 \u\U 來轉(zhuǎn)義,也可以使用標(biāo)準(zhǔn) C 的轉(zhuǎn)義序列。它們都可以用來寫字符串文本:

julia> s = "\u2200 x \u2203 y"
"? x ? y"

非 ASCII 字符串文本使用 UTF-8 編碼。 UTF-8 是一種變長編碼,意味著并非所有的字符的編碼長度都是相同的。在 UTF-8 中,碼位低于 0x80 (128) 的字符即 ASCII 字符,編碼如在 ASCII 中一樣,使用單字節(jié);其余碼位的字符使用多字節(jié),每字符最多四字節(jié)。這意味著 UTF-8 字符串中,并非所有的字節(jié)索引值都是有效的字符索引值。如果索引到無效的字節(jié)索引值,會(huì)拋出錯(cuò)誤:

julia> s[1]
'?'

julia> s[2]
ERROR: invalid UTF-8 character index
 in next at ./utf8.jl:68
 in getindex at string.jl:57

julia> s[3]
ERROR: invalid UTF-8 character index
 in next at ./utf8.jl:68
 in getindex at string.jl:57

julia> s[4]
' '

上例中,字符 ? 為 3 字節(jié)字符,所以索引值 2 和 3 是無效的,而下一個(gè)字符的索引值為 4。

由于變長編碼,字符串的字符數(shù)(由 length(s) 確定)不一定等于字符串的最后索引值。對(duì)字符串 s 進(jìn)行索引,并從 1 遍歷至 endof(s) ,如果沒有拋出異常,返回的字符序列將包括 s 的序列。因而 length(s) <= endof(s)。下面是個(gè)低效率的遍歷 s 字符的例子:

julia> for i = 1:endof(s)
         try
           println(s[i])
         catch
           # ignore the index error
         end
       end
?

x

?

y

所幸我們可以把字符串作為遍歷對(duì)象,而不需處理異常:

julia> for c in s
         println(c)
       end
?

x

?

y

Julia 不只支持 UTF-8 ,增加其它編碼的支持也很簡(jiǎn)單。特別是,Julia 還提供了 utf16stringutf32string 類型,由 utf16(s)utf32(s)函數(shù)分別支持 UTF-16 和 UTF-32 編碼。它還為 UTF-16 或 UTF-32 字符串提供了別名 WStringwstring(s),兩者的選擇取決于 Cwchar_t 大小。有關(guān) UTF-8 的討論,詳見下面的字節(jié)數(shù)組文本。

內(nèi)插

字符串連接是最常用的操作:

julia> greet = "Hello"
"Hello"

julia> whom = "world"
"world"

julia> string(greet, ", ", whom, ".\n")
"Hello, world.\n"

像 Perl 一樣, Julia 允許使用 $ 來內(nèi)插字符串文本:

julia> "$greet, $whom.\n"
"Hello, world.\n"

系統(tǒng)會(huì)將其重寫為字符串文本連接。

$ 將其后的最短的完整表達(dá)式內(nèi)插進(jìn)字符串??梢允褂眯±ㄌ?hào)將任意表達(dá)式內(nèi)插:

julia> "1 + 2 = $(1 + 2)"
"1 + 2 = 3"

字符串連接和內(nèi)插都調(diào)用 string 函數(shù)來把對(duì)象轉(zhuǎn)換為 String 。與在交互式會(huì)話中一樣,大多數(shù)非 String 對(duì)象被轉(zhuǎn)換為字符串:

julia> v = [1,2,3]
3-element Array{Int64,1}:
 1
 2
 3

julia> "v: $v"
"v: [1,2,3]"

Char 值也可以被內(nèi)插到字符串中:

julia> c = 'x'
'x'

julia> "hi, $c"
"hi, x"

要在字符串文本中包含 $ 文本,應(yīng)使用反斜杠將其轉(zhuǎn)義:

julia> print("I have \$100 in my account.\n")
I have $100 in my account.

一般操作

使用標(biāo)準(zhǔn)比較運(yùn)算符,按照字典順序比較字符串:

julia> "abracadabra" < "xylophone"
true

julia> "abracadabra" == "xylophone"
false

julia> "Hello, world." != "Goodbye, world."
true

julia> "1 + 2 = 3" == "1 + 2 = $(1 + 2)"
true

使用 search 函數(shù)查找某個(gè)字符的索引值:

julia> search("xylophone", 'x')
1

julia> search("xylophone", 'p')
5

julia> search("xylophone", 'z')
0

可以通過提供第三個(gè)參數(shù),從此偏移值開始查找:

julia> search("xylophone", 'o')
4

julia> search("xylophone", 'o', 5)
7

julia> search("xylophone", 'o', 8)
0

另一個(gè)好用的處理字符串的函數(shù) repeat

julia> repeat(".:Z:.", 10)
".:Z:..:Z:..:Z:..:Z:..:Z:..:Z:..:Z:..:Z:..:Z:..:Z:."

其它一些有用的函數(shù):

  • endof(str) 給出 str 的最大(字節(jié))索引值
  • length(str) 給出 str 的字符數(shù)
  • i = start(str) 給出第一個(gè)可在 str 中被找到的字符的有效索引值(一般為 1 )
  • c, j = next(str,i) 返回索引值 i 處或之后的下一個(gè)字符,以及之后的下一個(gè)有效字符的索引值。通過 startendof ,可以用來遍歷 str 中的字符
  • ind2chr(str,i) 給出字符串中第 i 個(gè)索引值所在的字符,對(duì)應(yīng)的是第幾個(gè)字符
  • chr2ind(str,j) 給出字符串中索引為 i 的字符,對(duì)應(yīng)的(第一個(gè))字節(jié)的索引值

非標(biāo)準(zhǔn)字符串文本

Julia 提供了非標(biāo)準(zhǔn)字符串文本 。它在正常的雙引號(hào)括起來的字符串文本上,添加了前綴標(biāo)識(shí)符。下面將要介紹的正則表達(dá)式、字節(jié)數(shù)組文本和版本號(hào)文本,就是非標(biāo)準(zhǔn)字符串文本的例子。元編程章節(jié)有另外的一些例子。

正則表達(dá)式

Julia 的正則表達(dá)式 (regexp) 與 Perl 兼容,由 PCRE 庫提供。它是一種非標(biāo)準(zhǔn)字符串文本,前綴為 r ,最后面可再跟一些標(biāo)識(shí)符。最基礎(chǔ)的正則表達(dá)式僅為 r"..." 的形式:

julia> r"^\s*(?:#|$)"
r"^\s*(?:#|$)"

julia> typeof(ans)
Regex (constructor with 3 methods)

檢查正則表達(dá)式是否匹配字符串,使用 ismatch 函數(shù):

julia> ismatch(r"^\s*(?:#|$)", "not a comment")
false

julia> ismatch(r"^\s*(?:#|$)", "# a comment")
true

ismatch 根據(jù)正則表達(dá)式是否匹配字符串,返回真或假。 match 函數(shù)可以返回匹配的具體情況:

julia> match(r"^\s*(?:#|$)", "not a comment")

julia> match(r"^\s*(?:#|$)", "# a comment")
RegexMatch("#")

如果沒有匹配, match 返回 nothing,這個(gè)值不會(huì)在交互式會(huì)話中打印。除了不被打印,這個(gè)值完全可以在編程中正常使用:

m = match(r"^\s*(?:#|$)", line)
if m == nothing
  println("not a comment")
else
  println("blank or comment")
end

如果匹配成功, match 的返回值是一個(gè) RegexMatch 對(duì)象。這個(gè)對(duì)象記錄正則表達(dá)式是如何匹配的,包括類型匹配的子字符串,和其他捕獲的子字符串。本例中僅捕獲了匹配字符串的一部分,假如我們想要注釋字符后的非空白開頭的文本,可以這么寫:

julia> m = match(r"^\s*(?:#\s*(.*?)\s*$|$)", "# a comment ")
RegexMatch("# a comment ", 1="a comment")

當(dāng)調(diào)用 match 時(shí),你可以選擇指定一個(gè)索引,它指示在哪里開始搜索。比如:

julia> m = match(r"[0-9]","aaaa1aaaa2aaaa3",1)
RegexMatch("1")

julia> m = match(r"[0-9]","aaaa1aaaa2aaaa3",6)
RegexMatch("2")

julia> m = match(r"[0-9]","aaaa1aaaa2aaaa3",11)
RegexMatch("3")

可以在 RegexMatch 對(duì)象中提取下列信息:

  • 完整匹配的子字符串: m.match
  • 捕獲的子字符串組成的字符串多元組: m.captures
  • 完整匹配的起始偏移值: m.offset
  • 捕獲的子字符串的偏移值向量: m.offsets

對(duì)于沒匹配的捕獲,m.captures 的內(nèi)容不是子字符串,而是 nothingm.offsets 為 0 偏移(Julia 中的索引值都是從 1 開始的,因此 0 偏移值表示無效):

julia> m = match(r"(a|b)(c)?(d)", "acd")
RegexMatch("acd", 1="a", 2="c", 3="d")

julia> m.match
"acd"

julia> m.captures
3-element Array{Union(SubString{UTF8String},Nothing),1}:
 "a"
 "c"
 "d"

julia> m.offset
1

julia> m.offsets
3-element Array{Int64,1}:
 1
 2
 3

julia> m = match(r"(a|b)(c)?(d)", "ad")
RegexMatch("ad", 1="a", 2=nothing, 3="d")

julia> m.match
"ad"

julia> m.captures
3-element Array{Union(SubString{UTF8String},Nothing),1}:
 "a"
 nothing
 "d"

julia> m.offset
1

julia> m.offsets
3-element Array{Int64,1}:
 1
 0
 2

可以把結(jié)果多元組綁定給本地變量:

julia> first, second, third = m.captures; first
"a"

可以在右引號(hào)之后,使用標(biāo)識(shí)符 i,m, sx 的組合,來修改正則表達(dá)式的行為。這幾個(gè)標(biāo)識(shí)符的用法與 Perl 中的一樣,詳見 perlre manpage

i   不區(qū)分大小寫

m   多行匹配。 "^" 和 "$" 匹配多行的起始和結(jié)尾

s   單行匹配。 "." 匹配所有字符,包括換行符

    一起使用時(shí),例如 r""ms 中, "." 匹配任意字符,而 "^" 與 "$" 匹配字符串中新行之前和之后的字符

x   忽略大多數(shù)空白,除非是反斜杠??梢允褂眠@個(gè)標(biāo)識(shí)符,把正則表達(dá)式分為可讀的小段。 '#' 字符被認(rèn)為是引入注釋的元字符

例如,下面的正則表達(dá)式使用了所有選項(xiàng):

julia> r"a+.*b+.*?d$"ism
r"a+.*b+.*?d$"ims

julia> match(r"a+.*b+.*?d$"ism, "Goodbye,\nOh, angry,\nBad world\n")
RegexMatch("angry,\nBad world")

Julia 支持三個(gè)雙引號(hào)所引起來的正則表達(dá)式字符串,即 r"""...""" 。這種形式在正則表達(dá)式包含引號(hào)或換行符時(shí)比較有用。

... 三重引號(hào)的正則字符串,形式為 r"""...""",也是 ... 支持的(可能對(duì)于含有 ... 等式標(biāo)記或換行的正則表達(dá)式是方便的)。

字節(jié)數(shù)組文本

另一類非標(biāo)準(zhǔn)字符串文本為 b"..." ,可以表示文本化的字節(jié)數(shù)組,如 Uint8 數(shù)組。習(xí)慣上,非標(biāo)準(zhǔn)文本的前綴為大寫,會(huì)生成實(shí)際的字符串對(duì)象;而前綴為小寫的,會(huì)生成非字符串對(duì)象,如字節(jié)數(shù)組或編譯后的正則表達(dá)式。字節(jié)表達(dá)式的規(guī)則如下:

  • ASCII 字符與 ASCII 轉(zhuǎn)義符生成一個(gè)單字節(jié)
  • \x 和八進(jìn)制轉(zhuǎn)義序列生成對(duì)應(yīng)轉(zhuǎn)義值的字節(jié)
  • Unicode 轉(zhuǎn)義序列生成 UTF-8 碼位的字節(jié)序列

三種情況都有的例子:

julia> b"DATA\xff\u2200"
8-element Array{Uint8,1}:
 0x44
 0x41
 0x54
 0x41
 0xff
 0xe2
 0x88
 0x80

ASCII 字符串 “DATA” 對(duì)應(yīng)于字節(jié) 68, 65, 84, 65 。 \xff 生成的單字節(jié)為 255 。Unicode 轉(zhuǎn)義 \u2200 按 UTF-8 編碼為三字節(jié) 226, 136, 128 。注意,字節(jié)數(shù)組的結(jié)果并不對(duì)應(yīng)于一個(gè)有效的 UTF-8 字符串,如果把它當(dāng)作普通的字符串文本,會(huì)得到語法錯(cuò)誤:

julia> "DATA\xff\u2200"
ERROR: syntax: invalid UTF-8 sequence

\xff\uff 也不同:前者是字節(jié) 255 的轉(zhuǎn)義序列;后者是碼位 255 的轉(zhuǎn)義序列,將被 UTF-8 編碼為兩個(gè)字節(jié):

julia> b"\xff"
1-element Array{Uint8,1}:
 0xff

julia> b"\uff"
2-element Array{Uint8,1}:
 0xc3
 0xbf

在字符文本中,這兩個(gè)是相同的。 \xff 也可以代表碼位 255,因?yàn)樽址?em>永遠(yuǎn)代表碼位。然而在字符串中,\x 轉(zhuǎn)義永遠(yuǎn)表示字節(jié)而不是碼位,而 \u\U 轉(zhuǎn)義永遠(yuǎn)表示碼位,編碼后為 1 或多個(gè)字節(jié)。

版本號(hào)文字

版本號(hào)可以很容易地用非標(biāo)準(zhǔn)字符串的形式 v"..." 表示。版本號(hào)會(huì)遵循語義版本的規(guī)范創(chuàng)建 VersionNumber 對(duì)象,因此版本號(hào)主要是由主版本號(hào),次版本號(hào)和補(bǔ)丁的值決定的,其后是預(yù)發(fā)布和創(chuàng)建的數(shù)字注釋。例如,v"0.2.1-rc1+win64" 可以被分塊解釋為主版本 0,次要版本 2,補(bǔ)丁版本 1,預(yù)發(fā)布 RC1 和創(chuàng)建為 Win64。當(dāng)輸入一個(gè)版本號(hào)時(shí),除了主版本號(hào)的其余字段都是可選的,因此,會(huì)出現(xiàn)例如 v"0.2"v"0.2.0" 等效(空預(yù)發(fā)布/創(chuàng)建注釋),v"2"v"2.0.0" 等效,等等。

VersionNumber 對(duì)象大多是能做到容易且準(zhǔn)確地比較兩個(gè)(或更多)的版本。例如,恒定的 VERSION 把 Julia 版本號(hào)作為一個(gè) VersionNumber 對(duì)象管理,因此可以使用簡(jiǎn)單的語句定義一些特定版本的行為,例如:

if v"0.2" <= VERSION < v"0.3-"
    # do something specific to 0.2 release series
end

既然在上面的示例中使用了非標(biāo)準(zhǔn)的版本號(hào) v"0.3-",它使用了一個(gè)后連接號(hào):此符號(hào)是一個(gè) Julia 擴(kuò)展的標(biāo)準(zhǔn)符號(hào),它是用來表示一個(gè)低于任何 0.3 發(fā)行版的版本,其中包括其所有的預(yù)發(fā)行版本。所以在上面的例子中的代碼只會(huì)運(yùn)行穩(wěn)定在 0.2 版本,并不能運(yùn)行在這樣的版本 v"0.3.0-rc1"。為了允許它也在不穩(wěn)定的(即預(yù)發(fā)行版)0.2 版上運(yùn)行,較低的檢查應(yīng)修改為 v"0.2-" <= VERSION。

另一個(gè)非標(biāo)準(zhǔn)版規(guī)范擴(kuò)展允許對(duì)使用尾部 + 來表達(dá)一個(gè)上限構(gòu)建版本,例如 VERSION > "v"0.2-rc1+" 可以被用來表示任何版本在 0.2-rc1 之上且任何創(chuàng)建形式的版本:對(duì)于版本 v"0.2-rc1+win64" 將返回 false ,而對(duì)于 v"0.2-rc2" 會(huì)返回 true 。

使用這種特殊的版本比較是好的嘗試(特別是,尾隨 - 應(yīng)該總是被使用在上限規(guī)范,除非有一個(gè)很好的理由不去這樣),但這樣的形式不得被當(dāng)作任何的實(shí)際版本號(hào)使用,因?yàn)樵谡Z義版本控制方案上它們是非法的。

除了用于 VERSION 常數(shù),VersionNumber 對(duì)象還廣泛應(yīng)用于 Pkg 模塊,來指定包的版本和它們的依賴關(guān)系。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)