字符串¶
一个 字符串 代表一个不可变的 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"
在这种情况下,前导空格不包括在结果字符串中。
Here文档¶
Here 文档 或 heredoc 在编写跨越多行的字符串时很有用。Here 文档由 <<-
后跟一个 heredoc 标识符表示,该标识符是一个以字母开头的字母数字序列(并且可以包含下划线)。Here 文档从下一行开始,以下一行结束,该行仅包含 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
Here 文档通常允许插值和转义。
为了表示不带插值或转义的 heredoc,开始的 heredoc 标识符用单引号括起来
<<-'HERE' # => "hello \\n \#{world}"
hello \n #{world}
HERE