NDC 2014, 피할 수 없는 문자열의 세계
Upcoming SlideShare
Loading in...5
×
 

NDC 2014, 피할 수 없는 문자열의 세계

on

  • 4,794 views

 

Statistics

Views

Total Views
4,794
Views on SlideShare
1,125
Embed Views
3,669

Actions

Likes
9
Downloads
49
Comments
0

4 Embeds 3,669

http://blog.naver.com 3528
http://cafe.naver.com 96
https://twitter.com 44
http://memolog.blog.naver.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    NDC 2014, 피할 수 없는 문자열의 세계 NDC 2014, 피할 수 없는 문자열의 세계 Presentation Transcript

    • 피할 수 없는 문자열의 세계 ㈜ 넥슨코리아 데브캣스튜디오 파이오니어랩 김 재석
    • 김 재석 수석연구원 2014/현재 미공개 신작 링토스 세계여행 Crew 2010/2013 마비노기2: 아레나 2006/2009 마비노기 영웅전 테크니컬 디렉터 2004/2005 마비노기 2003/2004 프로젝트 T2 2001/2003 오즈 [ɡ̊ɪm̚ ʨæːsɤk̚] /ɡim ɟɛːzʌɡ/
    • 대상 / 강연의 목적 • 이제 프로그래밍을 하셔야 하는 분을 대상으로 합니다. • 전업 프로그래머 중 일부에게는 상식이라고 생각할 수 있지만, 정작 정규 교육과정에서는 잘 다루지 않는 내용을 공유하려고 했습니다. • 여러분의 시행착오를 줄이거나 교양을 늘리는데 도움이 되면 좋겠습니다.
    • 목차 문자열 / 문자열 쌓기 / 텍스트 쓰기 / 서식 문자열 부록: 한국어의 조사 처리
    • 문자열? 뜬금없이 왜?
    • 다른 기본 형식들은 (1) • 논리식을 표현하기 위해 컴퓨터에서 진위 값을 쓰려면? • 2가지 상태만 필요하면 불 (George Boole) 대수를 사용 • 3가지 상태가 필요하면 우카시에비치 (Jan Łukasiewicz) 대수를 사용
    • 다른 기본 형식들은 (2) • 정수의 부분 집합을 표현하려면? • 음수 표현이 필요 없으면 모듈로 연산을 사용, 필요하면 2의 보수를 사용 • [0, 255] 혹은 [-128, 127] 범위는 8비트, • [0, 65535] 혹은 [-32768, 32767] 범위는 16비트, • [0, 4294967295] 혹은 [-231, 231-1] 범위는 32비트 정수형을 사용 • 이 범위를 넘어서게 되면 주변의 프로그래머와 상담
    • 다른 기본 형식들은 (3) • 유리수나 실수를 다루려면? • IEEE (전기 전자 기술자 협회) 754 표준 부동소수점 실수형 • 유효자리가 10진수 7자리 이내라면 32비트, • 유효자리가 10진수 15자리 이내라면 64비트 실수형을 사용 • 이 범위를 넘어서게 되면 주변의 프로그래머와 상담
    • 문자열은 비교적 답이 쉽게 나오는 다른 기본 형식들과 달리 • 트랜지스터가 나온 뒤 1갑자60년 가까이 지났음에도 표현 및 구현 방법의 정석이 불명확 • 내부 구현 방법에 따른 숨은 비용을 고려하지 않고 남용하기도 쉬움 • 특히 대중적으로 많이 사용되는 언어들은 최초 개발로부터 시간이 많이 흘러 최근 유행을 못 따름 • C 1972 개발 1990 ISO/IEC 국제 표준화 2011 창시자 사망 • C++ 1983 개발 1998 ISO/IEC 국제 표준화
    • 문자열 사람이 사고하는 문자열, 기계가 처리하는 문자열.
    • Hello, world! 프로그래밍을 하면 보통 가장 먼저 접하게 되는 문자열 ”“ 사람은 보통 따옴표를 쓰지 않아도 끝을 안다. 시작과
    • Hello, world! 프로그래밍을 하면 보통 가장 먼저 접하게 되는 문자열 ”“ 기계에게는 따옴표를 붙여 이 범위가 문자열임을 알려줘야 한다.
    • 불변 문자열 연산 따옴표로 만들어진 이 문자열의 내용은 바꿀 수 없다
    • 문자 나열 H e l l o , w o r l d !
    • 길이 H e l l o , w o r l d ! 1 2 3 4 5 6 7 8 9 10 12 13 이렇게 세고 있어야 하나? 11
    • 일치 연산 H e l l o , w o r l d ! H e l l o , w o r l d !
    • 요약 digest 값 H e l l o , w o r l d ! H e l l , w o r l d ! 6cd3556deb0da54bca060b4c39479839 e614491f3139889c2e2822a4110b3745 요약 값이 다르면 중간까지 비교해볼 필요도 없다배꼽?
    • 비교 연산 H e l l o , w o r l d ! H e l l , w o r l d !
    • 비교 연산: 조합형 코드의 경우 ㅇ ㅏ ㄴ ㄴ ㅕ ㅇ ㅇ ㅏ ㄴ ㅕ 조합 단위 처리를 잘못 할 경우 결과가 반대가 된다 첫 가 끝 첫 가 끝 첫 가 첫 가
    • 문자열 생성 실행 중에 문자열을 만들고 싶다면
    • 부분 문자열 H e l l o , w o r l d ! w o r l d복사
    • 문자열 결합 H e l l o , w o r l d ! H e l l o , w o r l d ! 복사 복사
    • 문자열 구현 시간 對 공간, 어느 쪽을 선택?
    • 길이로 시작 / 종결 문자로 종료? • 시간이 더 귀중한 자원인 프로세서를 위해서는 길이로 시작 • 문자열의 길이를 얻는 연산은 상당히 자주 사용됨 • 종결 문자로 끝내는 경우가 유용한 경우도 있지만, 일반적인 문자열 조작은 아님
    • 색인은 어떻게? • [0, 길이) 색인은 기계의 메모리 주소 친화적이지만 • [1, 길이] 색인을 사용하면, 대칭이 되는 [-길이, -1] 색인을 문자열 끝으로부터의 위치를 표현하는데 사용할 수 있다. H e l l o , w o r l d ! 1 2 3 4 5 6 7 8 9 10 12 13 -13 -12 -11-10 -9 -8 -7 -6 -5 -4 -2 -1 11 -3 찾기 실패: -1 찾기 실패: 0
    • 각 글자를 어떻게 저장? • 문자열 처리는 대체로 프로세서 안에서 이뤄짐 • 가변 길이로 저장을 하면 주요 연산이 느려지므로 고정 길이로 처리 • 실제로 가변 길이로 저장을 하는 경우는 뒤에 다시 소개
    • 생성 시점에 문자당 길이를 고정 • 모두 ISO 8859-1 (1바이트) 영역이면 1바이트씩, • 라틴 문자 • 모두 UCS-2 (2바이트) 영역이면 2바이트씩, • 현대에 사용되고 있는 의 각종 언어들의 문자 • 그밖에 UCS-4 (4바이트) 영역이 하나라도 포함될 때 4바이트씩 사용하면 전반적인 수행 시간을 포기하지 않고, 가능한 경우에는 공간을 절약 • 평생 볼 일 없을 고고학적 문자 등
    • 요약 digest 값은 언제 계산? • 일치 비교 연산을 처음 사용할 때 계산해서 저장해 놓는 것을 추천 • 전혀 비교하지 않는 문자열도 많다. • 쓰기 전용
    • 복사를 덜 하는 멋진 방법은? • 안 해도 될 때 안 하면 된다. • 문자열을 내부에서 보관하고 있다 재활용 하는 방법도 있고, 문자열 상수만 가질 수 있는 특성을 이용하는 방법도 있다. • 종속 형식 dependent type 을 일부 흉내낼 수 있는 C++의 경우: template <int N> string(const wchar_t[N]& s) : length(s[N – 1] != L’0’ ? N : N - 1), s(s), own(false) { }
    • 복사가 너무 많다 • N개의 문자열을 하나로 합치면 어떻게 결합시켜도 N-1번의 복사가 발생 • 공간을 미리 확보해서 복사를 줄이면 안 되나?  문자열을 쌓아서 하나의 문자열로 변환
    • 문자열 쌓기 string builder 순차 접근 / 임의 접근
    • 문자열로 바꾸기 전에는 조각나도 괜찮다. “Hello” + “,“ + “ “ + “world”, “!” 결합했다가 일부분을 제외하고 다시 추가 결합하는 경우 없이 계속 결합만 반복
    • 순차 접근 / 임의 접근 • 많은 경우 문자열을 쌓는 작업은 한번에 써 내려가게 만들 수 있다. • 어쩌다 가끔 필요한 기능은, 바로 그 때 따로 만드는 것이 낫다. • 나중 내용을 알아야만 하는 경우는 계산 미루기를 lazy evaluation 이용
    • 문자열 결합의 일반화 H e l l o , w o r l d ! H e l l o , w o r l d !
    • 텍스트 쓰기 메모리 밖으로 이주하는 문자열
    • 표현 • 문자열 쌓을 곳에 문자열을 쓰면 문자열 쌓기와 다를 바가 없지만, • 메모리나 파일, 네트워크에 문자열을 쓰기 시작하면 • 이진 형식으로 표현해야 하는 경우가 생기며 • 처리 형식이 아닌 저장 형식이므로 가변 길이 출력을 해도 상관 없다.
    • UTF-32 32비트 유니코드 변환 서식 • 글자마다 32비트 (4바이트) 값으로 변환해서 쓴다 • 가장 단순하고 용량을 많이 사용하는 방법 • U+FEFF 코드를 Byte Order Mark로 사용해서 각 4바이트의 높은 자리부터 쓸지, 낮은 자리부터 쓸지 결정한다. http://flickeringtubelight.net/blog/2004/05/ big-endian-and-little-endian-storage-schemes-how-to-remember/
    • H e l l o , w o r l d ! 48 65 6C 6C 6F 54 20 77 6F 72 6C 64 21 00 00 00 48 00 00 00 65 00 00 00 6C 00 00 00 6C 00 00 00 6F 00 00 00 54 00 00 00 20 00 00 00 77 00 00 00 6F 00 00 00 72 00 00 00 6C 00 00 00 64 00 00 00 21 48 00 00 00 65 00 00 00 6C 00 00 00 6C 00 00 00 6F 00 00 00 54 00 00 00 20 00 00 00 77 00 00 00 6F 00 00 00 72 00 00 00 6C 00 00 00 64 00 00 00 21 00 00 00 UTF-32 BE UTF-32 LE
    • 세 상 아 , 안 녕 ! C138 C0C1 C544 54 20 C548 B155 21 00 00 C1 38 00 00 C0 C1 00 00 C5 44 00 00 00 54 00 00 00 20 00 00 C5 48 00 00 B1 55 00 00 00 21 38 C1 00 00 C1 C0 00 00 44 C5 00 00 54 00 00 00 20 00 00 00 48 C5 00 00 55 B1 00 00 21 00 00 00 UTF-32 BE UTF-32 LE
    • UTF-16 16비트 유니코드 변환 서식 • U+FFFF까지의 기본 다국어 평면 영역은 그대로 16비트 (2바이트) 로 쓴다. • U+100000부터 U+10FFFF까지의 영역은 총 20비트  10+10비트 인코딩 • 110110xxxxxxxxxx 110111xxxxxxxxxx • U+FEFF 코드를 Byte Order Mark로 사용해서 각 2바이트의 높은 자리부터 쓸지, 낮은 자리부터 쓸지 결정한다.
    • H e l l o , w o r l d ! 48 65 6C 6C 6F 54 20 77 6F 72 6C 64 21 00 48 00 65 00 6C 00 6C 00 6F 00 54 00 20 00 77 00 6F 00 72 00 6C 00 64 00 21 48 00 65 00 6C 00 6C 00 6F 00 54 00 20 00 77 00 6F 00 72 00 6C 00 64 00 21 00 UTF-16 BE UTF-16 LE
    • UTF-16 BE UTF-16 LE 세 상 아 , 안 녕 ! C138 C0C1 C544 54 20 C548 B155 21 C1 38 C0 C1 C5 44 00 54 00 20 C5 48 B1 55 00 21 38 C1 C1 C0 44 C5 54 00 20 00 48 C5 55 B1 21 00
    • UTF-8 8비트 유니코드 변환 서식 • U+007F까지의 Basic Latin 영역은 그대로 8비트 (1바이트) 로 쓴다 • U+07FF까지의 영역은 11비트  5+6비트로 인코딩 • 110xxxxx 10xxxxxx • U+FFFF까지의 영역은 16비트  4+6+6비트로 인코딩 • 1110xxxx 10xxxxxx 10xxxxxx • U+1FFFFF까지의 영역은 21비트  3+6+6+6비트로 인코딩 • 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    • H e l l o , w o r l d ! 48 65 6C 6C 6F 54 20 77 6F 72 6C 64 21 48 65 6C 6C 6F 54 20 77 6F 72 6C 64 21 UTF-8
    • EC 84 B8 EC 83 81 EC 95 84 2C 20 EC 95 88 EB 85 95 UTF-8 세 상 아 , 안 녕 ! C138 C0C1 C544 54 20 C548 B155 21
    • 종결 문자로 끝내기 • 전체 내용의 길이를 알 경우, 개별 문자열 길이는 전체 길이를 넘지 않는다. • 딱 한 번 쓰고 읽는 경우 반복적인 처리가 없으므로 종결문자로 끝내는 것도 유용하다. H e l l o , w o r l d !전체 14
    • 보너스: UTF-8 길이 계산 • i  1 • count  0 • while i <= #s • case 0≤si<0x80: • i, count  i+1, count+1 • case 0xC0≤si<0xE0: • ⊨∃si+1 ∧ 0x80≤si+1<0xC0 • i, count  i+1, count+2 • case 0xC0≤si<0xF0: • ⊨∃si+1 ∧ 0x80≤si+1<0xC0 • ⊨∃si+2 ∧ 0x80≤si+2<0xC0 • i, count  i+1, count+3 • case 0xF0≤si<0xF8: • ⊨∃si+1 ∧ 0x80≤si+1<0xC0 • ⊨∃si+2 ∧ 0x80≤si+2<0xC0 • ⊨∃si+3 ∧ 0x80≤si+3<0xC0 • i, count  i+1, count+4 • otherwise: error • end of while 110xxxxx 10xxxxxx 0xxxxxxx 1110xxxx 10xxxxxx 10xxxxxx 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
    • 서식 문자열 문자열을 만드는 데 사용하는 문자열
    • 최대 길이 /채움 문자 • “{0:*^15}”.format(“Hello, world!”) H e l l o , w o r l d !* *
    • 왼쪽 / 가운데 / 오른쪽 / 양쪽 맞춤 • “{0:<15}”.format(“Hello, world!”) • “{0:^15}”.format(“Hello, world!”) • “{0:>15}”.format(“Hello, world!”) • “{0:=15}”.format(“Hello, world!”) H e l l o , w o r l d ! H e l l o , w o r l d ! H e l l o , w o r l d ! H e l l o , w o r l d !
    • 인자 위치 / 인자 이름 • “{1}, {0}!”.format(“world”, “Hello”) • “{h}, {w}!”.format(h: “Hello”, w: “world”)
    • 변환 / 동적 객체 처리 • 첫 글자만 대문자로 title case “{:t}, {}!”.format(“hello”, “world”) • 동적 접근 dynamic lookup “{.h}, {.w}!”.format{h = “Hello”, w = “world”}
    • 서식 문자열 구현 시 고려 사항 • 지역화 문자열을 localized string 만들게 되면 언어에 따라 어순이 바뀌므로 인자의 위치를 조정하는 기능은 반드시 필요하다. • 채움 문자/맞춤 처리는 개발 비용에 비해 사용할 곳이 많다. • 별도로 식별자를 할당해서 변환하게 하면 조건부 문자열 처리를 분리하기 좋다. • 동적 처리들은 언어에서 지원하지 않는 한 구현할 필요가 없음
    • 한국어의 조사 처리 링토스 세계여행에서 적용한 서식 문자열을 중심으로
    • 조사 처리? • 바로 앞 글자가 받침으로 끝나는지 여부에 따라 -은/는, -이/가, -을/를 등의 토씨를 알맞게 붙여주는 문제 • 서비스 중인 마비노기의 경우 11:56:06 김동건 [object] 나는 내부적으로 이렇게 쓴다 11:56:13 김동건 [object] 김동건{을} 11:56:18 김동건 [object] 김동건{이} 11:56:23 김동건 [object] 김동건{가} 11:56:29 전형규 [henjeon] 마비1에서 쓰던 방식 11:56:48 김동건 [object] 그때 내가 저렇게 하라고 했을듯 11:56:50 김동건 [object] 과거의 내가
    • 마비노기 방식의 장/단점 • 장점 • 서식 문자열이 짧다: {은} {는} {이} {가} {을} {를} • 단점 • 서식 문자열이 늘어날 때마다 처리 코드를 갱신해야 한다. • 의외로 한국어의 조사 목록 전체를 생각해보면 잘 생각이 나지 않는다.
    • 링토스 세계여행의 방식 • 언어가 C# (Unity) 이므로 C# 서식 문자열 변형 • 후딱 만들어야 하는 것이므로 앞으로도 코드는 잘 안 바뀌게 • 사용자가 직관적으로 사용할 수 있게 “을/를”, “(으)로” 2014-03-11 입니다. 17:25:21 김재석 [gim] 후딱 만들었는데 이거가지고 올해 NDC할까.. 17:25:25 김재석 [gim] 님이 이미지를 전송합니다. 17:25:29 김재석 [gim] @henjeon 17:26:49 전형규 [henjeon] 오 잘되네 17:27:11 전형규 [henjeon] 괄호가 옵션이고 17:27:17 전형규 [henjeon] 슬래시는 선택이죠? 17:27:25 김재석 [gim] 예 17:27:31 김재석 [gim] 국어시간 노테이션대로 17:27:43 전형규 [henjeon] 설마 저거 17:27:46 전형규 [henjeon] 이미 있는거? 17:27:55 김재석 [gim] 아뇨 17:27:57 김재석 [gim] 지금 짠거 17:28:36 전형규 [henjeon] 굳 어디서 날로 먹으려고…….
    • 숫자 처리 • 처리되는 것이 한글 뿐이면 저렴해 보인다. • 0으로 끝나는 숫자는 다행히 모두 받침이 있다. • .net 서식 문자열의 경우 ToString 메소드에 전달해서 다시 각 값을 서식 문자열로 가공하는데, 이 문자들과 구분하기 위해 구분자 ‘-’를 도입 • e.g. {0:c-을/를} 2014-03-12 입니다. 20:41:05 전형규 [henjeon] @김재석 20:41:10 전형규 [henjeon] 영어도 해주 20:41:13 전형규 [henjeon] 숫자도 해주 (32비트 정수형)
    • 외국어 처리 • 정석대로 처리하는 방법 • 사전 기반으로 국제 음성 기호 (IPA) 로 변환 • 국제 음성 기호와 한글 대조표를 참고하여 한글로 전사 • 이렇게 변환된 한글을 기준으로 조사 처리 • 언어학적으로는 적절하지만, • 사전 데이터가 많이 필요 (언어별로) • 조사처리하자고 온라인 게임 만들면 곤란 • 이렇게는 시간 내에 만들 수 없다.
    • 영어 처리 (1) • 외래어 표기법 표기 세칙의 영어 표기를 참고 • 세칙에서 가장 자주 등장하는 받침은 받침 없음이므로 받침이 있는 경우만 정규식으로 처리 • 제1항 무성 파열음 [p] [t] [k] • 제5항 비음 [m] [n] [ŋ] • 제6항 유음 [l] • “([ptk]|m|ng?|l)$” • 여기까지는 쉬워 보인다
    • 영어 처리 (2) • 한 단계 더 들어가면 • 제1항 무성 파열음 [p] [t] [k] • 자음이나 단모음이 아닌 모음 다음의 어말에서는 ‘으’를 붙여 적는다 • 보통 끝의 h는 묵음이지만, • -th [θ], [ð] • 묵음 처리되는 모음이 많다 • 주로 프랑스어 어원의 낱말들 • 모음으로 끝나는 정규식을 새로 만든다.
    • 영어 처리 (3) • 단모음이 아닌 모음 경우마다 처리한다 • 모음이 겹치는 경우 • 묵음이 들어가 장모음이 되는 경우 • 반모음이 결합해 복모음이 되는 경우 • 모음으로 끝나는 것처럼 보이는 받침을 처리한다 • -ole, –ale, -ome, -ume, -tte 등 • 노골적인 프랑스어까지 처리하진 않아도 된다: rendezvous
    • 결과 2014-03-12 입니다. 23:09:12 김재석 [gim] 뭘만드는지 모르겠다 23:09:13 김재석 [gim] 님이 이미지를 전송합니다. 23:10:13 전형규 [henjeon] 이거 23:10:19 전형규 [henjeon] 어셋스토어에 10만원에 팔자 23:10:44 전형규 [henjeon] 는 농담이고 23:10:49 전형규 [henjeon] 깃헙에 올리자 나중에 https://github.com/tcaesvk/KoreanFormatInfo
    • 정리
    • 문자열 / 문자열 만들기 • 문자열을 쓸 곳과 문자열을 쌓아 올릴 곳은 구분해야 한다. • 자주 처리하는 내용은 공간을 더 써서 시간을 아끼고, 읽거나/쓸 때 한 번이지만 끊임없이 늘어나는 내용이면 시간을 약간 더 쓰고 공간을 아끼는 것이 낫다. • 직접 라이브러리를 만들 경우 가급적 용례를 고려해서 선택한다.
    • 서식 문자열 • 지역화 문자열을 만드는 데 쓸 최소한의 조건은 만족해야 한다. • 잘 만들어진 서식 문자열 처리 방법이 있으면 다양하게 확장해서 쓸 수 있다. • 반복적으로 사용되는 형식의 문장은 서식 문자열을 이용해서 문자열 틀로부터 생성하면 문자열 직렬화에서 이득을 볼 수 있다.
    • Q&A twitter:@tcaesvk