RAR vs. Zip format

내가 일상생활에서 흔히 사용하는 압축 포맷은 MS Windows에서는 rar이고, *nix에서는 tar.gz / rar / zip 순서 정도인 것 같다.

MS windows에서야 압축 속도 문제 때문에 1차적으로 rar을 쓰고 있었는데, (물론 압축 푸는 속도에 있어서도 느려터진 알집이랑은 좀 비교하기가 그렇고, 파일 종류에 따라 다르지만 빵집보다도 상당히 빠른 것 같다) 지난 주에 stania가 보내준 파일들 (집에서 쓰는 컴의 윈도우 쪽에서 받았음)을 연구실에서 쓰고있는 리눅스 데스크탑으로 보내면서 다시 좀 생각할 거리가 생겼음

stania군이 보내준 파일들은 zip파일 하나로 압축되어 있었는데 (사실 압축되어 있다기 보다는 묶여있었다 정도지만), CP949 (euc-kr비슷한 형태의 인코딩) 로 파일이름들이 인코딩 되어있다. 문제는 리눅스 데스크탑에서는 UTF-8을 사용하고 있기 때문에 이게 다 깨진다는 것 -_-; *nix에서 흔히 사용되는 zip utility인 Info-ZIP에서는 archive안의 파일이름 인코딩을 인식하는 방법이 없기 때문에, 서로 다른 인코딩을 사용하는 시스템에서는 제대로 된 파일 이름 전송이 불가하다.

실제로 zip 파일 포맷을 살펴보면 개략적으로 다음과 같다. (UPnL Berryz WebShare의 플러그인을 만들면서 얻은 내용들임)

파일 헤더 1 | 파일 데이터 1 | 파일 헤더 2 | 파일 데이터 2 | … | Central Directory (각 파일 메타데이터 / 전체 archive 정보)

문제는 파일 헤더와 central directory에 있는 각 파일 이름에 대한 정보가 각 파일에 대한 정보 플래그(general purpose bit flag라는 2bytes flag가 있고 안쓰는 bit들이 조금 남아있다)에 아무러 추가 지시사항없이 저장된다는 것. 만약 여기에 한 bit을 할당해서 특정 인코딩 (UTF-8도 좋고 그게 안되면 UCS2같은걸 써서라도 다국어 파일이름을 쓸 수 있었으면 좋겠다)임을 표시하지 않게 하거나, extra field라고 추가적인 정보를 저장하기 위한 공간이 있는데 여기에 인코딩 정보가 있다고 알려주는 기능 (실제로 이렇게 추가 구현을 한 3rd party 구현체도 있는 것 같았음)이라도 있었다면 참 좋을텐데.

RAR 파일 포맷은 개략적으로 zip이랑 비슷한데, central directory처럼 전체 파일 내용에 관한 목록이 없고, 중복을 이용해서 헤더 정보를 보호하는게 아니라 (zip은 사실 이런 방식으로 생각해도됨; 데이터 부분에 대해서는 CRC32를 사용하지만 헤더 정보에 대해서는 CRC를 쓰지 않음) 각 헤더 / 데이터 부분마다 별도의 CRC를 사용한다. (데이터에대해서는 CRC32 전체를, 헤더 부분은 CRC32의 하위 2 bytes을 사용함)

그리고 가장 중요한(?) 파일 이름 저장 방식에 있어서 RAR은 다국어 파일 이름을 지원한다. RAR도 기존에는 zip과 같은 방식을 써서인지 단순히 인코딩 정보없이 파일이름을 저장했던 것 같은데, 이 방법도 하위 지원하면서도 단순한 해결책을 제시한다. 우선 zip의 2bytes flag비슷한 header flag란 부분이 있는데 여기에 UNICODE를 사용하는지에 대한 flag가 있다. 이를 설정하면 파일 이름은 다음과 같이 저장된다.

Filename (without charset information) \0 Filename (in UCS2 encoding)

\0 까지만 읽어들이게 되는 UCS2를 지원하지 않는 하위 버젼은 그냥 파일이름만 해석할 것이고, UNICODE 플래그를 해석할 수 있는 경우엔 \0 뒤의 내용도 읽어들일 수 있을 것이다. 이런 방식을 사용하기 때문에 내가 MS Windows에서 압축한 일본어/한국어가 섞인 파일 이름들도 RAR 포맷인 경우엔 내 리눅스 데스크탑에서 멀쩡히 볼 수 있고 zip포맷인 경우 아주 박-_-살이 난다.

사실 UCS2 encoding (Unicode전체를 표현하는게 아니라 그 하위의 일부 문자들(2bytes 고정길이 표현방식; MS Windows에서 OS 내부 파일 시스템 접근 함수들은 이걸 쓴다)이긴 한데 이 이름에도 추가적인 압축(정말 압축인지는 모름(…))을 위해 다음과 같이 추가로 인코딩되긴 한다.

  1. 우선 1 byte을 읽고 2bits씩 쪼개서 4번 flag정보로 사용
  2. flag값이 0~3일 때 다음과 같이 해석
  • 0이면 그냥 1bytes를 그냥 그대로 복사 (대신 UCS2이므로 2bytes으로 커지긴함)
  • 1이면 1 bytes에다가 상위 8th bit을 1로 설정해서 복사 (인코딩된게 0x61 이면 0x 01 61 형식으로 복사됨)
  • 2이면 2 bytes를 동시에 해석함 (인코딩된게 0x 12 0x 34 순이면 0x 34 12로 복사됨; little endian으로 저장된 것처럼 해석한단 소리)
  • 3이면 RLE 비슷한 걸 쓴다(… 길이 정보 + mask 처럼 사용해서 하는 방식)

Berryz WebShare에 추가한 RAR plugin은 압축은 전혀 하지 않고 묶기만 하고, 파일이름은 flag를 전부 2로 셋팅해서 인코딩해서 보내는 걸로해서 구현되어있다. 이미 국경과는 무관하게 정보가 돌아다니는 시대에 살고있다는 자각이 있는 프로그래머라면 적어도 지금 당장 Unicode를 지원하진 않는다곤 해도 지원 해야할 경우를 대비한 무언가 (사용하지 않고 놔둔 flag 공간 + 추가 정보 저장방식 지원 정도만 가지고도 rar에선 다국어 지원을 추가했다)를 준비해놔야 하지 않을까