字串¶
字串 代表 UTF-8 字元的不可變序列。
字串通常使用雙引號 ("
) 將 UTF-8 字元括起來的字串字面值建立。
"hello world"
跳脫字元¶
反斜線表示字串內的特殊字元,它可以是具名跳脫序列或 Unicode 碼位的數值表示。
可用的跳脫序列
"\"" # double quote
"\\" # backslash
"\#" # hash character (to escape interpolation)
"\a" # alert
"\b" # backspace
"\e" # escape
"\f" # form feed
"\n" # newline
"\r" # carriage return
"\t" # tab
"\v" # vertical tab
"\377" # octal ASCII character
"\xFF" # hexadecimal ASCII character
"\uFFFF" # hexadecimal unicode character
"\u{0}".."\u{10FFFF}" # hexadecimal unicode character
反斜線後面的任何其他字元都將被解釋為字元本身。
反斜線後面最多跟隨三個介於 0 到 7 的數字,表示以八進位寫入的碼位
"\101" # => "A"
"\123" # => "S"
"\12" # => "\n"
"\1" # string with one character with code point 1
反斜線後面跟隨 u
表示 Unicode 碼位。它可以跟隨四個十六進位字元表示 Unicode 位元組 (\u0000
到 \uFFFF
),或者跟隨以大括號括住的一到六個十六進位字元 (\u{0}
到 \u{10FFFF}
)。
"\u0041" # => "A"
"\u{41}" # => "A"
"\u{1F52E}" # => "🔮"
一個大括號可以包含多個 Unicode 字元,每個字元之間以空白分隔。
"\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"
百分比字串陣列字面值¶
除了單一字串字面值之外,還有一個百分比字面值可以建立字串的 陣列。它以 %w
和一對分隔符號表示。有效的分隔符號與百分比字串字面值相同。
%w(foo bar baz) # => ["foo", "bar", "baz"]
%w(foo\nbar baz) # => ["foo\\nbar", "baz"]
%w(foo(bar) baz) # => ["foo(bar)", "baz"]
請注意,以 %w
表示的字面值不套用插值或跳脫,但空格除外。由於字串以單一空格字元 (
) 分隔,因此必須跳脫才能將其用作字串的一部分。
%w(foo\ bar baz) # => ["foo bar", "baz"]
多行字串¶
任何字串字面值都可以跨越多行
"hello
world" # => "hello\n world"
請注意,在上面的範例中,尾隨和前導空格以及換行符號都會出現在結果字串中。為避免這種情況,可以使用反斜線將字串拆分為多行,以連接多個字面值
"hello " \
"world, " \
"no newlines" # same as "hello world, no newlines"
或者,可以在字串字面值內插入反斜線,後接換行符號
"hello \
world, \
no newlines" # same as "hello world, no newlines"
在這種情況下,前導空白不會包含在結果字串中。
Heredoc¶
here document 或 heredoc 對於撰寫跨越多行的字串非常有用。heredoc 以 <<-
表示,後接 heredoc 識別符號,該識別符號是以字母開頭的英數字元序列(並且可以包含底線)。heredoc 在下一行開始,並以包含只有 heredoc 識別符號的下一行結束,可選擇性地在前面加上空白。
<<-XML
<parent>
<child />
</parent>
XML
根據 heredoc 識別符號之前最後一行中的空白數量,會從 heredoc 內容中移除前導空白。
<<-STRING # => "Hello\n world"
Hello
world
STRING
<<-STRING # => " Hello\n world"
Hello
world
STRING
在 heredoc 識別符號之後,以及在同一行中,後面的任何內容都會繼續執行 heredoc 之前出現的原始表達式。就好像起始 heredoc 識別符號的結尾是字串的結尾。但是,字串內容會出現在後續行中,直到結尾 heredoc 識別符號必須在自己的行中為止。
<<-STRING.upcase # => "HELLO"
hello
STRING
def upcase(string)
string.upcase
end
upcase(<<-STRING) # => "HELLO WORLD"
Hello World
STRING
如果多個 heredoc 在同一行開始,它們的主體將會循序讀取
print(<<-FIRST, <<-SECOND) # prints "HelloWorld"
Hello
FIRST
World
SECOND
heredoc 通常允許插值和跳脫。
若要表示沒有插值或跳脫的 heredoc,請將開頭 heredoc 識別符號括在單引號中
<<-'HERE' # => "hello \\n \#{world}"
hello \n #{world}
HERE