`Text` with locale

문자열을 좀 더 효율적으로 다루기 위해서는, String뿐만 아니라 TextByteString도 다루어야 한다.
특히 기존의 library들도 TextByteString을 적극적으로 사용하고 있기 때문에,
그 library들을 제대로 활용하기 위해서도 String만 다룰수는 없다.

그런데, 이 TextByteStringString과는 달리1 문자열을 꼭 UTF8로 다루지 않는다.
ByteString은 문자열을 UTF8로 다루기 위해서는 utf8-string이라는 library를 사용해야 한다.
그리고, Text의 경우에는 내부적으로 UTF8나 UTF16으로 다룰 수는 있는데, 이건 그 program이 작동하는 system의 locale 설정에 따른다.

Case 1: System environment depenednt locale…

System environment의 locale 설정에 따른다는 것은 상황에 따라서는 편리할지도 모르지만, 때로는 상당히 귀찮은 문제를 발생시킨다.

최근에는 Yesod를 사용해서 web service를 몇개 만들고 있는데, 그 중에서 markdown 문서를 읽어들이는 기능이 있다.
이 markdown 문서는 한글과 일본어를 섞어써 쓸 가능성이 있었기에 필연적으로 UTF8로 encoding했다.

문제는 이 web service program가 system environment의 locale 설정을 따라서 문서의 encoding을 지레짐작한다는 것이다.
현재 사용하고 있는 server에서는 web service를 deploy하기 위해서 upstart를 사용하고 있다.
그런데 upstart는 locale을 별도로 설정하지 않으면, *.UTF8가 아니라 c, 즉 POSIX locale로 설정한다.
결과적으로 program은 문자열은 제대로 읽어들일 수 없고, hGetContents: invalid argument (invalid byte sequence)과 같은 error를 밷어내면서 죽어버린다.

Case 1: read by String

처음에는 file을 읽어들일 때 System.IO.readFile 함수를 사용하고 있었다.
그리고 system environment의 locale과 독립적으로 encoding을 직접 설정하기 위해서 다음과 같은 wrapper를 만들었다.

이걸로 programe이 작동하는 system의 locale 설정과 관계없이, file을 UTF8로 읽어들일 수 있다.

Case 2: read by Text

성능 문제를 생각하다가 이 readUtf8File 함수도 재검토하게 되었다.
특히, markdown 문서를 HTML code로 변환해주는 함수는 그 입력 data type으로 Text를 받는다.
String으로 읽어들여도 Text로 변환해야 한다면, 처음부터 Text로 읽어들이는 것이 훨씬 낫다.
다행히도 Text로 file을 읽어들이는 Data.Text.IO.readFile가 있기 때문에 써봤다.

하지만, String일때에 경험했던 locale 관련 문제가 금방 생각났다.
System environment의 locale이 *.UTF8가 아닌 경우, Text에서는, Data.Text.IO.readFile은 읽어들인 문자열의 encoding이 어떻게 될 것인가?

Try 1: Just use readFile of Text

한번 readUtf8FileTextreadFile로 바꾸어보았다.
일단 local environment에서는 잘 동작했다.

문제는, locale을 c로 바꾼 경우 동작하는가?

locale을 c로 설정하는 방법은 여럿 있지만, 이번에는 가장 그럴싸해보이는 unset LC_ALL LANG LC_CTYPE LC_COLLATE LC_NUMERIC LC_TIME LC_MONETARY LC_MESSAGES을 사용해 보았다.
결과, 이전에 본 적이 있는 error message: hGetContents: invalid argument (invalid byte sequence)를 볼 수 있다.

Try 2: use Data.Text.IO.hGetContents

Data.Text.IO의 문서를 살펴보면 readUtf8File에서 사용한 System.IO.hGetContents와 동일한 Data.Text.IO.hGetContents 함수를 볼 수 있다.
그렇다면, readUtf8File에서 사용한 것과 동일한 방법을 쓸 수 있지 않을까?

당연히 사용할 수 있었다.

결과적으로, readUtf8File 함수는 다음과 같이 바뀌었다.

hGetContentsSystem.IO에서 Data.Text.IO에 들어있는 함수로 바꾸었을 뿐이다.

write Text

읽어들였으면 써보고도 싶다.

주의해야 할 점은, 쓰는데 사용하는 stdout과 같은 handle도 hSetEncoding을 사용해서 encoding을 설정해야 한다는 것이다.
설정하지 않고 Data.Text.IO.putStrLn같은 것을 사용하면 <stdout>: commitAndReleaseBuffer: invalid argument (invalid character)와 같은 error message를 볼 수 있다.
String을 사용하면 약간 다르긴 하지만 <stdout>: commitBuffer: invalid argument (invalid character)와 같은 error message를 볼 수 있다.


  1. GHC에서도 String이 문자열을 UTF8로 다루게 된 것도 그럭저럭 최근의 일이기는 하다. 

Post Revisions:

CC BY-NC-ND 4.0 This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다

This site uses Akismet to reduce spam. Learn how your comment data is processed.