Elixir `String.split` function caveat
Elixir의 String.split/1
, String.split/3
함수는 첫번째 인자에 split한 문자열, 두 번째 인자에 splitter로 쓰일 패턴이 들어오며 보통 다음처럼 사용한다.
iex> String.split("foo,bar", ",")
["foo", "bar"]
여기서 첫번째 인자에 빈 스트링(“”) 이 들어오면 어떻게 될까? 패턴에 상관없이 같은 결과가 나와야할것같지만, 흥미롭게도 두번째 인자에 따라 다음 3가지 결과를 볼 수 있다.
# 1
iex> String.split("")
[]
# 2
iex> String.split("", "")
["", ""]
# 3
iex> String.split("", ",")
[""]
어떻게 된 일일까?
일단 String.split/1
함수는 String.split/3
함수와 다르다. String.split/1
함수의 설명을 보면 다음과 같다.
Divides a string into substrings at each Unicode whitespace occurrence with leading and trailing whitespace ignored. Groups of whitespace are treated as a single occurrence. Divisions do not occur on non-breaking whitespace.
내부 코드를 들여다 보면, String.split/1
함수와 String.split/3
함수의 플로우가 크게 나뉘어져 있다. String.split/1
은 String.Break
모듈로 defdelegate
하고 있는데, String.Break
모듈은 unicode.ex 파일에 있다. 반면 String.split/3
함수는 String module 내에서 로직이 전개된다. pattern 인자에 정규식이 들어오는 경우를 제외하면 String.split/3
함수와 String.split/1
함수 모두 마지막에 :binary.split/3
함수를 호출하게 되는데, 여기서 String.split/1
함수는 마지막에 다음과 같은 코드가 호출된다.
:binary.split(string, unquote(whitespace -- non_breakable), [:global, :trim_all])
즉 String.split/1
함수는 String.split/3
함수를 다음처럼 호출한 것과 비슷하다.
iex> String.split(string, breakable_whitespace_character_list, trim: true)
결국 위에서 1번과 2, 3번의 결과 차이는 trim: true
옵션을 주었느냐의 차이이다. 또한 2번째와 3번째 결과의 차이는 :binary.split/3
함수의 결과 차이라고 볼 수 있다. 2번과 3번에 trim: true 옵션을 주면 다음처럼 결과가 모두 같아지게 된다.
iex> String.split("")
[]
iex> String.split("", "", trim: true)
[]
iex> String.split("", ",", trim: true)
[]
결론적으로, String.split/1
과 String.split/3
의 차이를 이해하고 split/3
함수를 쓸때 가급적 trim: true 옵션을 주어 결과가 예상치 못하게 어긋나는것을 방지하자.