字串 String
字串(String)相當於一組不可變(Immutable)的 UTF-8 字元序列。
字串通常由字串常值來表達——即由一對雙引號("
)括住數個 UTF-8 字元:
"hello world"
跳脫字元
如同字元,反斜線(\
)也可以拿來表達一些特定的字元:
"\"" # 雙引號
"\\" # 反斜線
"\a" # 蜂鳴器警報
"\b" # 退格
"\e" # 跳脫
"\f" # 換頁
"\n" # 換行
"\r" # 輸入鍵 (Enter)
"\t" # Tab
"\v" # 垂直 Tab
"\888" # 八進位 ASCII 字元
"\xFF" # 十六進位 ASCII 字元
"\uFFFF" # 十六進位萬國碼字元
"\u{0}".."\u{10FFFF}" # 十六進位萬國碼字元
其他接在反斜線後方的跳脫字元都表示該字元本身。
我們也可以使用反斜線搭配最多 3 個數字表達 8 進位編碼的字元:
"\101" # => "A"
"\123" # => "S"
"\12" # => "\n"
"\1" # 包含一個編碼位置 1 之字元的字串
我們還可以使用反斜線搭配一個 u 來表達萬國碼碼位。後面跟著 4 個十六進位數字或是使用大括號({}
)括住最多 6 個十六進位數字(從 0 到 10FFFF)來表達:
"\u0041" # => "A"
"\u{41}" # => "A"
"\u{1F52E}" # => "🔮"
同時,使用大括號的時候可以同時表達多個文字:
"\u{48 45 4C 4C 4F}" # => "HELLO"
內插表達式
建立字串時,你可以使用內插表達式來混合並嵌入表達式。
a = 1
b = 2
"sum: #{a} + #{b} = #{a + b}" # => "sum: 1 + 2 = 3"
字串也可以透過 String#% 方法來執行插值。
任何表達式都可以被放置於插值區塊中,但儘量保持插值表達式越短越能夠能保持可讀性。
我們可以透過跳脫 #
符號來避免被插值,或是使用 %q()
也可以避免被插值。
"\#{a + b}" # => "#{a + b}"
%q(#{a + b}) # => "#{a + b}"
內插表達式以 String::Builder 實作,每個表達式(#{...}
)內的的值都會呼叫 Object#to_s(IO)
來取得要填入的字串。
表達式 "sum: #{a} + #{b} = #{a + b}"
等義於:
String.build do |io|
io << "sum: "
io << a
io << " + "
io << b
io << " = "
io << a + b
end
百分比字串常值表示法
除了使用雙引號來表達字串以外,Crystal 亦支援使用百分比符號(%
)及成對的符號相夾來表示。
合法的符號為小括號(()
)、中括號([]
)、大括號({}
)、角括號(<>
)以及垂直條(||
)。
除了垂直條,其他的符號都可以在字串中成對使用而不會結束字串。
字串中需要使用雙引號時,就可以使用這個方法來避免使用跳脫字元:
%(hello ("world")) # => "hello (\"world\")"
%[hello ["world"]] # => "hello [\"world\"]"
%{hello {"world"}} # => "hello {\"world\"}"
%<hello <"world">> # => "hello <\"world\">"
%|hello "world"| # => "hello \"world\""
此外,使用 %q
可以避免字串被插值或跳脫,而 %Q
與 %
等義。
name = "world"
%q(hello \n #{name}) # => "hello \\n \#{name}"
%Q(hello \n #{name}) # => "hello \n world"
Percent string array literal
Besides the single string literal, there is also a percent literal to create an Array of strings. It is indicated by %w
and a pair of delimiters. Valid delimiters are as same as 百分比字串常值表示法.
%w(foo bar baz) # => ["foo", "bar", "baz"]
%w(foo\nbar baz) # => ["foo\\nbar", "baz"]
%w(foo(bar) baz) # => ["foo(bar)", "baz"]
Note that literal denoted by %w
does not apply interpolation nor escapes expect spaces. Since strings are separated by a single space character (
) which must be escaped to use it as a part of a string.
%w(foo\ bar baz) # => ["foo bar", "baz"]
多行字串
字串能夠橫跨多行文字:
"hello
world" # => "hello\n world"
注意上面的例子中,我們可以看到產生的字串中包含了換行以及空白。
如果想要避免產生換行及空白,但還是想要將字串切割成好幾行表達的話,可以使用反斜線來串聯各個部分:
"hello " \
"world, " \
"no newlines" # => "hello world, no newlines"
或是我們也可以將反斜線直接插在字串裡面:
"hello \
world, \
no newlines" # => "hello world, no newlines"
在這個範例中,我們可以看到行首空白是不會被包含在字串中的。
Heredoc
當建立多行字串的時候,我們也可以使用「Heredoc」來協助我們。
一個 Heredoc 起始於 <<-IDENT
,IDENT
是一個標識符(由字母開頭且只包含字母與數字),並結束於開頭為 IDENT
的某行(略過行首空白)。
後方可以直接接著非字母的字元(如 .
, )
)來方便對 Heredoc 呼叫方法或用來關閉成對的括號。
<<-XML
<parent>
<child />
</parent>
XML
與結束標識符之相同數量的行首空白將自動被忽略。如:
<<-STRING # => "Hello\n world"
Hello
world
STRING
<<-STRING # => " Hello\n world"
Hello
world
STRING
我們可以在 Heredoc 結束後直接呼叫方法,或是將其置於括號中:
<<-SOME.upcase # => "HELLO"
hello
SOME
def upcase(string)
string.upcase
end
upcase(<<-SOME) # => "HELLO"
hello
SOME
在 Heredoc 中亦支援跳脫及內插表達式。
同時,只要將 Heredoc 的標識符用單引號('
)括起就能不執行跳脫及內插:
<<-'HERE' # => "hello \\n \#{world}"
hello \n #{world}
HERE