跳到內容

字串

字串 代表 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 documentheredoc 對於撰寫跨越多行的字串非常有用。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