pack, unpack
예전부터 이 두가지는 (vec까지 하면 세 가지) 문서를 읽어도 도대체 뭐하자는 건지 알쏭달쏭하던 터였는데, 자꾸 곳곳에서 이 함수를 쓰는 경우를 보게 되는 터라 더는 미루지 못하고 이런 저런 문서를 읽으면서 정리해 둔다.
-
- 1. Pack / Unpack Tutorial - 문서 번역
-
-
- 1.1. 왜 pack과 unpack이 필요한가
-
- 1.2. 정수 포맷
-
- 1.3. 캐릭터 포맷
-
- 1.4. Perl의 pack 함수
-
- 1.5. Perl의 unpack 함수
-
- 1.6. 정수 포맷
-
- 1.7. 스트링 포맷
-
-
- 1.7.1. 'a', 'A', 그리고 'Z' 포맷
-
- 1.7.2. 'b'와 'B' 포맷
-
- 1.7.3. 'h'와 'H' 포맷
-
1.8. 추가 정보
-
- 1.9. 참고
-
- 1.10. 예제 코드
-
2. perlpacktut - Tutorial on pack and unpack - 문서 정리
-
-
- 2.1. 설명 Description
-
- 2.2. 기본 원리 The Basic Prinicple
-
- 2.3. 텍스트 팩킹 Packing Text
-
- 2.4. 수 팩킹 Packing Numbers
-
-
- 2.4.1. 정수 Integers
-
- 2.4.2. 스택 프레임 unpack하기 Unpacking a Stack Frame
-
- 2.4.3. 네트워크 위에서는 달걀을 어느 쪽으로 깨어 먹는가13
-
- 2.4.4. 바이트 순서 변경자 Byte-order modifiers
-
- 2.4.5. 부동소수 Floating point Numbers
-
2.5. Exotic Templates
-
-
- 2.5.1. 비트 스트링 Bit Strings
-
- 2.5.2. Uuencoding
-
- 2.5.3. 합 구하기 Doing Sums
-
- 2.5.4. Unicode
-
- 2.5.5. Another Portable Binary Encoding
-
2.6. Template Grouping
-
- 2.7. 길이와 폭 Lenghts and Widths
-
-
- 2.7.1. 스트링의 길이 String Lenghts
-
- 2.7.2. 동적 템플릿 Dynamic Templates
-
- 2.7.3. 반복 횟수를 세기 Counting Repetitions
-
2.8. C 구조체 Packing and Unpacking C Structures
-
-
- 2.8.1. 열맞추기 The Alignment Pit
-
- 2.8.2. 엔디안 다루기 Dealing with Endian-ness
-
- 2.8.3. 열맞추기, 장면2 Alignment, Take 2
-
- 2.8.4. 열맞추기, 장면3 Alignment, Take 3
-
- 2.8.5. 포인터를 어떻게 사용하는가 Pointers for How to Use Them
-
2.9. Pack 비책들 Pack Recipes
-
- 2.10. 재미있죠? Funnies Section
-
- 2.11. 저자
-
3. perldoc -f pack - 문서 번역
-
- 4. 관련 문서
-
- 5. 기타, comments
-
1. Pack / Unpack Tutorial - 문서 번역
최근에 채팅을 통해 대화를 나누다가 이 문서를 써야겠다는 생각을 하게 되었다. 어떤 초보 프로그래머가 정보를 pack과 unpack을 사용하여 인코드하려고 하였으나, pack과 unpack이 동작하는 원리를 정확히 이해하지 못해 애를 먹고 있었다. 내 경우에는 이 함수들을 사용하는데 아무런 문제가 없었으나, 이는 내가 하드웨어에 대한 배경지식이 있고 어셈블리와 C프로그래밍에 익숙한 상태에서 펄 프로그래밍을 시작했기 때문이다. 최근에 프로그래밍을 시작한 사람들은 아마 그런 저레벨 수준의 내용을 다뤄보지 않았을 것이고 컴퓨터가 데이타를 어떻게 저장하는지 이해하지 못할 듯 하다. 저레벨 수준의 일들을 조금만 이해하면 pack과 unpack을 보다 쉽게 이해할 수 있다.
1.1. 왜 pack과 unpack이 필요한가
Perl은 스트링, 정수, 부동소수 값들을 다룰 수 있다. 때때로 펄 프로그래머가 다른 언어로 작성된 프로그램과 데이타를 교환해야 할 경우가 있다. 이런 다른 언어들은 데이타의 종류가 더 다양하다. 정수값의 범위도 다르다. 고정된 길이의 스트링만 다룰 수 있을 수도 있다 (감히 COBOL을 언급해야 하나?) 때로는, 네트워크를 통해서 다른 머신과 바이너리 데이타를 교환해야 할 수도 있다. 이런 머신들은 word의 사이즈도 다를 수 있고, 값을 저장하는 방식조차 딴판일 수 있다. 여하튼 우리의 데이타를 다른 프로그램 또는 다른 머신이 이해할 수 있는 포맷으로 변경해야 한다. 또한 우리가 받은 응답을 해석할 수도 있어야 한다.
Perl의 pack과 unpack함수는 데이타가 들어있는 버퍼를 템플릿 문자열이 지시하는 대로 읽거나 쓸 수 있게 해 준다. 템플릿 문자열을 사용하여 바이트 오더와 워드 사이즈를 지정하거나 로컬 시스템의 디폴트 값을 쓰도록 지시할 수 있다. 이를 통해 외부 프로그램과 데이타를 매우 유연하게 주고받을 수 있다.
이 모든 것이 어떻게 동작하는지를 알기 위해서는, 컴퓨터가 여러 다른 타입의 정보들을 어떻게 저장하는지를 이해하는 게 좋다.
컴퓨터의 메모리는 바이트의 거대한 배열로 생각할 수 있다. 하나의 바이트는 8개의 비트를 저장하고 0부터 255까지의 무부호 값 또는 -128부터 127까지의 부호형 값을 표현한다. 이런 작은 범위의 값만 가지고는 수많은 연산을 할 수 없기 때문에, 현대의 컴퓨터에 있는 레지스터들의 크기는 바이트보다 크다. 현대의 프로세서들은 대부분 32비트 레지스터를 사용하고, 64비트 레지스터를 사용하는 것들도 있다. 32비트 레지스터는 0부터 4,294,967,295까지의 무부호 값 또는 -2,147,483,648부터 2,147,483,647까지의 값을 저장할 수 있다.
8비트보다 더 큰 값을 메모리에 저장할 때, 이 값은 8비트 단위의 세그먼트로 나뉘어서 저장장치의 연속적인 공간에 저장된다. 어떤 프로세서들은 최상위 비트(most significant bit)를 첫번째 공간에 저장한다. 이것은 "big endian(1)" 포맷이라 부른다. 다른 프로세서들은 최하위 세그먼트를 첫번째 바이트에 저장하고 나머지 세그먼트들을 더 높은 메모리 공간에 저장한다. 이것을 "little endian(2)" 포맷이라 한다.
그림을 보면 더 쉬울 것이다. 어떤 레지스터에 0x12345678 값이 담겨 있고 이 값을 메모리 주소 1000에 저장한다고 하면, 다음과 같이 나타낼 수 있다.
perldoc -f pack 매뉴얼을 봤거나 Programming Perl 책에서 pack 함수를 찾아봤었다면, Template(5) 문자와 그에 대응되는 데이타 타입이 나열된 표를 봤을 것이다. 그 표에는 여러 가지 크기와 바이트 순서의 정수 포맷이 나열되어 있다. 부호형과 무부호형의 경우도 나와 있다.
포맷 | 설명 |
c(6),C(7) | 부호형/무부호형 char (8비트 정수) 값 |
s(8),S(9) | 부호형/무부호형 short, 언제나 16비트 |
l(10),L(11) | 부호형/무부호형 long, 언제나 32비트 |
q(12),Q(13) | 부호형/무부호형 quat (64비트 정수) 값 |
i(14),I(15) | 부호형/무부호형 정수, 해당 기계의 고유 포맷 |
n(16),N(17) | "network"(big endian(18)) 순서의 16/32 비트 값 |
v(19),V(20) | "VAX"(little endian(21)) 순서의 16/32 비트 값 |
s,l,q 포맷은 16,32,64비트의 값을 호스트 머신의 고유한 순서로 pack한다. i 포맷은 값을 호스트 머신의 word 길이에 맞춰서 pack한다. n과 v 포맷은 사용자가 크기와 순서를 지정할 수 있으며, 다른 시스템과 데이타를 교환할 때 유용하다.
string(22)은 캐릭터의 배열로 저장된다. 전통적으로, 각각의 캐릭터는 ASCII나 EDCDIC같은 특정 코딩 시스템을 사용하여 한 바이트에 인코딩된다. Unicode 같은 새로운 인코딩 시스템에서는 캐릭터를 표현하기 위하여 2바이트 이상을 사용하거나 캐릭터마다 서로 다른 길이의 바이트를 사용하기도 한다.
Perl의 pack 함수는 문자열을 처리하기 위해 다음과 같은 템플릿 문자들을 사용한다.
형식 | 설명 |
a(23),A(24) | 널/공백으로 채워지는 스트링 |
b(25),B(26) | 오름차순/내림차순 비트 순서로 된 바이너리 스트링 |
h(27),H(28) | 낮은/높은 니블이 먼저 나오는 16진수 스트링 |
Z(29) | null로 끝나는 스트링 |
스트링은 가장 낮은 주소에 첫번째 문자가 저장되고 주소값이 커지는 방향으로 연속적으로 나머지 문자들이 저장된다.
1.4. Perl의 pack 함수
pack 함수는 템플릿 스트링과, 리스트로 구성된 값들을 인자로 받아서, 템플릿에 명시된 포맷에 맞춰 값들을 저장한 스칼라를 반환한다. 이를 사용하여 C나 다른 언어로 작성된 프로그램이 읽을 수 있는 형식으로 데이타를 작성할 수도 있고 네트워크 소켓을 통해서 원격 시스템에 데이타를 전달할 수도 있다.
템플릿은 위 테이블에 나온 일련의 문자들로 구성된다. 각각의 문자에는 옵션으로 반복 횟수(repeat count(30). 수의 경우) 또는 길이(문자열의 경우)를 뒤에 덧붙일 수 있다. 정수 포맷에서 '*(31)'는 남아 있는 값들에 대해 이 포맷을 사용하라는 의미이다. 스트링 포맷에서는 스트링의 길이를 사용하라는 의미이다.
주인장 보충:
말이 좀 애매하긴 한데, 관련 문서에 있는 aero님의 문서를 참조하면 좀 쉽다.
숫자를 나타내는 c 등에 *가 붙으면, 인자로 주어진 값 리스트의 나머지 원소들을 다 가져와서 pack하게 된다.
$a = pack "c*", "65", "66", "67";
반면에 스트링을 나타내는 a 같은 경우에는 그 문자에 해당하는 원소의 나머지 문자들 전부를 가져온다.
$a = pack "a*", "abc", "def", "ghi";
이제 예제를 하나 만들어보자. 웹 문서의 폼에서 어떤 정보를 수집하여, C로 작성된 백엔드 시스템에 전달하여 처리한다고 가정하자. 이 폼은 직원이 사무용품 지급을 요청할 수 있는 폼이다. 백엔드 시스템은 입력이 다음과 같은 포맷으로 주어져야 한다.
struct SupplyRequest {
time_t request_time;
int employee_id;
char item[32];
short quantity;
short urgent;
};
시스템의 헤더 파일을 봤더니 time_t는 long 타입이다. 백엔드 시스템에 보낼 레코드를 구성하기 위해서, 다음과 같이 할 수 있다.
$rec = pack( "l i Z32 s2", time, $emp_id, $item, $quan, $urgent);
이 템플릿은 "long 하나, int 하나, null로 끝나며 최대길이 32캐릭터인 스트링, short 두 개"를 의미한다.
사원번호 217641(어이! 그건 나잖아!)인 직원이 2003년 1월 1일에 종이클립 두 통을 긴급하게 요청한다면, $rec 변수에는 다음과 같은 값이 담긴다. (첫 줄은 십진수, 두번째 줄은 16진수, 셋째 줄은 해당 값을 문자로 변환할 수 있는 경우 변환된 문자) 각 필드의 경계를 나타내기 위해 파이프 기호를 사용했다.
오프셋 내용 (주소는 좌측에서 우측으로 커짐)
0 160 44 19 62| 41 82 3 0| 98 111 120 101 115 32 111 102
A0 2C 13 3E| 29 52 03 00| 62 6f 78 65 73 20 6f 66
| b o x e s o f
16 32 112 97 112 101 114 99 108 105 112 115 0 0 0 0 0
20 70 61 70 65 72 63 6c 69 70 73 00 00 00 00 00
p a p e r c l i p s
32 0 0 0 0 0 0 0 0| 2 0| 1 0
00 00 00 00 00 00 00 00| 02 00| 01 00
저것들이 어디서 왔는지 살펴 보자. 템플릿의 첫번째 항목은 'l'이고 이건 long 타입의 값을 pack한다. long은 32비트, 4바이트이다. 저장할 값은 time 함수를 사용하여 얻었다. 실제 값은 1041444000 = 0x3e132ca0이다. 이 값이 버퍼의 시작 부분에 어떻게 들어가는지 보라. 내 시스템은 인텔 펜티엄 프로세서가 달려 있고 little endian(32)이 사용된다.
두번째 템플릿 항목은 'i'이다. 이것은 머신 고유의 크기의 정수를 가져온다. 펜티엄은 32비트 프로세서이고 따라서 이번에도 4바이트로 pack한다. 사원번호는 217641이고 16진수로는 0x00035229이다.
세번째 항목은 'Z32'이다. 이것은 null로 끝나는 32캐릭터 필드를 의미한다. 위의 버퍼를 보면 'boxes of paperclips'가 저장되어 있고 32바이트 중 나머지 부분은 0(널 캐릭터)으로 채워져 있는 것을 볼 수 있다.
마지막 항목은 's2'이다. 이것은 두 개의 short값을 필요로 하고 short값은 16비트 정수이다. 이 항목은 pack에 전달된 값 리스트에서 두 개의 값을 가져온다. 16비트는 두 바이트에 저장된다. 첫번째 값은 2이고 두번째 값은 긴급을 1로서 긴급함을 나타낸다. 이 두 값은 버퍼의 마지막 네 바이트를 차지하고 있다.
1.5. Perl의 unpack 함수
우리가 이 웹 어플리케이션을 만드는 동안에, 우리도 모르는 사이에 누군가 백엔드 프로그램을 C에서 Perl로 포팅하고 있었다.(제대로 말을 했어야지!) 그러나 이미 우리가 웹 어플리케이션을 만들어놨기 때문에, 그들은 동일한 데이타 포맷을 사용하면 되겠다고 생각했다. 따라서 그들은 우리가 보낸 데이타를 다시 unpack해야 한다.
unpacK은 pack의 정반대이다. pack은 템플릿 스트링과 값의 리스트를 받아서 스칼라를 반환한다. unpack은 템플릿 스트링과 스칼라를 받아서 값의 리스트를 반환한다.
이론적으로는, 우리가 pack을 사용하여 얻은 스칼라와, pack할 때 사용한 것과 동일한 템플릿을 인자로 준다면, 결과적으로 pack에 인자로 주었던 값의 리스트를 그대로 얻을 수 있어야 한다. 왜 "이론적으로"라고 말했냐 하면, 만일 바이트 순서가 다르거나(big 아니면 little Endian(33)), word 사이즈가 다른(16,32,64 비트) 머신에서 unpack을 한다면, unpack을 pack이 만들어낸 데이타를 다르게 해석할 것이기 때문이다. 우리가 위에서 사용한 포맷들은 전부 우리 머신 고유의 바이트 순서를 사용했고 'i'는 다른 머신에서는 사이즈가 다를 수 있기 때문에, 문제가 발생할 수 있다. 그러나 예를 간단하게 들기 위해서, 백엔드 시스템은 웹 인터페이스 부분과 동일한 머신에서 실행된다고 가정한다.
우리가 작성한 데이타를 unpack하기 위해서, 백엔드 프로그램은 다음과 같은 구문을 사용할 것이다.
($order_time, $monk, $itemname, $quantity, $ignore) =
unpack( "l i Z32 s2", $rec );
여기에 사용된 템플릿이 우리가 pack할 때 사용한 템플릿과 동일하고, 같은 정보가 같은 순서로 반환되는 것에 주목하라.(우리가 pack한 $urgent 변수를 받는데 그들이 $ignore 변수를 사용한 것만 빼면 말이지. 도대체 저 친구들은 무슨 말을 하고 싶은 거지?)
(Integer(34) 포맷, 내지는, 왜 이리 많은 템플릿 타입이 필요하지?)
같은 데이타 타입의 값을 작성하는데 왜 이렇게 많은 방법이 존재하는지 의아할 수 있다. 'i', 'l', 'N', 'V'는 모두 32비트 정수를 버퍼에 기록하는데 사용된다. 그 중 하나를 골라야 하는 이유는? 음, 그건 당신에 정보를 교환할 대상이 무엇이냐에 달려있다.
동일한 머신 위에서 수행되는 프로그램들 사이에 정보를 교환한다면, 'i', 'l', 's', 'q' 또는 무부호형을 의미하는 'I', 'L', 'S', 'Q'를 사용하면 된다. 읽는 프로그램과 쓰는 프로그램이 동일한 시스템 아키텍처 위에서 실행되기 때문에, 이런 고유 포맷을 사용하는 게 낫다.
아키텍처에 따라 파일의 내용의 배치가 달라지는 경우에 그 파일을 읽는 프로그램을 작성하고 있다면, 'n', 'N', 'v', 'V'를 사용하라. 이렇게 하면 프로그램이 어떤 아키텍처 위에서 돌아가더라도 정보를 정확하게 해석할 수 있다. 예를 들어서 'wav' 파일 포맷은 little endian(35)을 쓰는 인텔 프로세서 위에서 돌아가는 윈도우즈에서 정의된 포맷이다. 'wav' 파일의 헤더를 읽으려 하는 경우, 'v'나 'V'를 사용하여 16비트 또는 32비트 값을 읽어야 한다.
'n'과 'N' 포맷은 "네트워크 순서"라 불리는데 이것이 TCP/IP 통신에 사용하기 위해 명세된 순서이기 때문이다. 네트워크 프로그래밍을 한다면 이 포맷을 사용해야 한다.
여러가지 string(36) 포맷들 중에 무엇을 사용해야 할 지 정하는 것은 정수의 경우와 좀 달라진다. 아마도 다른 프로그램이 어떤 언어로 작성되었느냐에 따라서 'a(37)', 'A(38)', 'Z(39)' 중에 하나를 선택하게 될 것이다. 다른 프로그램이 C나 C++로 작성되었다면 'a'나 'Z'를 쓰게 될 것이다. COBOL이나 FORTRAN이라면 'A'가 좋은 선택이 될 것이다.
1.7.1. 'a', 'A', 그리고 'Z' 포맷
pack을 할 때, 'a(40)'와 'Z(41)'는 여분의 공간을 null로 채운다. 'A(42)'는 여분의 공간을 스페이스로 채운다. unpack 할 때는, 'A'는 제일 뒤에 붙은 공백과 널 캐릭터들을 제거하고, 'Z'는 첫번째 널 캐릭터 이하를 전부 제거하고, 'a'는 필드 전체를 고스란히 반환한다.
예:
pack('a8',"hello") produces "hello\0\0\0"
pack('Z8',"hello") produces "hello\0\0\0"
pack('A8',"hello") produces "hello "
unpack('a8',"hello\0\0\0") produces "hello\0\0\0"
unpack('Z8',"hello\0\0\0") produces "hello"
unpack('A8',"hello ") produces "hello"
unpack('A8',"hello\0\0\0") produces "hello"
'b(43)'와 'B(44)' 포맷은 '0'과 '1' 캐릭터들로 구성된 스트링을 바이트 시퀀스로 pack하거나, 바이트 시퀀스를 '0'과 '1' 캐릭터들의 문자열로 unpack한다. pack할 때 Perl은 짝수값의 문자들은 0으로 취급하고 홀수값의 문자들은 1로 취급한다. 두 포맷의 차이점은 각각의 바이트 안에서 비트들의 순서가 다르다는 것이다. 'b'를 사용할 경우, 비트는 오름차순으로 명시된다. 'B'를 사용할 경우는 내림차순으로 명시된다. 카운트 값은 pack할 비트의 수를 나타낸다.
예:
ord(pack('b8','00100110')) produces 100 (4 + 32 + 64)
ord(pack('B8','00100110')) produces 38 (32 + 4 + 2)
'h(45)'와 'H(46)' 포맷은 16진수 숫자들의 스트링을 pack한다. 'h'는 낮은 니블(nybble)을 먼저, 'H'는 높은 니블을 먼저 사용한다. 카운트 값은 pack할 니블의 갯수를 나타낸다. 니블이 무엇인지 궁금하다면, 니블은 바이트의 절반이다.
예:
pack('h4','1234') produces 0x21,0x43
pack('H4','1234') produces 0x12,0x34
Perl 5.8(47)에는 pack과 unpack에 대한 [튜토리얼]이 포함되어 있다. 이 튜토리얼은 이 문서보다 더 깊이 있는 내용을 다루고 있으나, 내용 중 일부는 perl 5.8에 특화된 것일 수 있다. 아직 Perl 5.6(48)을 사용하고 있다면 저 튜토리얼에 있는 설명대로 동작하지 않는 경우는 5.6의 문서를 직접 체크해야 한다.
여기에서 다루지 않은 템플릿 문자들이 더 있다. 또한 ASCII 필드를 읽고 쓰는 방법들과 pack과 unpack을 가지고 할 수 있는 트릭들도 더 있다. 당신의 시스템에 있는 perldoc -f pack 매뉴얼이나 "[Programming Perl]" 서적을 읽어 보라. 무엇보다도, 직접 실험해보는 것을 두려워하지 말라 (잘 돌고 있는 프로그램에 하는 경우는 말고). pack이 데이타를 어떻게 다루는지 이해가 될 때까지, 아래 예제 코드에 있는 DumpString 함수를 사용하여 pack이 반환한 버퍼의 내용을 검사해 보라.
[Programming Perl], Third Edition, Larry Wall, Tom Christiansen, and Jon Orwant, ⓒ 2000, 1996, 1991 O'Reilly & Associates, Inc. ISBN 0-596-00027-8
Thanks to [bart] for [the reference] to the pack/unpack tutorial from perl 5.8(49).
Thanks to [Zaxo] and [jeffa] for reviewing this document and sharing their own efforts at creating a tutorial.
Thanks to [sulfericacid] and [PodMaster] for inspiring this on the CB.
아래의 프로그램은 이 문서에 있는 예제들이 담겨 있다.
#!/usr/bin/perl -w
use strict;
sub DumpString {
my @a = unpack('C*',$_[0]);
my $o = 0;
while (@a) {
my @b = splice @a,0,16;
my @d = map sprintf("%03d",$_), @b;
my @x = map sprintf("%02x",$_), @b;
my $c = substr($_[0],$o,16);
$c =~ s/[[:^print:]]/ /g;
printf "%6d %s\n",$o,join(' ',@d);
print " "x8,join(' ',@x),"\n";
print " "x9,join(' ',split(//,$c)),"\n";
$o += 16;
}
}
my $t = time;
my $emp_id = 217641;
my $item = "boxes of paperclips";
my $quan = 2;
my $urgent = 1;
my $rec = pack( "l i a32 s2", $t, $emp_id, $item, $quan, $urgent);
DumpString($rec);
my ($order_time, $monk, $itemname, $quantity, $ignore) =
unpack( "l i a32 s2", $rec );
print "Order time: ",scalar localtime($order_time),"\n";
print "Placed by monk #$monk for $quantity $itemname\n";
$rec = pack('a8',"hello");
DumpString($rec);
$rec = pack('Z8',"hello");
DumpString($rec);
$rec = pack('A8',"hello");
DumpString($rec);
($rec) = unpack('a8',"hello\0\0\0");
DumpString($rec);
($rec) = unpack('Z8',"hello\0\0\0");
DumpString($rec);
($rec) = unpack('A8',"hello ");
DumpString($rec);
($rec) = unpack('A8',"hello\0\0\0");
DumpString($rec);
$rec = pack('b8',"00100110");
DumpString($rec);
$rec = pack('B8',"00100110");
DumpString($rec);
$rec = pack('h4',"1234");
DumpString($rec);
$rec = pack('H4',"1234");
DumpString($rec);
2. perlpacktut - Tutorial on pack and unpack - 문서 정리
- 원문: perlpacktut
- (이 섹션의 이하의 내용은 원문을 정리한 것이고 주인장의 보충 설명이나 의견은 각주로 달았습니다)
2.1. 설명 Description
pack과 unpack은 데이타를 "Perl에서 값들이 저장되는 방식" 또는 "Perl 프로그램이 사용되는 환경에서 요구하는 잘 정의된 표현법" 사이에서 변환시킨다. 이 변환은 사용자가 정의한 템플릿에 따라서 이뤄진다.
2.2. 기본 원리 The Basic Prinicple
C 언어처럼 변수의 주소와 크기 등을 얻어내어 메모리에 직접 접근할 수는 없지만, pack과 unpack을 사용하여 원하는 작업을 할 수 있다.
- pack : 값들을 바이트 시퀀스로 변환. 이 변환은 템플릿이라 불리는 명세에 의해 이뤄진다.
- unpack : pack의 반대 역할. 바이트 스트링의 내용으로부터 값들을 얻어 낸다.
바이너리로 표현된 값들이 왜 필요한가?
- 파일, 장치, 네트워크 입출력 시에 이런 형태가 요구되거나, 이런 형태를 사용해야 처리가 더 편해지는 경우가 있다.
- Perl 함수로 제공되지 않는 시스템 콜 : syscall 함수는 C 프로그램의 방식으로 저장된 파라메터가 필요하다.
- 텍스트 처리를 매우 간단하게 할 수 있다.
간단한 예:
my ( $hex ) = unpack( 'H*', $mem );
print "$hex\n";
41204d414e204120504c414e20412043414e414c2050414e414d41
- 바이트 시퀀스의 내용을 16진수 표기로 변환한다.
- 'H(50)'는 16진수 한 자리를 변환
- '*(51)'는 남은 것 전부를 사용
반대 동작 - 16진수 숫자들의 스트링을 바이트 덩어리로 압축
my $s = pack( 'H2' x 10, map { "3$_" } ( 0..9 ) );
print "$s\n";
0123456789
2.3. 텍스트 팩킹 Packing Text
다음과 같은 데이타 파일을 읽는 경우 필드를 어떻게 구분할까:
Date |Description | Income|Expenditure
01/24/2001 Ahmed's Camel Emporium 1147.99
01/28/2001 Flea spray 24.99
01/29/2001 Camel rides to tourists 235.00
- split 은 비어 있는 필드를 잡아내지 못하기 때문에 Income과 Expenditure필드를 구분할 수 없다.
- substr:
while (<>) {
my $date = substr($_, 0, 11);
my $desc = substr($_, 12, 27);
my $income = substr($_, 40, 7);
my $expend = substr($_, 52, 7);
...
}
- 필드의 시작위치와 길이를 일일이 손으로 세어가며 계산해 주어야 함. 에러가 나기 쉽다.
- /정규표현식:
while (<>) {
my($date, $desc, $income, $expend) =
m|(\d\d/\d\d/\d{4}) (.{27}) (.{7})(.*)|;
...
}
- 조금 나아졌지만 여전히 유지 보수하기 힘들다.
- pack과 unpack: 폭이 고정된 데이타를 다룰 때 유용하다
while (<>) {
my($date, $desc, $income, $expend) = unpack("A10xA27xA7xA*", $_);
...
}
- 1열부터 10열까지가 date 필드. "캐릭터"를 의미하는 템플릿은 "A(52)", 캐릭터 10개는 "A10"를 의미
my($date) = unpack("A10", $_);
- "x(53)" 는 "앞으로 건너뜀"을 의미.
- "*(54)" 는 "남아 있는 모든 것을 사용하라"는 의미.
- 정규표현식과 비슷해 보이지만, 들어오는 데이타와 매치가 안 되면 프로그램이 에러를 낸다. 주의.
위 예에서 수입, 지출의 합을 구하고, 그 내역을 제일 마지막 행에 추가하는 예:
while (<>) {
my($date, $desc, $income, $expend) = unpack("A10xA27xA7xA*", $_);
$tot_income += $income;
$tot_expend += $expend;
}
$tot_income = sprintf("%.2f", $tot_income);
$tot_expend = sprintf("%.2f", $tot_expend);
$date = POSIX::strftime("%m/%d/%Y", localtime);
print pack("A10xA27xA7xA*", $date, "Totals", $tot_income, $tot_expend);
- 출력을 해 보면 마지막 줄의 필드 사이에 띄어쓰기가 되지 않는다.
- x(55)는 "null byte", 즉 '\0'을 의미
- 각 필드의 폭을 늘려서 공백으로 채워넣자:
print pack("A11 A28 A8 A*", $date, "Totals", $tot_income, $tot_expend);
- "A(56)" 형식은 캐릭터가 없는 곳은 스페이스로 채운다.
- pack에서는 필드를 오른쪽 정렬로 맞출 수는 없다. 마지막 필드는 sprintf를 사용하여 열을 맞춰준다:
$tot_income = sprintf("%.2f", $tot_income);
$tot_expend = sprintf("%12.2f", $tot_expend);
$date = POSIX::strftime("%m/%d/%Y", localtime);
print pack("A11 A28 A8 A*", $date, "Totals", $tot_income, $tot_expend);
01/24/2001 Ahmed's Camel Emporium 1147.99
01/28/2001 Flea spray 24.99
01/29/2001 Camel rides to tourists 1235.00
11/07/2008 Totals 1235.00 1172.98
여기까지의 내용 정리:
- pack 을 사용하여 여러 데이타 조각을 하나의 고정폭 형태로 변환한다. unpack 을 사용하여 하나의 고정폭 형태의 스트링을 여러 데이타 조각들로 변환한다.
- "A(57)"는 "any character"를 의미한다. pack 도중에 더 이상 압축할 대상이 없다면 남은 부분을 공백으로 채운다.
- "x(58)"는 unpack하는 동안에는 "한 바이트 스킵"을 의미한다. pack하는 경우에는 "널 바이트를 추가"한다 - 텍스트를 처리하는 경우라면, 이것은 사용자가 원하는 동작은 아닐 것이다.
- 템플릿 포맷 뒤에 숫자를 덧붙여서 얼마나 많은 캐릭터에 적용할지를 명시한다. "A12"는 "12개의 캐릭터를 취함"을 의미. "x6"은 "6바이트 스킵" 또는 "널 캐릭터, 여섯 번"을 의미.
- 숫자 대신에 "*(59)"을 사용하여 "남은 것들 전부를 소모"하도록 할 수 있다.
경고:
- 복수개의 데이타 조각들을 pack하는 경우, "*"는 단지 "현재 데이타 조각의 모든 것을 소모"하라는 의미이다.
pack("A*A*", $one, $two);
- $one에 담겨있는 모든 내용을 첫번째 "A*"에 압축하고, $two에 담겨있는 모든 내용을 두번째 "A*"에 압축한다.
- 일반적인 원칙: pack하는 경우 각각의 포맷 문자는 하나의 데이타 조각에 대응된다.
2.4. 수 팩킹 Packing Numbers
Integer(60)를 표현하는 바이너리 표현법의 주요 속성:
20302를 16비트 정수형으로 팩킹: "s(63)"
my $ps = pack( 's', 20302 );
- 결과는 2바이트짜리 스트링
- 출력할 경우 시스템의 바이트 순서에 따라서 "ON"또는 "NO"가 출력될 것임. (ASCII를 사용하지 않는다면 다른 것이 출력될 수도 있음)
$ps를 언팩하면 원래의 정수값을 얻음:
my( $s ) = unpack( 's', $ps );
이것 - pack한 결과를 unpack하면 원래의 값을 얻을 수 있는 것 - 은 기본적으로 수와 관련된 모든 템플릿에 해당되나,
- pack된 값이 할당단 바이트 용량을 초과할 경우 상위 비트는 버려짐
- "s(64)"와 같이 부호를 적용하는 템플릿을 사용하여 pack한 경우에 초과된 값이 부호 비트를 세팅하여, unpack했을 때 음수값이 반환될 수 있음
l(65), L(66) : 부호형/무부호형 32비트 정수
q(67), Q(68) : 부호형/무부호형 64비트 정수
i(69), I(70) : 부호형/무부호형 정수를 시스템 고유의 형태(원문: of "local custom" variety)로 저장 : 이런 정수는 최소 32비트를 차지하고, 최대로는 로컬 C 컴파일러에서 sizeof(int)를 수행하여 얻은 리턴값만큼의 바이트를 차지한다.
s,S,l,L,q,Q로 정수를 pack하면 어느 시스템에서 실행하든지 항상 고정된 갯수의 바이트가 나온다. 이것은 어떤 어플리케이션에서는 유용하지만, 데이타 구조를 Perl과 C 프로그램 사이에서 전달하는 경우 (XS 확장을 호출하거나 syscall함수를 호출할 때 발생난다), 또는 바이너리 파일을 읽거나 쓰는 경우 이식성 있는 방법을 제공해주지 못한다. 시스템의 C 컴파일러가 "short", "unsigned long" 등의 코드를 컴파일했을 때 나오는 결과와 동일한 사이즈의 바이트 수를 사용하고 싶은 경우는 다음 표와 같이 느낌표를 붙여서 사용한다.
signed unsigned byte length in C byte length in Perl
s! S! sizeof(short) $Config{shortsize}
i! I! sizeof(int) $Config{intsize}
l! L! sizeof(long) $Config{longsize}
q! Q! sizeof(long long) $Config{longlongsize}
- %Config 해쉬를 사용하고 싶으면 "use Config"를 써서 import할 것
- i!와 L!은 i,L과 동일하며 단지 일관성을 맞추기 위해서 존재한다
2.4.2. 스택 프레임 unpack하기 Unpacking a Stack Frame
프로그램이 실행되는 시스템과 전혀 별개의 아키텍처에서 전달된 바이너리 데이타를 다뤄야 하는 경우 특정한 바이트 순서를 요청해야 한다.
Intel8086 머신에서 스택 프레임의 내용을 담고 있는 24바이트를 다루는 예:
+---------+ +----+----+ +---------+
TOS: | IP | TOS+4:| FL | FH | FLAGS TOS+14:| SI |
+---------+ +----+----+ +---------+
| CS | | AL | AH | AX | DI |
+---------+ +----+----+ +---------+
| BL | BH | BX | BP |
+----+----+ +---------+
| CL | CH | CX | DS |
+----+----+ +---------+
| DL | DH | DX | ES |
+----+----+ +---------+
my( $ip, $cs, $flags, $ax, $bx, $cd, $dx, $si, $di, $bp, $ds, $es ) =
unpack( 'v12', $frame );
- 또는 "C(73)"를 사용하여 FL, FH 등 개별적으로 접근할 수 있는 바이트 레지스터를 unpack :
my( $fl, $fh, $al, $ah, $bl, $bh, $cl, $ch, $dl, $dh ) =
unpack( 'C10', substr( $frame, 4, 10 ) );
- 위 두가지를 동시에 수행하고 싶다면?
- short를 unpack하고, 되돌아와서 다시 두 개의 byte를 unpack
- X(74) - 한 바이트를 되돌림(back up)
my( $ip, $cs,
$flags,$fl,$fh,
$ax,$al,$ah, $bx,$bl,$bh, $cx,$cl,$ch, $dx,$dl,$dh,
$si, $di, $bp, $ds, $es ) =
unpack( 'v2' . ('vXXCC' x 5) . 'v5', $frame );
2.4.3. 네트워크 위에서는 달걀을 어느 쪽으로 깨어 먹는가
big endian(75) 형태의 정수 pack
- n(76) - 16비트
- N(77) - 32비트
이 순서는 표준 "network order"로 택해진 관례이기 때문에, 네트워크를 통하여 바이너리 데이타를 교환할 때는 반드시 이것을 사용할 것.
어떤 메시지의 길이와 그 메시지를 이어서 전송하는 경우의 예:
my $buf = pack( 'N', length( $msg ) ) . $msg;
my $buf = pack( 'NA*', length( $msg ), $msg );
2.4.4. 바이트 순서 변경자 Byte-order modifiers
n(78), N(79), v(80), V(81)는 부호형 정수나 64비트 정수를 다루지 못하는 한계가 있음.
signed big endian(82) 16-bit 정수를 플랫폼에 독립적인 방법으로 unpack하기 위해서는 다음과 같이 해야 한다:
my @data = unpack 's*', pack 'S*', unpack 'n*', $buf;
Perl 5.9.2(83)에서는 원하는 바이트-오더를 명시할 수 있는 방법을 제공한다.
my @data = unpack 's>*', $buf;
이 변경자들은 C 구조체를 다룰 때 더욱 유용하다.
2.4.5. 부동소수 Floating point Numbers
부동소수(Floating Point Number(88)) 관련 템플릿:
- f(89) - 단정도형. 시스템 고유 포맷.
- d(90) - 배정도형. 시스템 고유 포맷.
- D(91) - long double. 시스템이 지원하는 경우에만
- F(92) - Perl 내부에서 사용하는 부동소수형태(NV).
실수값에 대한 네트워크 표현법은 없다. 따라서 상대 시스템이 무엇인지 확실하지 않다면 ASCII 형태로 전송하는 것이 좋다. 더 모험적으로는, 위에 나왔던 바이트 순서 변경자를 부동 소수 코드에도 사용할 수 있다.
2.5. Exotic Templates
2.5.1. 비트 스트링 Bit Strings
'0'과 '1' 캐릭터들로 구성된 스트링과 8개 비트의 그룹인 바이트들의 시퀀스 사이의 변환.
한 바이트의 내용이 비트 스트링으로 표현되는 두 가지 방식:
7 6 5 4 3 2 1 0
+-----------------+
| 1 0 0 0 1 1 0 0 |
+-----------------+
MSB LSB
- 두 가지 비트 스트링 표기가 가능B(93),b(94):
$byte = pack( 'B8', '10001100' );
$byte = pack( 'b8', '00110001' );
pack은 항상 다음 바이트의 경계에서 시작해서 8의 배수만큼 자르며, 필요하다면 0 비트를 추가해서 채워넣는다. 따라서 비트 필드를 pack,unpack 할 수는 없음. 비트 필드를 다루려면 vec 함수를 쓰거나, unpack해서 얻은 비트 스트링에 split, substr, concatenation 등을 사용하여 캐릭터 스트링 차원에서 처리할 것.
unpack 예제 - 상태 레지스터 분해:
+-----------------+-----------------+
| S Z - A - P - C | - - - - O D I T |
+-----------------+-----------------+
MSB LSB MSB LSB
- "-"로 표기된 미사용 비트는 undef에 할당해서 버려버림
($carry, undef, $parity, undef, $auxcarry, undef, $zero, $sign,
$trace, $interrupt, $direction, $overflow) =
split( //, unpack( 'b16', $status ) );
u(95) - "uuencoded string(96)"을 pack함.
ASCII 데이타 이외의 데이타를 지원하지 않는 전송 매체를 사용할 때 쓰던 인코딩 방식이라, 현재는 쓸 필요가 거의 없다.
인코딩 방법:
- 3바이트(24비트)를 취함
- 이것을 4개의 6비트 덩어리로 나누고 각 덩어리에 스페이스에 해당하는 값(0x20)을 더함. (단 원래 바이트가 0x00인 경우는 0x20이 아니라 0x60으로 만듦)
- 4바이트로 이뤄진 그룹들을 60바이트 이하의 라인에 접어 넣음 (Fold groups of 4 bytes into lines no longer than 60 - 이 60이 60바이트인지 60그룹인지 잘 모르겠음)
- 제일 앞에는 원래의 바이트 갯수에 0x20을 더하여 붙이고, 제일 뒤에는 "\n"을 덧붙임
my $uubuf = pack( 'u', $bindat );
u 뒤에 붙는 카운트는 uuencoding된 라인 하나에 삽입할 바이트 수를 설정. 최대값은 디폴트로 45이고, 그 이하의 정수 중 3의 배수로 설정할 수 있다. unpack은 반복 카운트는 무시.
2.5.3. 합 구하기 Doing Sums
'%(97)<number>'
- 다른 템플릿 코드의 prefix로 사용된다.
- pack 함수에서는 전혀 쓰이지 않는다.
- unpack 에서, 데이타 값들의 합을 구해서 number비트의 정수를 만든다.
my $buf = pack( 'iii', 100, 20, 3 );
print unpack( '%32i3', $buf ), "\n";
- 스트링 값에서는 바이트 값들의 합을 반환한다. substr과 ord를 써서 합을 구해야 하는 불편을 덜어준다.
print unpack( '%32A*', "\x01\x10" ), "\n";
- b(98)나 B(99)와 함께 사용할 경우, %는 단순히 비트들을 더한다. 1로 세팅된 비트의 갯수를 셀 때 효과적이다.
my $bitcount = unpack( '%32b*', $mask );
my $evenparity = unpack( '%1b*', $mask );
Perl은 Unicode(100) string(101)을 처리할 때 내부적으로 UTF-8을 사용한다.
'U(102)'
- 유로화 심볼(코드 번호 0x20AC)을 만드는 예:
$UTF8{Euro} = pack( 'U', 0x20AC );
- $UTF8{Euro}는 3바이트를 차지하며 그 내용은 "\xe2\x82\xac"이다.
- 역으로 유니코드 번호를 얻어내는 예:
$Unicode{Euro} = unpack( 'U', $UTF8{Euro} );
- UTF-8 스트링을 pack 또는 unpack:
my $alefbet = pack( 'U*', 0x05d0..0x05ea );
my @hebrew = unpack( 'U*', $utf );
일반적인 경우, Encode::decode_utf8를 사용하여 UTF-8로 인코딩된 바이트 스트링을 Perl 유니코드 스트링으로 디코드하고, Encode::encode_utf8을 써서 Perl 유니코드 스트링을 UTF-8 바이트들로 인코딩하는 것이 좋다. 잘못된 바이트 시퀀스를 처리할 수 있고 인터페이스도 더 친숙하다.
2.5.5. Another Portable Binary Encoding
'w(103)'
- 이식성 있는 바이너리 데이타 인코딩 제공
- BER(Binary Encoded Representation)로 압축된 무부호 정수
- 128진수 형식으로 저장
- 최상위 비트가 먼저
- 자릿수를 최소한으로 사용
- 마지막 바이트를 제외하고 모든 바이트의 최상위 비트를 1로 세팅
my $berbuf = pack( 'w*', 1, 128, 128+1, 128*128+127 );
- 위 코드의 $berbuf 를 덤프해보면 "01 8100 8101 81807F"가 나옴. 마지막 바이트는 항상 128보다 작으므로 unpack 함수는 어디서 멈춰야 할 지 알 수 있음.
2.6. Template Grouping
Perl 5.8(104)부터는 '((105)'와 ')(106)'를 repeat count(107)와 합쳐서 사용할 수 있다.
앞서 봤는 스택 프레임을 unpack하는 예는 다음과 같이 고쳐 쓸 수 있다:
unpack( 'v2 (vXXCC)5 v5', $frame )
또 다른 예:
- @str 배열의 원소 스트링들의 첫 글자들로 이뤄진 스트링 만들기
join( '', map( substr( $_, 0, 1 ), @str ) )
pack( '(A)'.@str, @str )
- "필요한 만큼 반복"을 의미하는 "*"를 써서 더 간단히
pack( '(A)*', @str )
- "A*"라고 쓰면 단지 $str[0]에 담긴 스트링 전체를 pack하는 거에 유의
(일, 월, 년) 형태로 된 날짜 데이타가 @dates 배열에 저장되어 있을 때 이것을 byte, byte, short 정수로 pack 하는 경우:
$pd = pack( '(CCS)*', map( @$_, @dates ) );
길이가 짝수인 스트링 안에 있는 캐릭터들을 둘씩 짝지은 후 맞바꾸는 예:
- x(108)와 X(109)를 사용하여 앞뒤로 스킵하는 방법
$s = pack( '(A)*', unpack( '(xAXXAx)*', $s ) );
- '@(110)'를 사용하여 어떤 오프셋만큼 건너뛸 수 있다. 오프셋 0은 마지막 "("를 만났을 때 있던 지점이다.
$s = pack( '(A)*', unpack( '(@1A @0A @2)*', $s ) );
- 전혀 다른 방향에서 해결: 스트링을 2바이트씩 나누어서 big endian short 형태로 취급하여 unpack한 후 반대 순서로 pack
$s = pack( '(v)*', unpack( '(n)*', $s );
2.7. 길이와 폭 Lenghts and Widths
2.7.1. 스트링의 길이 String Lenghts
string(111)의 끝에 null을 붙이는 방식과, 스트링의 길이를 미리 명시하는 방식을 동시에 사용한 예:
- 발신자/수신자 주소는 널로 끝나는 스트링을 사용, 메시지는 그 길이를 앞에 명시
my $msg = pack( 'Z*Z*CA*', $src, $dst, length( $sm ), $sm );
( $src, $dst, $len, $sm ) = unpack( 'Z*Z*CA*', $msg );
이 때 문제가 있다. $sm 뒤에 다른 필드를 추가할 경우, pack은 문제가 없지만 unpack을 제대로 할 수 없다.
my $msg = pack( 'Z*Z*CA*C', $src, $dst, length( $sm ), $sm, $prio );
( $src, $dst, $len, $sm, $prio ) = unpack( 'Z*Z*CA*C', $msg );
- 'A*'가 나머지 바이트를 전부 사용해 버리기 때문에, $prio는 정의되지 않은 상태로 남게 된다.
'/(112)'로 두 개의 pack 코드를 조합하면 인자 리스트에서 하나의 값에 결합된다:
my $msg = pack( 'Z* Z* C/A* C', $src, $dst, $sm, $prio );
( $src, $dst, $sm, $prio ) = unpack( 'Z* Z* C/A* C', $msg );
- pack에서는, 인자의 길이를 계산하여 첫번째 코드에 맞춰 pack하고, 인자 자체는 슬래쉬 뒤에 있는 코드에 맞춰서 변환한 후 추가한다.
- length 함수를 호출을 따로 할 필요 없다.
- unpack 시에는, 길이를 나타내는 바이트의 값을 사용하여 버퍼에서 읽을 스트링의 끝을 표시한다.
- 이 조합은 두번째 코드가 'a*', 'A*', 'Z*'일 때만 의미가 있기 때문에 그 외의 경우에는 허용되지 않는다.
슬래쉬 앞에 오는 코드는 수를 표현할 수 있는 것이면 아무거나 쓸 수 있다. 수를 바이너리로 pack하는 모든 코드들과, 심지어 'A4'나 'Z*'같은 텍스트 코드도 가능하다:
my $buf = pack( 'A4/A*', "Humpty-Dumpty" );
my $txt = unpack( 'A4/A*', $buf );
'/(113)'는 Perl 5.6(114)에는 구현되지 않았으므로, 구버전 펄에서 동작하게 하려면 일단 unpack('Z* Z* C')를 하여 길이값을 얻어낸 후에, 그 값을 사용해서 스트링을 다시 unpack해야 한다:
my $msg = pack( 'Z* Z* C A* C', $src, $dst, length $sm, $sm, $prio );
( undef, undef, $len) = unpack( 'Z* Z* C', $msg );
($src, $dst, $sm, $prio) = unpack ( "Z* Z* x A$len C", $msg );
2.7.2. 동적 템플릿 Dynamic Templates
pack할 항목들의 길이가 고정되어 있지 않고 '((115)',')(116)','*(117)'를 사용할 수 없는 상황이라면 템플릿을 생성하는 식을 사용해야 한다.
예: 이름이 있는 스트링 값들을 C 프로그램에서 분석할 수 있는 형태로 저장한다. 이름, "=", 널로 끝나는 ASCII 스트링 순서로 각 값들을 나열하고 제일 마지막에는 널 바이트를 종결자로 추가한다.
my $env = pack( '(A*A*Z*)' . keys( %Env ) . 'C',
map( { ( $_, '=', $Env{$_} ) } keys( %Env ) ), 0 );
- map을 호출하여 $env 버퍼에 넣을 항목들을 생성
- 키($_)에 "=" 구분자와 해시 값을 덧붙임
- 이렇게 구성된 한 벌의 데이타가 "A*A*Z" 코드에 맞춰 pack됨
- 키의 갯수(keys 함수가 스칼라 컨텍스트에서 반환한 값)만큼 반복됨
- 제일 마지막에 0을 "C"코드에 맞춰 pack함
역으로 동작시키기 위해서, 먼저 항목들의 갯수를 알아내야 한다:
my $n = $env =~ tr/\0// - 1;
my %env = map( split( /=/, $_ ), unpack( "(Z*)$n", $env ) );
- tr은 널바이트의 숫자를 센다.
- unpack은 이름-값 쌍의 리스트를 반환한다.
- map은 리스트의 각 원소에서 이름과 값을 분리한다.
2.7.3. 반복 횟수를 세기 Counting Repetitions
각 데이타 항목의 끝과 전체 리스트의 끝에다가 끝을 알리는 감시 문자를 저장하는 것보다, 데이타 앞에 카운트를 넣을 수도 있다.
해쉬의 키와 값을 pack하는 예: 각각의 앞에 unsigned short 형의 카운트값을 붙이고, 제일 앞에는 키-값 쌍의 갯수를 저장한다.
my $env = pack( 'S(S/A* S/A*)*', scalar keys( %Env ), %Env );
반복하는 횟수를 '/(118)'를 사용하여 Unpack할 수 있기 때문에, 역으로 변환하는 과정이 매우 간단해진다:
my %env = unpack( 'S/(S/A* S/A*)', $env );
이 예는 pack과 unpack에서 동일한 템플릿을 사용할 수 없는, 드문 경우 중에 한 가지임에 유의할 것. pack에서는 ((119))(120)-그룹의 반복 횟수를 결정할 수 없다.
2.8. C 구조체 Packing and Unpacking C Structures
다뤄야 할 C 구조체가 많고, 모든 템플릿을 직접 만들고 싶지 않다면 Convert::Binary::C 모듈을 알아보라. 이 모듈은 C 소스를 직접 분석할 수도 있고, 이 섹션에 나올 잡다한 것들을 지원할 수 있게 짜여져 있다.
2.8.1. 열맞추기 The Alignment Pit
속도와 메모리 용량간의 균형에서 빠른 속도가 더 우선시되며, 이는 C 컴파일러가 구조체를 저장할 메모리를 할당하는 방식에 영향을 끼친다: 어떤 아키텍처에서는 16비트 또는 32비트 피연산자가 메모리 주소가 짝수,4의 배수,또는 8의 배수인 곳에 정렬되면 메모리 내에서의 이동이나 레지스터와의 교환이 더 빠르게 이뤄진다. 이 경우 C 컴파일러는 구조체에 여분의 바이트를 채워넣는다.
다음 두 개의 C 구조체를 비교해보자.
typedef struct {
char c1;
short s;
char c2;
long l;
} gappy_t;
typedef struct {
long l;
short s;
char c1;
char c2;
} dense_t;
- C 컴파일러는 gappy_t 타입의 변수에는 12바이트를 할당하지만 dense_t의 경우는 8바이트만 할당한다.
0 +4 +8 +12
+--+--+--+--+--+--+--+--+--+--+--+--+
|c1|xx| s |c2|xx|xx|xx| l | xx = fill byte
+--+--+--+--+--+--+--+--+--+--+--+--+
gappy_t
0 +4 +8
+--+--+--+--+--+--+--+--+
| l | h |c1|c2|
+--+--+--+--+--+--+--+--+
dense_t
- pack과 unpack 템플릿은 'x(121)' 코드를 써서 이런 여분의 fill byte를 처리해야 한다.
'x'를 사용하여 정렬을 맞추는 한 가지 예
my $gappy = pack( 'cxs cxxx l!', $c1, $s, $c2, $l );
- 'l(122)'뒤에 '!(123)'를 붙여서, long 정수를 C 컴파일러가 컴파일했을 때와 동일하게 pack 되게 하였다. 그러나 이런 방법도 컴파일러가 위에처럼 정렬을 해 주는 플랫폼에서나 유효하고, 세상 어딘가에는 안 그런 플랫폼도 있다. (Cray의 경우는 short, int, long이 다 8바이트이다)
길이가 긴 구조체의 바이트 수를 세고 정렬을 살펴보는 것은 쉽지 않다.
#include <stdio.h>
#include <stddef.h>
typedef struct {
char fc1;
short fs;
char fc2;
long fl;
} gappy_t;
#define Pt(struct,field,tchar) \
printf( "@%d%s ", offsetof(struct,field), # tchar );
int main() {
Pt( gappy_t, fc1, c );
Pt( gappy_t, fs, s! );
Pt( gappy_t, fc2, c );
Pt( gappy_t, fl, l! );
printf( "\n" );
}
- 위 프로그램의 출력을 템플릿으로 쓸 수 있다:
my $gappy = pack( '@0c @2s! @4c @8l!', $c1, $s, $c2, $l );
- @(124)를 사용하여 pack 버퍼의 첫 부분으로부터의 오프셋을 명시할 수 있다.
오프셋도 'x'도 쓰지 않고 갭을 건너려면
- x!N(125) - "다음 N의 배수 주소에 도달하기 위해 필요한 바이트 수만큼을 건너뛰어라"
my $gappy = pack( 'c x!2 s c x!4 l!', $c1, $s, $c2, $l );
- 위 방법에서는 각 정수들의 길이를 알아야 한다.
- 숫자 대신에 "short 의 길이만큼"이라 명시
- pack 코드를 대괄호로 둘러싸서 표현, [(126)s](127)
my $gappy = pack( 'c x![s] s c x![l!] l!', $c1, $s, $c2, $l );
2.8.2. 엔디안 다루기 Dealing with Endian-ness
바이트 순서가 다른 머신에게 데이타를 넘겨주기 위해 pack하는 경우.
- 먼저 대상 머신에서 데이타 타입들의 크기를 알아야 한다. long이 32비트, short가 16비트인 경우라 하자. 위에서 작성한 템플릿을 다시 쓰면:
my $gappy = pack( 'c x![s] s c x![l] l', $c1, $s, $c2, $l );
- 대상 머신이 little endian(128)을 사용한다면, short와 long멤버가 little endian 으로 저장되게 강제:
my $gappy = pack( 'c x![s] s< c x![l] l<', $c1, $s, $c2, $l );
- 바이트 순서 수정자를 그룹에 대해 지정할 수도 있다:
- 의도를 좀 더 명확히 드러내 주고, 읽거나 보수하기도 쉽다.
my $gappy = pack( '( c x![s] s c x![l] l )<', $c1, $s, $c2, $l );
2.8.3. 열맞추기, 장면2 Alignment, Take 2
구조체의 배열의 경우:
typedef struct {
short count;
char glyph;
} cell_t;
typedef cell_t buffer_t[BUFLEN];
count의 앞, 또는 count와 glyph 사이에 패딩을 할 필요가 없으므로, 간단히 다음과 같이 쓸 지 모른다:
pack( 's!a' x @buffer,
map{ ( $_->{count}, $_->{glyph} ) } @buffer );
- 이것은 3*@buffer개의 바이트를 pack한다. 그러나 buffer_t의 크기는 BUFLEN의 4배인 것으로 밖혀진다.
구조체나 배열의 정렬은, 각 성분의 끝에도 패딩을 해야 할 것인지 고려해야 한다. 올바른 템플릿은 다음과 같다:
pack( 's!ax' x @buffer,
map{ ( $_->{count}, $_->{glyph} ) } @buffer );
2.8.4. 열맞추기, 장면3 Alignment, Take 3
위에 나온 모든 얘기를 고려한다 해도, 또 문제가 있다:
typedef struct {
char foo[2];
} foo_t;
- ANSI 표준에서 위의 구조체의 크기는 변할 수 있다.
- 구조체의 원소들 뿐 아니라 구조체 전체의 정렬도 제약을 받는다.
- 일반적인 경우가 아니라고 생각한다면, 눈에 띄는 휴대폰을 분해해 보라. 많은 휴대폰에는 ARM 코어가 사용되고 있고, ARM에서 sizeof(foo_t)==4 이다.
2.8.5. 포인터를 어떻게 사용하는가 Pointers for How to Use Them
C 구조체를 pack할때 마주치는 두번째 문제가 포인터이다.
호출하려는 함수가 'void *' 타입의 값을 요구한다고 단순히 Perl 변수의 레퍼런스를 줄 수 있는 게 아니다.
'P(129)' - "고정된 길이의 스트링을 가리키는 포인터"를 pack한다.
my $memory = "\x00" x $size;
my $memptr = pack( 'P', $memory );
pack이 반환한 것은 바이트의 시퀀스이고, C 코드가 요구하는 것은 포인터, 즉 어떤 수이다. 따라서 pack이 리턴한 바이트 문자열을 숫자로 된 주소로 바꿔야 한다:
my $ptr = unpack( 'L!', $memptr );
- 이 경우는 포인터를 unsigned long으로 타입변환할 수 있고 그 역도 가능하다고 가정했다. 이 가정이 언제나 옳지는 않다.
포인터를 활용하는 예:
ssize_t read(int fd, void *buf, size_t count);
require 'syscall.ph';
sub cat($){
my $path = shift();
my $size = -s $path;
my $memory = "\x00" x $size;
my $ptr = unpack( 'L', pack( 'P', $memory ) );
open( F, $path ) || die( "$path: cannot open ($!)\n" );
my $fd = fileno(F);
my $res = syscall( &SYS_read, fileno(F), $ptr, $size );
print $memory;
close( F );
}
- 이 예제의 핵심은, 장막 뒤로 기어들어가서 Perl이 잘 보호하고 있는 메모리에 접근할 수 있다는 것이다.
- 주의(중요함): Perl의 syscall 함수는 굳이 이런 번거로운 방법으로 포인터를 만들어서 넘기지 않아도 된다. 그냥 스트링 변수를 넘겨주면 Perl이 변수의 주소를 전달한다.
'P(130)'를 unpack에서 사용하면 어떻게 동작하는가?
- 버퍼에 어떤 포인터가 담겨 있는 것을 unpack한다고 생각해보자. 이게 널 포인터(이 경우는 undef이 반환된다)가 아니라면, 시작 주소를 얻을 수 있다. 그러나 Perl은 그 포인터가 가리키는 "고정 길이 스트링"의 길이를 알 수 없기 때문에, 사용자가 실제 사이즈를 P뒤에 적어 주어야 한다:
my $mem = "abcdefghijklmn";
print unpack( 'P5', pack( 'P', $mem ) );
- 결과적으로, pack은 'P'뒤에 오는 숫자나 '*(131)'를 무시한다.
p(132) - unpack할 때 사용하면, 버퍼에서 얻은 주소에서 시작하는 "널로 끝나는 스트링"을 반환한다.
my $buf = pack( 'p', "abc\x00efhijklmn" );
print unpack( 'p', $buf );
- 좀 혼동의 여지가 있는데, 'P(133)'코드 뒤에 오는 숫자는 길이를 의미하지만, 'p(134)' 코드의 경우는 스트링의 길이를 가지고 길이를 알 수 있기 때문에 코드 뒤에 오는 숫자는 길이가 아니라 repeat count(135)를 의미하게 된다.
주인장의 보충:
"Pack / Unpack Tutorial"의 마지막 예제 코드에 있는 DumpString를 사용하여 덤프를 출력하며 비교해보자.
my $str1 = "ABC\x{41}DEFGHI";
my $str2 = "abc\x{00}defghi";
my $str3 = "123\x{00}567890";
my $pack1 = pack( 'P' , $str1, $str2, $str3 );
my $pack2 = pack( 'P2', $str1, $str2, $str3 );
my $pack3 = pack( 'P*', $str1, $str2, $str3 );
my $pack4 = pack( 'PP', $str1, $str2, $str3 );
my $pack5 = pack( 'P2P3', $str1, $str2, $str3 );
DumpString( $pack1 );
DumpString( $pack2 );
DumpString( $pack3 );
DumpString( $pack4 );
DumpString( $pack5 );
0 048 130 084 009
30 82 54 09
0 T
0 048 130 084 009
30 82 54 09
0 T
0 048 130 084 009
30 82 54 09
0 T
0 048 130 084 009 136 081 084 009
30 82 54 09 88 51 54 09
0 T Q T
0 048 130 084 009 136 081 084 009
30 82 54 09 88 51 54 09
0 T Q T
- 처음 셋은 다 동일하다, 'P'는 해당 변수의 주소를 pack하는 것이라서, "길이"란 게 의미가 없다.
- "PP"로 템플릿을 주었을 때는 $str1과 $str2의 주소가 각각 pack된다.
- "PP"와 "P2P3"도 서로 동일하다.
- 이걸 unpack하자. 리스트의 원소들을 구분하기 위해 "|" 기호로 join하고, 좌우에도 대괄호를 붙여서 공백을 확인할 수 있게 한다.
print "[", join( '|', unpack( 'P' , $pack1 ) ), "]\n";
print "[", join( '|', unpack( 'P2' , $pack2 ) ), "]\n";
print "[", join( '|', unpack( 'P10', $pack2 ) ), "]\n";
print "[", join( '|', unpack( 'P*' , $pack3 ) ), "]\n";
print "[", join( '|', unpack( 'PP' , $pack4 ) ), "]\n";
print "[", join( '|', unpack( 'P2P3' , $pack4 ) ), "]\n";
[A]
[AB]
[ABCDEFGHI]
'P' must have an explicit size in unpack at ./t07.pl line 25.
<- 실제로는 이 시점에서 에러가 나며 프로그램이 종료되므로, 'P*' 라인을 지워야 이하의 출력이 나온다.
[A|a]
[AB|abc]
- "P"는 "P1"로 간주되는 걸 알 수있다.
- "P*"는 시작 주소를 주면서 "길이는 최대"라고 주는 셈이라서 의미가 없다. 런타임 에러가 나면서 프로그램이 종료된다.
- "PP"는 pack되어 있던 두 개의 포인터에서 각각 한 바이트씩을 끄집어낸다.
- "P2P3"는 두 개의 포인터에서 2바이트, 3바이트씩 가져온다.
my $pack1 = pack( 'p' , $str1, $str2, $str3 );
my $pack2 = pack( 'p2', $str1, $str2, $str3 );
my $pack3 = pack( 'p*', $str1, $str2, $str3 );
my $pack4 = pack( 'pp', $str1, $str2, $str3 );
my $pack5 = pack( 'p2p3', $str1, $str2, $str3 );
DumpString( $pack1 );
DumpString( $pack2 );
DumpString( $pack3 );
DumpString( $pack4 );
DumpString( $pack5 );
0 048 130 084 009
30 82 54 09
0 T
0 048 130 084 009 136 081 084 009
30 82 54 09 88 51 54 09
0 T Q T
0 048 130 084 009 136 081 084 009 176 223 084 009
30 82 54 09 88 51 54 09 b0 df 54 09
0 T Q T T
0 048 130 084 009 136 081 084 009
30 82 54 09 88 51 54 09
0 T Q T
0 048 130 084 009 136 081 084 009 176 223 084 009 232 074 083 009
30 82 54 09 88 51 54 09 b0 df 54 09 e8 4a 53 09
0 T Q T T J S
16 232 074 083 009
e8 4a 53 09
J S
- 'p'는 $str1의 포인터를 pack
- 'p2'는 'p'와 다르다. $str1, $str2의 포인터를 pack한다
- 'p*'는 뒤에 오는 리스트 전부를 받아들이니 총 3개의 포인터
- 'pp'는 'p2'와 같다
- 'p2p3'는 좀 묘하다. 'ppppp'와 같은 상황이니 변수 3개의 포인터가 앞에 pack되는 데, 그 뒤에 8바이트가 더 있다. 포인터 두개가 더 들어가 있는 듯 한데, 둘은 같은 값이다.
print "[", join( '|', unpack( 'p' , $pack1 ) ), "]\n";
print "[", join( '|', unpack( 'p2' , $pack2 ) ), "]\n";
print "[", join( '|', unpack( 'p10', $pack2 ) ), "]\n";
print "[", join( '|', unpack( 'p*' , $pack3 ) ), "]\n";
print "[", join( '|', unpack( 'pp' , $pack4 ) ), "]\n";
print "[", join( '|', unpack( 'p2p3' , $pack5 ) ), "]\n";
[ABC]
[ABC|abc]
[ABC|abc]
[ABC|abc|123]
[ABC|abc]
[ABC|abc|123||]
- 'p'는 길이가 명시되지 않아서 널 캐릭터가 있는 곳까지 세 글자를 가져온다.
- 'p2'는 포인터 두개에서 세 글자를 가져온다.
- 'p10'을 해봤자 실제 pack되어 있는 포인터는 두개뿐이니 'p2'와 같은 출력
- 'p*'는 pack되어 있는 모든 포인터에서 스트링을 가져온다.
- 'pp'는 'p2'와 같다
- 'p2p3'는 'ppppp'와 같고, 처음 세 포인터에서 세 글자씩 가져오고, 나머지 두 포인터를 가지고도 unpack하여 원소가 두 개 더 반환되었다. 실제로 뭐를 가리키는지 몰라도, 아무것도 출력되지 않았다.
$x란 변수가 실제로 저장되어 있는데 'p'노 'P' 코드로 pack(..., $x)을 할 때는 신중해야 한다. Perl 내부에서는 변수와 변수의 주소 사이의 관계를 펄만의 비밀스런 방식으로 처리하며, 사용자가 사본을 획득한 것에 대해 그다지 신경쓰지 않는다. 따라서
- 해당 주소의 메모리 사용을 마치기 전에 스코프를 벗어날 (그래서 메모리를 반납할) 변수의 주소를 얻으려 하지 말라.
- 변수의 값을 변경할 Perl 동작을 수행할 때 매우 조심하라. 예를 들어, 변수의 뒤에 뭔가를 덧붙일 경우 저장 공간을 새로 할당해야만 될 때가 있고, 이 경우 사용자가 얻었던 포인터는 무의미한 곳을 가리키게 된다.
- 어떤 Perl 변수가 integer나 double 타입의 수를 저장한 상태에서 그 변수의 주소를 얻을 수 있다고 생각하지 말라! pack('P',$x)는, 마치 사용자가 $x .= '' 같은 코드를 작성한 것처럼, 그 변수의 내부 표현을 스트링 형태로 강제로 바꾼다.
그러나, 스트링 리터럴을 P또는 p로 pack하는 것은 안전하다. 왜냐하면 Perl이 익명 변수를 할당하기 때문이다.
2.9. Pack 비책들 Pack Recipes
pack과 unpack을 사용한 유용한 비책들.
pack( "C4", split /\./, "123.4.5.6" );
unpack( '%32b*', $mask );
$is_little_endian = unpack( 'c', pack( 's', 1 ) );
$is_big_endian = unpack( 'xc', pack( 's', 1 ) );
$bits = unpack( '%32I!', ~0 );
my $timespec = pack( 'L!L!', $secs, $nanosecs );
주인장 보충:
위 예 중에
$bits = unpack( '%32I!', ~0 );
이것은 제대로 동작하지 않는다(적어도 주인장이 테스트한 Perl 5.8.8에서는 그랬다). 아래처럼 바꿔야 한다.
$bits = unpack( '%32B*', pack( 'I!', ~0 ) );
2.10. 재미있죠? Funnies Section
print unpack( 'C', pack( 'x' ) ),
unpack( '%B*', pack( 'A' ) ),
unpack( 'H', pack( 'A' ) ),
unpack( 'A', unpack( 'C', pack( 'A' ) ) ), "\n";
my $advice = pack( 'all u can in a van' );
Simon Cozens and Wolfgang Laun.
3. perldoc -f pack - 문서 번역
pack TEMPLATE, LIST
값들의 LIST를 받아서 TEMPLATE에 의해 주어진 규칙을 사용하여 스트링으로 변환한다. 전형적으로, 변환된 값은 머신-레벨의 표현법으로 나타난다. 예를 들어, 32-비트 머신에서 정수는 네 개의 바이트의 시퀀스로 표현되며, 이 시퀀스는 네 개의 캐릭터의 시퀀스로 변환될 것이다.
TEMPLATE은 값들의 순서와 타입을 지정하는 문자들의 시퀀스이고, 다음과 같은 것들이 있다:
- a(137) - 임의의 바이너리 데이타로 이뤄진 스트링. 널로 채워짐
- A(138) - 텍스트(ASCII) 스트링, 스페이스로 채워짐
- Z(139) - 널로 끝나는 (ASCIZ) 스트링, 널로 채워짐
- b(140) - 비트 스트링 (vec과 같이, 각 바이트 내의 비트 순서는 오름차순.)
- B(141) - 비트 스트링 (각 바이트 내의 비트 순서는 내림차순.)
- h(142) - 16진수 스트링 (낮은 니블이 먼저)
- H(143) - 16진수 스트링 (높은 니블이 먼저)
- c(144) - 부호형 char (8-비트) 값.
- C(145) - 무부호형 char (octet) 값.
- W(146) - 무부호형 char 값. (255보다 큰 값 가능).
- s(147) - 부호형 short (16-비트) 값.
- S(148) - 무부호형 short 값.
- l(149) - 부호형 long (32-비트) 값.
- L(150) - 무부호형 long 값.
- q(151) - 부호형 quad (64-비트) 값.
- Q(152) - 무부호형 quad 값.
- (시스템에서 64-비트 정수를 지원하고, 또한 Perl도 64-비트 정수를 지원하도록 컴파일되어 있어야 한다. 그렇지 않을 경우는 파탈 에러 발생)
- i(153) - 부호형 정수 값
- I(154) - 무부호형 정수 값
- (여기서 말하는 '정수'는 최소 32비트이다. 정확한 사이즈는 C 컴파일러가 'int'타입을 어떻게 취급하느냐에 달려 있다.)
- j(163) - Perl 내부에서 사용하는 부호형 정수 값 (IV).
- J(164) - Perl 내부에서 사용하는 무부호형 정수 값 (UV).
- f(165) - 시스템 고유 포맷으로 된 단정도 부동소수
- d(166) - 시스템 고유 포맷으로 된 배정도 부동소수
- F(167) - 시스템 고유 포맷으로 된, Perl 내부에서 사용하는 부동소수 (NV)
- D(168) - 시스템 고유 포맷으로 된, long double-precision 부동소수
- (long double은 시스템이 지원해야 하고 또한 Perl도 지원하도록 컴파일되어 있어야 한다. 그렇지 않은 경우 파탈 에러 발생)
- p(169) - 널로 끝나는 스트링을 가리키는 포인터
- P(170) - 구조체(고정길이 스트링)를 가리키는 포인터
- u(171) - uuencode(172) 된 스트링
- U(173) - Unicode(174) 캐릭터 번호. Encodes to a character in character mode and UTF-8 (or UTF-EBCDIC in EBCDIC platforms) in byte mode.
- w(175) - BER 압축된 정수 (ASN.1 BER은 아님, 자세한 것은 perlpacktut 문서를 볼 것). 이 바이트 시퀀스는 128진수로 된 무부호형 정수를 표현한다. 이 때 제일 높은 자릿수가 먼저 나오며, 최소한의 자릿수만 사용한다. 마지막 자릿수인 경우만 제외하고 각 자릿수의 8번째 비트(가장 높은 비트)는 1로 세팅된다.
- x(176) - 널 바이트.
- X(177) - 한 바이트 백업
- @(178) - 절대값으로 주어진 위치만큼 널로 채우거나 잘라냄. 위치는 가장 안쪽의 ()-그룹의 시작지점부터 센다.
- .(179) - 값에 명시된 절대적인 위치만큼 널로 채우거나 잘라냄.
- ((180) - ()-그룹의 시작 표시.
아래 있는 변경자를 하나 이상 옵션으로 템플릿 문자 뒤에 붙일 수 있다 (두번째 열에 있는 목록은 해당 변경자를 사용할 수 있는 문자들)
- !(181)
- s S l L i I - 고정된(16-/32-비트) 크기 대신에 머신 고유의 (short, long, int) 크기를 사용하도록 강제함
- x X - x와 X를 열맞춤 명령으로 동작하도록 함
- n N v V - 무부호형 대신 부호형 정수로 취급하도록 함
- @. - pack된 스트링의 내부적인 표현법에서의 바이트 오프셋으로 위치를 명세할 수 있게 함. 효율적이지만 위험하다.
- >(182)
- s S i I l L q Q
- j J f F d D p P
- 해당 타입에 대해 big endian(183)을 사용하도록 강제함. (부등호의 "커다란 끝"이 생성자를 닿아 있음)
- <(184)
- s S i I l L q Q
- j J f F d D p P
- 해당 타입에 대해 little endian(185)을 사용하도록 강제함. (부등호의 "작은 끝"이 생성자에 닿아 있음)
>와 < 변경자는 ()-그룹에도 사용할 수 있다. 이 경우 그 그룹과 그 안의 서브그룹들까지 포함해서 모든 성분들이 특정한 바이트 순서를 따르도록 강제한다.
다음의 규칙들이 적용된다:
- 각 (템플릿)문자는 추가로 반복 카운트(repeat count(186))를 나타내는 숫자가 뒤에 올 수 있다. 'a','A','Z','b','B','h','H','@','.','x','X','P'를 제외하고 나머지 타입들에서는 반복 카운트 숫자에 있는 갯수 만큼의 값들을 LIST에서 가져다 쓴다. 반복 카운트 대신 '*(187)'를 사용하면 남아 있는 아이템 전부를 사용하라는 의미인데, 다음의 경우는 예외이다: '@','x','X'의 경우는 "0"과 같다; '<','>'의 경우에는 스트링이 어떻게 시작하느냐에 따라 다르다; 'u'의 경우에는 1(또는 45, 결과적으로 동일하다)과 같다. 숫자로 이뤄진 반복횟수는 "pack 'C[80]', @arr"과 같이 대괄호 안에 적어줄 수도 있다.
- 숫자로 이뤄진 반복횟수 대신에 대괄호로 둘러싼 템플릿을 적어 줄 수 있다; 이 경우 그 템플릿을 pack한 결과의 길이(바이트 단위)가 카운트 값으로 사용된다. 예를 들어서, "x[L]"은 long만큼 스킵한다(즉 long타입이 차지하는 바이트의 수만큼 스킵); "$t X[$t] $t"는 $t가 unpack하는 대상을 두 번 unpack하게 된다. If the template in brackets contains alignment commands (such as "x![d]" ), its packed length is calculated as if the start of the template has the maximal possible alignment.
- '*'가 'z'와 함께 사용될 경우, 끝에 널 바이트를 추가하게 된다 (따라서 pack된 결과의 길이는 pack하는 대상 항목에 대해 length 함수를 수행한 결과보다 1이 더 클 것이다)
- 반복 카운트가 '@'와 함께 쓰일 경우, 가장 안쪽의 ()그룹의 시작 지점으로부터의 오프셋을 의미한다.
- When used with ., the repeat count is used to determine the starting position from where the value offset is calculated. If the repeat count is 0, it's relative to the current position. If the repeat count is * , the offset is relative to the start of the packed string. And if its an integer n the offset is relative to the start of the n-th innermost () group (or the start of the string if n is bigger then the group level).
- The repeat count for u is interpreted as the maximal number of bytes to encode per line of output, with 0, 1 and 2 replaced by 45. The repeat count should not be more than 65.
- The a , A , and Z types gobble just one value, but pack it as a string of length count, padding with nulls or spaces as necessary. When unpacking, A strips trailing whitespace and nulls, Z strips everything after the first null, and a returns data verbatim.
- If the value-to-pack is too long, it is truncated. If too long and an explicit count is provided, Z packs only $count-1 bytes, followed by a null byte. Thus Z always packs a trailing null (except when the count is 0).
- Likewise, the b and B fields pack a string that many bits long. Each character of the input field of pack() generates 1 bit of the result. Each result bit is based on the least-significant bit of the corresponding input character, i.e., on ord($char)%2. In particular, characters "0" and "1" generate bits 0 and 1, as do characters "\0" and "\1" .
- Starting from the beginning of the input string of pack(), each 8-tuple of characters is converted to 1 character of output. With format b the first character of the 8-tuple determines the least-significant bit of a character, and with format B it determines the most-significant bit of a character.
- If the length of the input string is not exactly divisible by 8, the remainder is packed as if the input string were padded by null characters at the end. Similarly, during unpack()ing the "extra" bits are ignored.
- If the input string of pack() is longer than needed, extra characters are ignored. A * for the repeat count of pack() means to use all the characters of the input field. On unpack()ing the bits are converted to a string of "0" s and "1" s.
- The h and H fields pack a string that many nybbles (4-bit groups, representable as hexadecimal digits, 0-9a-f) long.
- Each character of the input field of pack() generates 4 bits of the result. For non-alphabetical characters the result is based on the 4 least-significant bits of the input character, i.e., on ord($char)%16. In particular, characters "0" and "1" generate nybbles 0 and 1, as do bytes "\0" and "\1" . For characters "a".."f" and "A".."F" the result is compatible with the usual hexadecimal digits, so that "a" and "A" both generate the nybble 0xa==10 . The result for characters "g".."z" and "G".."Z" is not well-defined.
- Starting from the beginning of the input string of pack(), each pair of characters is converted to 1 character of output. With format h the first character of the pair determines the least-significant nybble of the output character, and with format H it determines the most-significant nybble.
- If the length of the input string is not even, it behaves as if padded by a null character at the end. Similarly, during unpack()ing the "extra" nybbles are ignored.
- If the input string of pack() is longer than needed, extra characters are ignored. A * for the repeat count of pack() means to use all the characters of the input field. On unpack()ing the nybbles are converted to a string of hexadecimal digits.
- The p type packs a pointer to a null-terminated string. You are responsible for ensuring the string is not a temporary value (which can potentially get deallocated before you get around to using the packed result). The P type packs a pointer to a structure of the size indicated by the length. A NULL pointer is created if the corresponding value for p or P is undef, similarly for unpack().
- If your system has a strange pointer size (i.e. a pointer is neither as big as an int nor as big as a long), it may not be possible to pack or unpack pointers in big- or little-endian byte order. Attempting to do so will result in a fatal error.
- The / template character allows packing and unpacking of a sequence of items where the packed structure contains a packed item count followed by the packed items themselves.
- For pack you write length-item/sequence-item and the length-item describes how the length value is packed. The ones likely to be of most use are integer-packing ones like n (for Java strings), w (for ASN.1 or SNMP) and N (for Sun XDR).
- For pack, the sequence-item may have a repeat count, in which case the minimum of that and the number of available items is used as argument for the length-item. If it has no repeat count or uses a '*', the number of available items is used.
- For unpack an internal stack of integer arguments unpacked so far is used. You write /sequence-item and the repeat count is obtained by popping off the last element from the stack. The sequence-item must not have a repeat count.
- If the sequence-item refers to a string type ("A" , "a" or "Z" ), the length-item is a string length, not a number of strings. If there is an explicit repeat count for pack, the packed string will be adjusted to that given length.
unpack 'W/a', "\04Gurusamy"; gives ('Guru')
unpack 'a3/A A*', '007 Bond J '; gives (' Bond', 'J')
unpack 'a3 x2 /A A*', '007: Bond, J.'; gives ('Bond, J', '.')
pack 'n/a* w/a','hello,','world'; gives "\000\006hello,\005world"
pack 'a/W2', ord('a') .. ord('z'); gives '2ab'
- The length-item is not returned explicitly from unpack.
- Adding a count to the length-item letter is unlikely to do anything useful, unless that letter is A , a or Z . Packing with a length-item of a or Z may introduce "\000" characters, which Perl does not regard as legal in numeric strings.
- The integer types s, S , l , and L may be followed by a ! modifier to signify native shorts or longs--as you can see from above for example a bare l does mean exactly 32 bits, the native long (as seen by the local C compiler) may be larger. This is an issue mainly in 64-bit platforms. You can see whether using ! makes any difference by
print length(pack("s")), " ", length(pack("s!")), "\n";
print length(pack("l")), " ", length(pack("l!")), "\n";
- i! and I! also work but only because of completeness; they are identical to i and I .
- The actual sizes (in bytes) of native shorts, ints, longs, and long longs on the platform where Perl was built are also available via Config:
use Config;
print $Config{shortsize}, "\n";
print $Config{intsize}, "\n";
print $Config{longsize}, "\n";
print $Config{longlongsize}, "\n";
- (The $Config{longlongsize} will be undefined if your system does not support long longs.)
- The integer formats s, S , i , I , l , L , j , and J are inherently non-portable between processors and operating systems because they obey the native byteorder and endianness. For example a 4-byte integer 0x12345678 (305419896 decimal) would be ordered natively (arranged in and handled by the CPU registers) into bytes as
0x12 0x34 0x56 0x78 # big-endian
0x78 0x56 0x34 0x12 # little-endian
- Basically, the Intel and VAX CPUs are little-endian, while everybody else, for example Motorola m68k/88k, PPC, Sparc, HP PA, Power, and Cray are big-endian. Alpha and MIPS can be either: Digital/Compaq used/uses them in little-endian mode; SGI/Cray uses them in big-endian mode.
- The names `big-endian' and `little-endian' are comic references to the classic "Gulliver's Travels" (via the paper "On Holy Wars and a Plea for Peace" by Danny Cohen, USC/ISI IEN 137, April 1, 1980) and the egg-eating habits of the Lilliputians.
- Some systems may have even weirder byte orders such as
0x56 0x78 0x12 0x34
0x34 0x12 0x78 0x56
- You can see your system's preference with
print join(" ", map { sprintf "%#02x", $_ }
unpack("W*",pack("L",0x12345678))), "\n";
- The byteorder on the platform where Perl was built is also available via Config:
use Config;
print $Config{byteorder}, "\n";
- Byteorders '1234' and '12345678' are little-endian, '4321' and '87654321' are big-endian.
- If you want portable packed integers you can either use the formats n , N , v , and V , or you can use the > and < modifiers. These modifiers are only available as of perl 5.9.2. See also perlport.
- All integer and floating point formats as well as p and P and () -groups may be followed by the > or < modifiers to force big- or little- endian byte-order, respectively. This is especially useful, since n , N , v and V don't cover signed integers, 64-bit integers and floating point values. However, there are some things to keep in mind.
- Exchanging signed integers between different platforms only works if all platforms store them in the same format. Most platforms store signed integers in two's complement, so usually this is not an issue.
- The > or < modifiers can only be used on floating point formats on big- or little-endian machines. Otherwise, attempting to do so will result in a fatal error.
- Forcing big- or little-endian byte-order on floating point values for data exchange can only work if all platforms are using the same binary representation (e.g. IEEE floating point format). Even if all platforms are using IEEE, there may be subtle differences. Being able to use > or < on floating point values can be very useful, but also very dangerous if you don't know exactly what you're doing. It is definitely not a general way to portably store floating point values.
- When using > or < on an () -group, this will affect all types inside the group that accept the byte-order modifiers, including all subgroups. It will silently be ignored for all other types. You are not allowed to override the byte-order within a group that already has a byte-order modifier suffix.
- Real numbers (floats and doubles) are in the native machine format only; due to the multiplicity of floating formats around, and the lack of a standard "network" representation, no facility for interchange has been made. This means that packed floating point data written on one machine may not be readable on another - even if both use IEEE floating point arithmetic (as the endian-ness of the memory representation is not part of the IEEE spec). See also perlport.
- If you know exactly what you're doing, you can use the > or < modifiers to force big- or little-endian byte-order on floating point values.
- Note that Perl uses doubles (or long doubles, if configured) internally for all numeric calculation, and converting from double into float and thence back to double again will lose precision (i.e., unpack("f", pack("f", $foo)) will not in general equal $foo).
- Pack and unpack can operate in two modes, character mode (C0 mode) where the packed string is processed per character and UTF-8 mode (U0 mode) where the packed string is processed in its UTF-8-encoded Unicode form on a byte by byte basis. Character mode is the default unless the format string starts with an U . You can switch mode at any moment with an explicit C0 or U0 in the format. A mode is in effect until the next mode switch or until the end of the ()-group in which it was entered.
- You must yourself do any alignment or padding by inserting for example enough 'x' es while packing. There is no way to pack() and unpack() could know where the characters are going to or coming from. Therefore pack (and unpack) handle their output and input as flat sequences of characters.
- A ()-group is a sub-TEMPLATE enclosed in parentheses. A group may take a repeat count, both as postfix, and for unpack() also via the / template character. Within each repetition of a group, positioning with @ starts again at 0. Therefore, the result of
pack( '@1A((@2A)@3A)', 'a', 'b', 'c' )
is the string "\0a\0\0bc".
- x and X accept ! modifier. In this case they act as alignment commands: they jump forward/back to the closest position aligned at a multiple of count characters. For example, to pack() or unpack() C's struct {char c; double d; char cc[2]} one may need to use the template W x![d] d W[2] ; this assumes that doubles must be aligned on the double's size.
- For alignment commands count of 0 is equivalent to count of 1; both result in no-ops.
- n , N , v and V accept the ! modifier. In this case they will represent signed 16-/32-bit integers in big-/little-endian order. This is only portable if all platforms sharing the packed data use the same binary representation for signed integers (e.g. all platforms are using two's complement representation).
- A comment in a TEMPLATE starts with # and goes to the end of line. White space may be used to separate pack codes from each other, but modifiers and a repeat count must follow immediately.
- If TEMPLATE requires more arguments to pack() than actually given, pack() assumes additional "" arguments. If TEMPLATE requires fewer arguments to pack() than actually given, extra arguments are ignored.
예:
$foo = pack("WWWW",65,66,67,68);
$foo = pack("W4",65,66,67,68);
$foo = pack("W4",0x24b6,0x24b7,0x24b8,0x24b9);
$foo = pack("U4",0x24b6,0x24b7,0x24b8,0x24b9);
$foo = pack("C0U4",0x24b6,0x24b7,0x24b8,0x24b9);
$foo = pack("ccxxcc",65,66,67,68);
$foo = pack("s2",1,2);
$foo = pack("a4","abcd","x","y","z");
$foo = pack("aaaa","abcd","x","y","z");
$foo = pack("a14","abcdefg");
$foo = pack("i9pl", gmtime);
$utmp_template = "Z8 Z8 Z16 L";
$utmp = pack($utmp_template, @utmp1);
@utmp2 = unpack($utmp_template, $utmp);
sub bintodec {
unpack("N", pack("B32", substr("0" x 32 . shift, -32)));
}
$foo = pack('sx2l', 12, 34);
$bar = pack('s@4l', 12, 34);
$baz = pack('s.l', 12, 4, 34);
$foo = pack('nN', 42, 4711);
$foo = pack('S>L>', 42, 4711);
$foo = pack('s<l<', -42, 4711);
$foo = pack('(sl)<', -42, 4711);
unpack()에서도 일반적으로 동일한 템플릿이 사용된다.
4. 관련 문서
5. 기타, comments
컴퓨터분류
찾아보기:
* 187 | Endian 33 big endian 1, 3, 18, 61, 75, 82, 85, 156, 158, 183 little endian 2, 4, 21, 32, 35, 62, 71, 87, 128, 160, 162, 185 | Floating Point Number 88 | Integer 34, 60 |
Perl version 5.10 136 5.6 48, 114 5.8 47, 49, 104 5.9.2 83 | repeat count 30, 107, 135, 186 | string 22, 36, 96, 101, 111 | Template 5 ! 123, 181 % 97 > 84, 182 < 86, 184 ( 105, 115, 119, 180 ) 106, 116, 120 * 31, 51, 54, 59, 117, 131 . 179 / 112, 113, 118 @ 110, 124, 178 [ 126 ] 127 A 24, 38, 42, 52, 56, 57, 138 a 23, 37, 40, 137 B 26, 44, 93, 99, 141 b 25, 43, 94, 98, 140 C 7, 73, 145 c 6, 144 D 91, 168 d 90, 166 F 92, 167 f 89, 165 H 28, 46, 50, 143 h 27, 45, 142 I 15, 70, 154 i 14, 69, 153 J 164 j 163 L 11, 66, 150 l 10, 65, 122, 149 N 17, 77, 79, 157 n 16, 76, 78, 155 P 129, 130, 133, 170 p 132, 134, 169 Q 13, 68, 152 q 12, 67, 151 S 9, 148 s 8, 63, 64, 147 U 102, 173 u 95, 171 V 20, 81, 161 v 19, 72, 80, 159 W 146 w 103, 175 X 74, 109, 177 x 53, 55, 58, 108, 121, 176 x!N 125 Z 29, 39, 41, 139 |
Unicode 100, 174 | uuencode 172 | | |
big/little endian 이라는 표현은 걸리버 여행기에서 소인국 사람들이 달걀을 깨뜨리는 방법을 논쟁하는 장면에서 유래한 것이거든요. 자세한 내용은 아래 페이지에서 확인하실 수 있습니다.
http://en.wikipedia.org/wiki/Endianness
http://www.ietf.org/rfc/ien/ien137.txt