레이블이 encoding인 게시물을 표시합니다. 모든 게시물 표시
레이블이 encoding인 게시물을 표시합니다. 모든 게시물 표시

2011년 1월 10일 월요일

[Emacs] Encoding 자동 판별 모듈 - Unicad

들어가기 전에 - 인코딩이 왜 문제인가?

한글 Windows 환경(꼭 한글 Windows가 아니더라도 OS 기본 인코딩이 영문 또는 서유럽어가 아닌 경우)에서 소스 코드를 다루는 사람이라면 한번 정도는 겪어보았을 법한 문제가 있습니다. 소스를 text editor로 열었더니 ?로 보이는 부분이 있고, 이 파일을 조금 수정한 다음 저장해서 compile 하면 compile이 실패하거나 성공하더라도 실행 중에 엉뚱한 글자(?)가 보여지는 일 말이죠. 이것은 다중 바이트 인코딩 문자셋(여기서는 euc-kr 또는 cp949)에서 ASCII 코드 확장 영역의 문자 코드를 일부 중복 사용하기 때문에 발생하는 문제입니다.

Windows의 기본 편집기인 메모장(notepad.exe), Scintella 기반의 Notepad++SciTE 등은 물론이고 대다수의 text editor들이 latin-1(iso-8859-1, ASCII 코드 기반의 서유럽어 표현을 위한 문자셋을 정의) 인코딩을 시스템 기본 인코딩과 구별하지 않거나, 또는 구별해준다고 해도 적절히 판별하지 못하기 때문에 한글 Windows 환경에서 latin-1 인코딩으로 작성된 파일을 읽어오면 일부 문자가 ?로 보이는 문제를 겪게 됩니다. (대표적으로 ©, 16진 코드값은 A9)

Notepad++과 몇몇 text editor의 경우는 메뉴에서 파일의 인코딩을 강제 지정해줄 수 있지만, 인코딩이 제대로 판별되지 않았음을 알려주지 않기 때문에 사용자가 주의 깊게 관찰하지 않으면 놓치기 십상이지요.

굳이 위와 같은 특수한 사례를 들지 않는다고 하더라도 이미 Unicode(특히, UTF-8)는 개발 프로젝트의 국제화 및 현지화(Internationalization and localization)와 관련하여 다국어를 지원하는 데 꼭 필요한 인코딩으로 확실하게 자리잡고 있고, 기존에 사용되고 있던 다양한 인코딩의 데이터를 적절히 판별하여 Unicode로 변환하는 것은 매우 중요한 일이 되었습니다. 그리고, 이러한 작업은 전 세계에서 Unicode 하나만 사용하는 날이 올 때까지는 결코 피해갈 수 없는 일이기도 합니다.

그렇다면 Emacs에서는?

Emacs에서는 기본적으로 BOM(byte-order mark)을 가지고 있는 Unicode(UTF-16, UTF-8) 인코딩 정도만 적절히 판단해줍니다. 그래서, EmEditor를 볼 때마다 항상 부러웠던 것이 대부분의 character encoding(문자 인코딩)을 외부 라이브러리나 도구의 도움 없이 에디터 자체적으로 깔끔하게 지원한다는 점이었습니다. 특히 BOM이 없는 Unicode를 포함한 다양한 인코딩에 대한 자동 판별 능력은 타의 추종을 불허할 만큼 탁월하다고 할 수 있지요. 그래서 간혹 파일의 인코딩이 애매한 경우(위에서 언급했던 것처럼 latin-1 인코딩이 사용됐다고 짐작되는 경우 또는 BOM없이 저장된 UTF-16 인코딩 파일의 경우 등)에는 EmEditor Freeware Edition을 사용해서 확인해보곤 했습니다.

그런데, EmEditor 없이도 필요한 만큼 유용하게 사용할 수 있는 문자 인코딩 자동 판별 모듈을 발견하게 되면서 더 이상 EmEditor를 부러워하지 않아도 되게 되었습니다. '필요한 만큼 유용하게'라는 단서를 단 이유는, 발견한 그 자동 판별 모듈이 세상 모든 인코딩을 완벽하게 판별해주지는 못하지만 한글과 영문을 주로 사용하는 사람이 만날 수 있는 대부분의 인코딩은 무리 없이 잘 판별해주기 때문입니다.

