문자열
문자열 (String)
String은 UTF-8 문자의 불변(immutable) 시퀀스를 나타내요.
String은 보통 큰따옴표(")로 UTF-8 문자를 감싸는 문자열 리터럴로 만들어요:
"hello world"
출처: Crystal 공식 문서
본문
이스케이프 (Escaping)
문자열 안에서 백슬래시는 특수 문자를 나타내요. 이 특수 문자는 이름이 있는 이스케이프 시퀀스일 수도, 유니코드 코드포인트의 숫자 표현일 수도 있어요.
사용 가능한 이스케이프 시퀀스:
"\"" # 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까지의 숫자가 최대 세 자리 오면, 8진수로 쓰인 코드포인트를 나타내요:
"\101" # => "A"
"\123" # => "S"
"\12" # => "\n"
"\1" # string with one character with code point 1
백슬래시 뒤에 u가 오면 유니코드 코드포인트를 나타내요. 그다음에는 유니코드 바이트를 나타내는 정확히 네 자리의 16진수(\u0000부터 \uFFFF까지)가 오거나, 중괄호로 감싼 한 자리에서 여섯 자리의 16진수(\u{0}부터 \u{10FFFF}까지)가 올 수 있어요.
"\u0041" # => "A"
"\u{41}" # => "A"
"\u{1F52E}" # => "🔮"
하나의 중괄호 안에는 공백으로 구분된 여러 유니코드 문자를 담을 수 있어요.
"\u{48 45 4C 4C 4F}" # => "HELLO"
보간 (Interpolation)
보간(interpolation)이 있는 문자열 리터럴은 문자열 안에 표현식을 넣을 수 있게 해주고, 런타임에 그 표현식이 전개돼요.
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
퍼센트 문자열 리터럴 (Percent string literals)
큰따옴표 문자열 외에도, 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)
단일 문자열 리터럴 외에도, 문자열 Array를 만드는 퍼센트 리터럴이 있어요. %w 또는 %W 다음에 한 쌍의 구분자를 붙여 표시해요. 유효한 구분자는 퍼센트 문자열 리터럴과 같아요.
구분자 안에서 공백이 개별 문자열 값을 나눠요.
%w 리터럴은 보간이나 이스케이프 시퀀스(공백 이스케이프 제외) 없이 정적 문자열 값을 담아요.
%W 리터럴은 보간과 이스케이프 시퀀스를 허용해요.
대소문자 표기는 퍼센트 문자열 리터럴 %q와 %Q와 유사해요.
보간 문법은 문자열 리터럴에서의 보간과 비슷하게 동작해요. 보간된 값은 문자열화되어 현재의 배열 요소에 삽입돼요. 배열 요소는 보간과 정적 구성요소의 조합으로 이뤄질 수 있어요.
%w(foo bar baz) # => ["foo", "bar", "baz"]
%w(foo\nbar baz) # => ["foo\\nbar", "baz"]
%w(foo(bar) baz) # => ["foo(bar)", "baz"]
# escapes white space
%w(foo\ bar baz) # => ["foo bar", "baz"]
%W(foo bar baz) # => ["foo", "bar", "baz"]
%W(foo\nbar baz) # => ["foo\\nbar", "baz"]
%W(foo(bar) baz) # => ["foo(bar)", "baz"]
# escapes
%W(foo\ bar baz) # => ["foo bar", "baz"]
%W(foo 'bar baz') # => ["foo", "'bar", "baz'"]
%W(foo "bar baz") # => ["foo", "\"bar", "baz\""]
보간 문법은 문자열 리터럴에서의 보간과 비슷하게 동작해요. 보간된 값은 문자열화되어 현재의 배열 요소에 삽입돼요. 배열 요소는 보간과 정적 구성요소의 조합으로 이뤄질 수 있어요.
%W[foo #{"bar"} baz] # => ["foo", "bar", "baz"]
%W[foo #{1 + 1} baz] # => ["foo", "2", "baz"]
%W[foo #{"bar"}baz#{"bab"} qux] # => ["foo", "barbazbab", "qux"]
%W[foo _#{"bar"}_ baz] # => ["foo", "_bar_", "baz"]
%W[foo #{"bar baz"} qux] # => ["foo", "bar baz", "qux"]
보간 문법은 스플랫(splat) 전개도 지원해서, 해당 위치에 여러 요소를 배열에 삽입해요. 스플랫 보간은 정적 접두사나 접미사 문자열을 지원하지 않아요. 즉 공백으로 둘러싸이거나 리터럴의 시작·끝에 붙어 있어야 해요.
%W[foo #{*%w[bar baz]} qux] # => ["foo", "bar", "baz", "qux"]
여러 줄 문자열 (Multiline strings)
어떤 문자열 리터럴이든 여러 줄에 걸칠 수 있어요:
"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 은 여러 줄에 걸친 문자열을 쓸 때 유용해요. 히어독은 <<- 다음에 히어독 식별자(identifier)를 붙여 표시해요. 식별자는 문자로 시작하는 영숫자 시퀀스이고 언더스코어를 포함할 수 있어요. 히어독은 다음 줄에서 시작하고, 식별자만(선택적으로 앞에 공백이 올 수 있는)을 포함하는 다음 줄에서 끝나요.
<<-XML
<parent>
<child />
</parent>
XML
히어독 내용의 앞 공백은, 히어독 식별자 앞에 오는 마지막 줄의 공백 수에 따라 제거돼요.
<<-STRING # => "Hello\n world"
Hello
world
STRING
<<-STRING # => " Hello\n world"
Hello
world
STRING
히어독 식별자 뒤, 그리고 같은 줄에 있는 것들은 히어독 앞에 있던 원래 표현식을 이어가요. 마치 시작하는 히어독 식별자의 끝이 문자열의 끝인 것처럼 말이죠. 다만 문자열 내용은 그다음 줄들에서 오고, 반드시 자신만의 줄에 있어야 하는 끝나는 히어독 식별자에서 끝나요.
<<-STRING.upcase # => "HELLO"
hello
STRING
def upcase(string)
string.upcase
end
upcase(<<-STRING) # => "HELLO WORLD"
Hello World
STRING
같은 줄에서 여러 히어독이 시작되면, 그 본문들은 순서대로 읽혀요:
print(<<-FIRST, <<-SECOND) # prints "HelloWorld"
Hello
FIRST
World
SECOND
히어독은 일반적으로 보간과 이스케이프를 허용해요.
보간이나 이스케이프 없는 히어독을 나타내려면, 여는 히어독 식별자를 작은따옴표로 감싸면 돼요:
<<-'HERE' # => "hello \\n \#{world}"
hello \n #{world}
HERE
더 알아보기
- String은 UTF-8 문자의 불변 시퀀스고, 큰따옴표 리터럴로 만들어요.
- 백슬래시 이스케이프(이름·8진수·16진수·유니코드)와
#{...}보간을 활용할 수 있어요. %(),%q,%Q,%w,%W같은 퍼센트 리터럴로 따옴표 이스케이프를 피하거나 문자열 배열을 만들 수 있어요.- 여러 줄 문자열과 히어독(
<<-)으로 긴 텍스트를 다루고, 작은따옴표 식별자로 보간·이스케이프를 끌 수 있어요.