그 문자 인코딩 자동 판별 모듈의 이름은 Unicad입니다. 자세한 소개를 하기 전에 우선 Emacs가 파일의 인코딩을 판별하는 방법에 대해 간단하게 살펴보고 넘어가겠습니다.

Emacs의 파일 인코딩 판별 방법

Emacs에는 파일의 인코딩을 판별하는 데 사용되는 find-auto-coding 함수가 있어서 파일을 읽어올 때 이 함수를 통해 파일의 인코딩을 판별하게 됩니다. 실제로는 그 과정이 좀 더 복잡하겠지만, 핵심이 되는 부분만 간추려보면 대략 다음과 같습니다.

auto-coding-alist
      |
      V
auto-coding-regexp-alist
      |
      V
'coding:' 태그 (파일 내용 중)
      |
      V
auto-coding-functions

auto-coding-alist

파일명 규칙인코딩 쌍으로 구성된 cons의 list를 저장하는 변수입니다. 파일의 인코딩을 확인하기 위해 가장 먼저 적용하는 방법으로, 파일명이 규칙과 일치하는 항목이 있을 경우 해당하는 인코딩을 선택하고 일치하는 항목이 없으면 다음 판별 과정으로 넘어갑니다.

제가 사용 중인 Emacs에서 변수값은 다음과 같았습니다. 주로 파일의 확장자와 관련된 내용이 대부분입니다.

(("\\.\\(arc\\|zip\\|lzh\\|lha\\|zoo\\|[jew]ar\\|xpi\\|rar\\|7z\\|ARC\\|ZIP\\|LZH\\|LHA\\|ZOO\\|[JEW]AR\\|XPI\\|RAR\\|7Z\\)\\'" . no-conversion-multibyte)
 ("\\.\\(exe\\|EXE\\)\\'" . no-conversion)
 ("\\.\\(sx[dmicw]\\|odt\\|tar\\|tgz\\)\\'" . no-conversion)
 ("\\.\\(gz\\|Z\\|bz\\|bz2\\|xz\\|gpg\\)\\'" . no-conversion)
 ("\\.\\(jpe?g\\|png\\|gif\\|tiff?\\|p[bpgn]m\\)\\'" . no-conversion)
 ("\\.pdf\\'" . no-conversion)
 ("/#[^/]+#\\'" . emacs-mule))

auto-coding-regexp-alist

파일 시작 바이트 패턴인코딩 쌍으로 구성된 cons의 list를 저장하는 변수입니다. 파일 첫 n 개의 바이트와 일치하는 패턴이 있으면 해당하는 인코딩을 선택하고 일치하는 것이 없으면 다음 판별 과정으로 넘어갑니다.

제가 사용 중인 Emacs에서 변수값은 다음과 같았습니다. Unicode의 BOM과 몇몇 특정 바이너리 형식 파일에 대한 내용을 포함하고 있습니다.

(("\\`BABYL OPTIONS:[   ]*-\\*-[        ]*rmail[        ]*-\\*-" . no-conversion)
 ("\\`\376\377" . utf-16be-with-signature)
 ("\\`\377\376" . utf-16le-with-signature)
 ("\\`\357\273\277" . utf-8-with-signature)
 ("\\`;ELC^T^@^@^@" . emacs-mule))

'coding: ' 태그

파일의 첫 두 줄 안에서 특정 형식의 태그를 찾아 인코딩을 판별하는 방법으로, 태그의 형식은 아래와 같습니다.

-*- ... coding: CODING-SYSTEM; ... -*-

예를 들어, C 소스 파일에 인코딩이 UTF-8임을 명시하고자 한다면 다음과 같이 추가해주면 됩니다.

/* -*- coding: utf-8 -*- */

앞선 다른 방법들과 마찬가지로, 인코딩 지정 태그를 발견하지 못하여 인코딩 판별에 실패했을 경우 다음 판별 과정으로 넘어갑니다.

auto-coding-functions

인코딩 판별 함수의 list를 저장하는 변수입니다. 파일 내용의 전부 또는 일부(Emacs 도움말에 따르면 파일의 첫 1 KB와 끝 3 KB 정도를 포함해야 한다고 되어 있습니다.)에 대해 각 판별 함수를 순서대로 호출하면서 알맞은 인코딩이 발견될 경우 그 인코딩을 선택합니다. 조금 있다가 설명할 Unicad는 이 변수에 고유 판별 함수를 추가하게 됩니다. 그러므로, 위 세 단계에서 적절한 인코딩을 판별해내지 못했을 경우에만 효과가 있는 것이죠.

제가 사용 중인 Emacs에서 변수값은 다음과 같았습니다.

(sgml-xml-auto-coding-function sgml-html-meta-auto-coding-function)

Unicad

이제 본론으로 들어가서, UnicadUniversal Characterset Auto Detector의 줄임말이고, Mozilla Universal Charset Detector를 Emacs 모듈로 이식(porting)한 것이라고 합니다. 이 모듈은 Google Code에서 호스팅되고 있고, EmacsWiki에 별도로 설명하는 페이지를 가지고 있습니다.

Project Link: http://code.google.com/p/unicad/

EmacsWiki Link: http://www.emacswiki.org/cgi-bin/emacs/Unicad

사용 방법은 무척 간단합니다. unicad.el 파일을 다운로드 한 다음 load-path에 경로를 추가하거나 site-lisp(또는 ~/.emacs.d) 폴더에 복사해넣은 뒤 .emacs 파일에 간단하게 한 줄 추가해주면 됩니다.

(require 'unicad)

그리고, 속도 향상을 위해서 byte-compile 해주는 것을 권장합니다. (M-x byte-compile-file RET <path_of_unicad>\unicad.el)

이렇게 하면 모듈이 로드될 때 auto-coding-functions 변수에 unicad-universal-charset-detect 함수가 자동으로 추가되기 때문에 이후 파일을 읽어올 때마다 그 파일의 인코딩을 적절하게 판별해줍니다. Unicad 모듈을 로드한 다음 auto-coding-functions 변수값을 살펴보면 아래와 같이 함수가 추가되어 있는 것을 확인할 수 있습니다.

(unicad-universal-charset-detect sgml-xml-auto-coding-function sgml-html-meta-auto-coding-function)

제가 테스트 해보니 적어도 Unicode(UCS2 with BOM, UTF-8 with/without BOM)와 euc-kr(cp949), latin-1(iso-8859-1) 등은 문제 없이 잘 판별해주었습니다. 이 정도면 필요한 만큼 충분하지요.

단점이라면, 파일을 읽어올 때 인코딩을 판별하기 위한 함수를 한번 더 거치는 것이기 때문에 그 만큼 파일을 여는 속도가 느려진다는 것인데, 거슬릴 정도로 느껴지는 수준은 아니라서 문자 인코딩 자동 판별이라는 이점을 고려할 때 충분히 감수할 수 있다고 봅니다.

Tip - Vim에서 파일 인코딩 자동 판별하기

Vim에서도 필요한 만큼 유용하게 사용할 수 있는 문자 인코딩 자동 판별 방법이 있습니다. 특별히 추가 모듈이나 외부 도구를 사용할 필요 없이 .vimrc(또는 Windows 환경에서 _vimrc) 파일에 간단하게 아래 설정을 추가해주면 됩니다.

" 멀티 바이트를 지원하면
if has("multi_byte")
  " Vim 편집 시 내부 인코딩
  set encoding=utf-8
  " 터미널 인코딩
  set termencoding=utf-8
  " 새로 작성하는 파일 기본 인코딩
  setglobal fileencoding=utf-8
  " 읽어 오는 파일의 인코딩을 판별하는 순서
  set fileencodings=ucs-bom,utf-8,cp949,latin1
endif

위와 같이 설정하면, 파일 편집을 위해 Vim 내부적으로는 UTF-8 인코딩을 사용하고, 읽어오는 파일의 인코딩을 판별하기 위해 ucs-bom(Universal Character Set with byte-order mark), utf-8, euc-kr(cp949), latin1 순으로 적용해봅니다. 각 인코딩을 적용해 파일의 내용을 디코딩할 때 해당 문자셋 테이블을 벗어나는 값이 발견되면 다음 인코딩을 시도하는 방식이기 때문에, 실제 한글 Windows 환경에서 꽤 자주 문제가 되는 latin-1 인코딩 파일도 문제 없이 잘 판별해줍니다.

마치면서...

Text editor는 프로그래머 또는 개발자라는 타이틀을 달고 살아가는 전쟁터에서 가장 기본이 되는 무기입니다. 좋은 무기를 가지는 것이 첫 번째로 중요한 일이라면 그 무기를 충분히 활용할 수 있도록 익숙해지는 것 또한 그 못지 않게 중요한 일입니다. 비록 하나의 무기로 모든 전투에서 승리할 수는 없겠지만, 잘 찾아보면 무기의 성능을 한층 향상시켜줄 수 있는 많은 비법들이 있습니다. Emacs를 위한 문자 인코딩 자동 판별 모듈인 Unicad가 바로 그런 것들 중 하나이겠지요. 지금은 멋진 물건의 발견에 기뻐하는 것으로 만족해야 하지만 좀 더 노력한 뒤에는 멋진 물건을 스스로 만들 수 있게 되리라 기대해봅니다.

2010년 4월 27일 화요일

Emacs와 Mercurial(hg)에 관한 두 가지 팁

몇 년 전까지만 해도 개발 소스 버전 관리를 위해서 주로 CVS나 Subversion을 많이 사용했지만(대형 상용 프로젝트에는 Perforce도 많이 사용됩니다.), 근래에 와서는 DVCS에 대한 관심이 증가하게 되고 여러 가지 장점들이 소개되면서, Mercurial / Git / Bazaar 같은 DVCS도 차츰 많이 사용하는 추세입니다. 특히 Open Source Project 그룹에서 이런 현상이 두드러집니다.
아무튼 최근에 개인적으로 개발 중인 자잘한 소스들을 Mercurial(이하 hg)로 버전 관리하고 있는데, Emacs에 기본 탑재되어 있는 vc-hg를 사용하다 보니 몇 가지 불편한 점이 있더군요.

1. encoding 문제
한글 Windows 환경에서 hg의 기본 encoding은 cp949입니다. 그런데, Emacs에서 buffer의 기본 encoding을 utf-8으로 두고 사용하다 보니, Emacs 상에서 log나 diff를 볼 때 한글로 작성한 내용이 모두 \xxx 같은 형식으로 출력되어 버리는 겁니다.
이 문제를 해결하기 위해서는 .emacs 파일 등에 다음 라인을 추가해주면 됩니다.
(setq vc-hg-global-switches "--encoding=utf-8")
eshell 상에서 hg를 직접 실행하는 경우에는 위 설정이 적용되지 않기 때문에 alias를 사용해서 간단하게 encoding 옵션을 붙여 주면 됩니다.
alias hg 'hg --encoding=utf-8 $*'

2. HTTP 인증 문제
로컬에서만 버전 관리를 하면 크게 상관이 없지만, Bitbucket 같은 호스팅 서비스를 이용하게 될 경우에는 pull 또는 push 같은 작업을 할 때 HTTP 인증이 필요하게 됩니다. 이 때 console 상에서 바로 실행하면 password를 물어보는 prompt가 뜨게 되지만, Emacs eshell이나 shell 상에서는 standard input이 Emacs에 의해 별도 처리되기 때문에 prompt가 뜨는 대신 HTTP 인증이 바로 실패하게 됩니다.
이 문제를 해결하기 위해서는 저장소 폴더(.hg) 내에 있는 hgrc 파일에 인증 정보를 기록해주면 됩니다.
[paths]
default = https://username:password@bitbucket.org/myproject1/
이렇게 하면 인증 정보가 자동으로 전달되기 때문에 매번 password를 입력하는 번거로움도 함께 해결됩니다. 대신, hgrc 파일에 기록된 인증 정보가 타인에게 유출되는 것은 주의할 필요가 있습니다.