pack/unpack用法

上一篇 / 下一篇  2008-12-02 12:56:36 / 个人分类:perl

  • 文件版本: V1.0
  • 开发商: 本站原创
  • 文件来源: 本地
  • 界面语言: 简体中文
  • 授权方式: 免费
  • 运行平台: Win9X/Win2000/WinXP
51Testing软件测试网,D} l] ~;Jg'g

Pack 与unpack使用说明:
8rP7wk&QMIO0资料来源摘自perlpacktut,初学perl的时候,想必大家对於pack与unpack都不是很了解,因此本人撷取perldoc里面的文章,为各位简单说明pack与unpack的使用方法:首先定义一下pack与unpack:pack可视为将一系列的片段的数值打包在一起,可用於对dev档案、socket、memory的读写,因为这些需要一块完整的memory,而且需要事先打包成特定格式;而unpack可以视为将将这些完整的memory切割计算,取得我们所需要各部分的Variable。例子如下:51Testing软件测试网'hogBSv6ePg
print pack(“H2”x10,map{ “3$_” } (0..9);
\?"uF)o4i'CT0得到
1s#L(z8dq/~d[Ja0012345678951Testing软件测试网fZSUL2Bn
因为ASCII中30~39代表数字0-9。所以pack後可以得到ascii编码的0->;9,而我们可以利用unpack将string作逆向操作。51Testing软件测试网D k}:o+AU4E1W
print unpack(“H*”,”0123456789”);51Testing软件测试网s*Zk w.@1fv
得到51Testing软件测试网6AEL Z].Z
3031323334353637383951Testing软件测试网p\?9[7n _ q
同样的作法可以用在中文字中,至是你要注意OS的编码格式,UTF-8、GB2312、Big5得到的数值并不会相同。

i"sYT!Pc&S0

E2@{M KT0EJ.L*o)c0Pack与unpack也可以用在对於对於固定格式的文件作处理的情形下:51Testing软件测试网IFd_:d F
[Copy to clipboard] [ - ]CODE:51Testing软件测试网+{ Z k*}%r+[/L9}lK E
    Date        |Descrīption                               | Income|Expenditure
X3|\[/b6I?#q(B0    01/24/2001 Ahmed's Camel Emporium                       1147.9951Testing软件测试网m\,q*Qr!H\
    01/28/2001 Flea spray                                   24.9951Testing软件测试网C.Cc/VN a5c.N&u
    01/29/2001 Camel rides to tourists                235.00 51Testing软件测试网Raas;A_ }
看到上面格式,想必大家很多人都用用substr将固定栏位中的字串取出来,或利用有点点复杂的Regular Expression将上面的文件栏位匹配出来,而实际上我们要将上面栏位一个一个匹配出来可以用一行搞定51Testing软件测试网&F ar f0h5~
例子:51Testing软件测试网(EC,x0VKW:?0qfz
while(<FF>{51Testing软件测试网4f0I{s BB.x
my($date,$desc,$income,$expend) = unpack(“A10xA27xA7xA*”);51Testing软件测试网*bPTA(s~rXpr
}51Testing软件测试网~\c s ~/fz!v
简单说明:A表示ASCII,A10表示10个ASCII character,x表示null byte也等於skip a byte,也就是说我们要跳过一个char(|),然後接着27个ASCII char,然後跳过一个vhar,再接上7个ASCII,在跳过一个char,最後A*表示不管後面char有多少个,我全含括进来。

'Ky(gW?^Vw051Testing软件测试网f"fn-C e'W2L

这样子就可以得到各个栏位相对应的资料,很简单吧! 不需要什麽太多的技巧,一行搞定。之後将income与expend作加总,得到total_income、total_expend,数值,然後再输出的时候,如果也要格式对称,要怎麽办?大多数人都会用format或sprintf,然後一行一行很辛苦的,将结果输出。其实可以简化成:51Testing软件测试网.}*S0u%p[
$income = sprintf("%.2f", $income); # Get them into51Testing软件测试网l+k j/J|7lBp0R
$expend = sprintf("%.2f", $expend); # "financial" format
C"M XU+O0将$income与$expend先格式化,然後同上例将结果输出:51Testing软件测试网 C*[Qa-wHYA
print pack("A11 A28 A8 A*", $date, "Totals", $income, $expend);
(O!j.D+[ H*L0注意:这边多加一个char主要是因为’x’表示的是null byte,所以如果用A7xA27xA7xA*,则输出的字串会连在一起,而多给他一个char,就可以让字串不会都连在一起。
bYnVd0完成程式如下:
B1Q)^^(i;^0[Copy to clipboard] [ - ]CODE:51Testing软件测试网p4EF*M!V5J
#!/usr/bin/perl
|9Z&_1h4Xl{0use POSIX;51Testing软件测试网H/P$h"?)n ~r
open(FF,"tt.txt");
Yn$n2hKZ#G$o0while(<FF>;){
s{gP @ K0  my ($date,$desc,$income,$expend) = unpack("A10xA27xA7xA*",$_);51Testing软件测试网/qI/]7WflW(Jl
     $tot_income += $income;
y4m)?5~j/[2S^ zH{0     $tot_expend += $expend;51Testing软件测试网CaLw_t'XN\a
     $income = sprintf("%.2f", $income);51Testing软件测试网$u|4Ajba
     $expend = sprintf("%12.2f", $expend);
H$\1e[#gE0print pack("A11 A28 A8 A*", $date, "$desc", $income, $expend),"\n";51Testing软件测试网+|+]j8{w
}
7Bt3[CC'`7{YJ0$tot_income = sprintf("%.2f", $tot_income); # Get them into51Testing软件测试网b&Z&O-Pd OZ)U
$tot_expend = sprintf("%12.2f", $tot_expend); # "financial" format51Testing软件测试网T-S1BF,k'N5L{7B
$date = POSIX::strftime("%m/%d/%Y", localtime);
%V#by&K'A;R-xmK0print pack("A11 A28 A8 A*", $date, "Totals", $tot_income, $tot_expend),"\n";51Testing软件测试网v!Z7p'QFO"x%\
整数的pack:
Jv#?0K{X!P!F#A0对於整数需要注意各个OS所定义的int的长度,与各个OS所用的byte order顺序是little-endian还是big-endian,就是高位元在前还是低位元在前的意思。51Testing软件测试网j` J]0[h&P
例子:51Testing软件测试网 Vb2~zVh
my $ps = pack(‘s’,20302);51Testing软件测试网 e6I1V%?/SG[
s:a signed short intger,一般都是16bits=2bytes,如果我们将他print出来(列印这些pack後的字元,实际上是不具任何意义的),会发现他等於NO或ON,然後将$ps在unpack可以得到20302
[hU!?yT&Z0unpack(‘s’,’NO’); ----------------->;2030251Testing软件测试网$B W'R!D }}SEp4Dt;@

51Testing软件测试网 wH;b0OHf1w

注意:如果今天你用”s”,pack一个大於65535的数字,高位元会自动被删除,而得到与你想要的数字不同的结果,这点需要注意。

)e$\ v l I!W.A te0

w/f/z*z|0“l”: signed 32bits integer51Testing软件测试网~fV D7Yo/Q!{ f
“L”:unsigned 32bits integer
lt&F-[_$qQ0“q”:signed 64bits intger
\dncu0“Q”:unsigned 64bits integer
shB*d2cl0“i”:signed integers of “local custom” variety51Testing软件测试网wdQ0y1[-r RT5J+|
“I”:unsigned integers of “local custom” variety
r |%`a8h0这两个”i”与”I”主要与机器的OS定义有关系,其长度等於c中的sizeof(int),如果要用於perl与其他语言的沟通,最好使用这个编码原则。
*gxt4j%k0IN0对照表:(其中%Config要先use Config;才能使用)
4b4v3x/] I$aL6F*[0[Copy to clipboard] [ - ]CODE:
}Czj.r IF1CXu M0   signed unsigned  byte length in C   byte length in Perl       51Testing软件测试网Gg8Ie%giA(D
     s!     S!      sizeof(short)      $Config{shortsize}51Testing软件测试网4G#Sq1KX|,Bp#N-e
     i!     I!      sizeof(int)        $Config{intsize}
H-`5?y8Op_6o If0     l!     L!      sizeof(long)&nb

Y4PL'KA0Dc D8}0

0F~k^@(? L.cR"d `0sp;      $Config{longsize}
GW4`^_#Y(Yu0     q!     Q!      sizeof(longlong)   $Config{longlongsize}  51Testing软件测试网bE'nb,T'o&byY"Hf"s0K
对memory stack作pack:51Testing软件测试网o4}l+KWV
例子如下:memory stack长得像下面这样,接着利用unpack将stack中的各个栏位取出。

1^T7H,W8\051Testing软件测试网zs1QiT9gE$q `
[Copy to clipboard][-]CODE:
j,C#rw3r G0      +---------+           +----+----+                        +---------+
a Q#[F,f7K0TOS: |   IP      |TOS+4:| FL | FH | FLAGS  TOS+14:|   SI    |
)g1~"j cmV[%b_5V0         +---------+           +----+----+                         +---------+
N/V7R9Q'j-w)v0      |   CS    |                 | AL | AH | AX                    |   DI    |
!uSlS!z0      +---------+              +----+----+                         +---------+51Testing软件测试网S#EpHb VM
                                    | BL | BH | BX                     |   BP    |
6NVE+LEXA0                                    +----+----+                         +---------+51Testing软件测试网5Fg+|mh/b.h
                                    | CL | CH | CX                     |   DS    |51Testing软件测试网$w7nh~Z B
                                    +----+----+                         +---------+
;AcAw"m;T9t` @0                                    | DL | DH | DX                       |   ES    |
E nC%Ue^l0                                    +----+----+                          +---------+  

X;rM%A@*n0[Copy to clipboard][-]CODE:51Testing软件测试网0?sx"j1Z!E2l
my( $ip, $cs, $flags, $ax, $bx, $cd, $dx, $si, $di, $bp, $ds, $es ) = unpack( 'v12', $frame );
oX2`c$KT0‘v’ :unsigned short in ‘VAX ‘ order
O%Ja[qf b0这是取出横列的IP、CS、FLAGS、AX、BX、CX、DX、SI、DI、BP、DS、ES
51Testing软件测试网lFYR O:O,x
[Copy to clipboard][-]CODE:
WJV9_F$O0   my( $fl, $fh, $al, $ah, $bl, $bh, $cl, $ch, $dl, $dh ) = unpack( 'C10', substr( $frame, 4, 10 ) );  51Testing软件测试网0H8Sng/z
‘C’:unsign character value,用於bytes读取51Testing软件测试网0^y9^f4t^ W){9k"k3`
这样子分两行很麻烦,所以将他们放在一起得到:51Testing软件测试网1E!XkrTz9M
my( $ip, $cs,
]V7hT:c6h0$flags,$fl,$fh,
9q7E)~ N,B6A0$ax,$al,$ah, $bx,$bl,$bh, $cx,$cl,$ch, $dx,$dl,$dh,
^;Nk8`)PUO%h0$si, $di, $bp, $ds, $es ) =
P(W'f2^nc"{n o0unpack( 'v2' . ('vXXCC' x 5) . 'v5', $frame );
#M'd,J!f Lv^$V7p0‘X’:Backup a byte
x.~{SU(t051Testing软件测试网U7W0^;A3U!~6A-UV,C
网路应用:51Testing软件测试网 R)i7uCq uX _(a
‘n’: An unsigned short in "network" (big-endian) order
#K-s5Ok#y;]*K-Uz0‘N’: An unsigned long in "network" (big-endian) order51Testing软件测试网I C n&dG*^&Z
在作网路连结时,往往需要将长度先送给Server,使其知道後面有多少character要读取,因此如果有一段msg,可以利用下列方式打包:51Testing软件测试网+N?#}6u(B+z'w
my $buf = pack( 'N', length( $msg ) ) . $msg;51Testing软件测试网F ~X2Wn ER
说明:等於「长度」( unsigned long)加上讯息内容,也可简化为:51Testing软件测试网g5J0\I#L;H
my $buf = pack(‘NA*’,length($msg),$msg);51Testing软件测试网] V*Z#} o!q9e!K4[
然後将$buf送至对方server。同样对方可用unpack(‘NA*’,$buf)取得送出的数据。
'R(m8c4fXm8?\}m0Floating Point Numbers:
"]DV_E[2w:W)}N&P0‘f’: float (A single-precision float in the native format)51Testing软件测试网,|uOT/c
‘d’:double(A double-precision float in the native format)
5T;K|^ [0对於浮点数(float point)可以使用f或d来作pack与unpack动作。
$rc:{@9~/maRN$? Y0奇特的例子:51Testing软件测试网{0AU/S0D1G F
Bit Strings:
/q?b{]k0String中都是0或1,对其作pack/unpack的转换,需要注意那一系列的0/1,与每八个bit一个字元的顺序性。假设今天有个string等於 ”10001100”可以用下列方式:51Testing软件测试网r%j}4^ KB T}lT
$byte = pack( 'B8', '10001100' ); # start with MSB
#V/gxotB%^$K0$byte = pack( 'b8', '00110001' ); # start with LSB51Testing软件测试网 zMz.{{(q!`^s!Y
b A bit string (ascending bit order inside each byte, like vec()).(渐增)
2Z)C.Vg?1JB6d0B A bit string (descending bit order inside each byte).(渐减)51Testing软件测试网gYi QV&|zd
如果要对string中的一部份bits作pack/unpack是不可能的,因为pack会从最旁边开始,然後以8个bits为一组,如果不足八个则补0。
51Testing软件测试网 y.FN4e gB5cY]

rdZ#E N&@vUm fCh0[Copy to clipboard][-]CODE:51Testing软件测试网5Y$X{ sl }s_'si
   +-----------------+-----------------+51Testing软件测试网 [ t5g;\9}%Q#Kqn#?
   | S Z - A - P - C | - - - - O D I T |51Testing软件测试网?#X!B_*K-bl:r2Y%T
   +-----------------+-----------------+
cfJn"f$j{0    MSB          

3|#z.D2D$y3n$H!ys4T-d0

;LSB MSB           LSB  
y2@h gd+Tw7l@0例子:如上图中「-」表示保留的bit,不使用。51Testing软件测试网v4`q Kw1r

x%Z6c}/K2f,z/G b;j0将上列这2 bytes 转换成一个string,可以利用’b16’来作:
2`FB:K `/nt }0#!usr/bin/perl
f&] XAx2P0$status = "1101010100001111";
pFCu"tF_-x'J$H0$ps= pack('b16',$status);
9c|4w@X_F0print "$ps\n";51Testing软件测试网%kA3g4v\ UyS
$status=unpack('b16',$ps);
!w9q#he8fq5| i0print "$status\n";51Testing软件测试网8dc ]aok8j
@aa = split(//,$status);51Testing软件测试网?!F6`S ^},zC:Ro
print "@aa\n";51Testing软件测试网8lKx5G-q.]bO
#---简化
$o`y:K+SU0#---可取得各个bit的数值51Testing软件测试网0d2``*B,qwQY
($carry, undef, $parity, undef, $auxcarry, undef, $sign,51Testing软件测试网`'vk4tX#QC q]
$trace, $interrupt, $direction, $overflow) =51Testing软件测试网]U M-K5Y#J4w
split( //, unpack( 'b16', $status ) );51Testing软件测试网4b2hM"y-R"u@&QgF
51Testing软件测试网8?{7j#V)t%WfB
如果使用’b12’则,後面的4个bit会被忽略并不会被使用。
v-G1_E M.I6`#@051Testing软件测试网q]o"](_2Z
Uuencode:(Unix-To-Unix Encode)51Testing软件测试网q/P;U P9_ _&`XD
如果对於UNIX很熟悉的,应该知道uuencode与uudecode是干什麽用的,他可视为早期UNIX之间互相传送资料,所用的一种编码方法,将文件编码,以便利於在早期的网路上传送资料,现在很少使用,但是有时还是有人会用到,编码原理:取出三个bytes,将之切割成6份,每份4个bits,然後在後面填入\x20,如此直到整个文件编码完成。Pack可使用’u’作这件事情。
8n?;iwxZF051Testing软件测试网?k/E@6GIC
my $uubuf = pack( 'u', $bindat );
'Ez` q&mF051Testing软件测试网)[9?(u1zlt-y8l
计算总和:(Do Sums)51Testing软件测试网}Y V;~Y VT
pack中有个特殊的template,%<number>;专门用来计算总和的,但是他不能在pack中使用,而且他只能用於其他数字的前置位置(prefix)。51Testing软件测试网e9G8O$\ PC8w `@y
51Testing软件测试网"}:X \Ac_
用於数字时:
*^jI kV d0my $buf = pack( 'iii', 100, 20, 3 );51Testing软件测试网5[9wd&^2By
print unpack( '%32i3', $buf ), "\n"; # prints 123
U'L/IO.}NZ0用於字串时:
,wlGV1yw u }0print unpack( '%32A*', "\x01\x10" ), "\n"; # prints 17
Z/U E` G0上面两个例子,%会将後面i3、A*加总起来,得到最後结果123与17,这可以用来得到最後的check sum。另外不要太相信他所得到的数值,因为他们无法保证正确(?)。51Testing软件测试网 ev[jMXu
用来取得netmask的bits数目:51Testing软件测试网s9}'a/ju$[QG,P
my $bitcount = unpack( '%32b*', $mask );51Testing软件测试网?I!|!z5Z `Kc
51Testing软件测试网~#A&g|w`/Iv4tH
my $evenparity = unpack( '%1b*', $mask );51Testing软件测试网1r2\7X$N6M P jy
取得evenparity的数值…mask=pack(‘b*’,”11111111111111111…0”);51Testing软件测试网V;S`5{]a n [[
Unicode:
|,Y3@0c)nR0$UTF8{Euro} = pack( 'U', 0x20AC );
Q)U,k U;`*CVq8}y#O051Testing软件测试网'gJg9?C@b
欧洲用字编码\0x20AC,其UTF8的编码字元为$URF{Euro}。
/s/Bt3wj @3SIM0# pack and unpack the Hebrew alphabet
J{U&H$Y'vJ0my $alefbet = pack( 'U*', 0x05d0..0x05ea );
5~7c]-E%Gc#V~0my @hebrew = unpack( 'U*', $utf );51Testing软件测试网3mTl-cm)D
用於pack/unpack unicode编码
qB'rp [{0
+NDD)t;mI0另一种Portable编码:
.w:yyh(}8x7zI3b0w A BER compressed integer. Its bytes represent an unsigned integer in base 128, most significant digit first, with as few digits as possible. Bit eight (the high bit) is set on each byte except the last.51Testing软件测试网d1x:j"h-A+y4ID

3WE7n;xWa4kr/G0my $berbuf = pack( 'w*', 1, 128, 128+1, 128*128+127 );
6ri7]jGTQ A!q0
[(R%W _.o#J2bE0长度与宽度(Length and Width)
]#d8Z,tB#_3d3e{;J0字串长度(String Length)
PQ]gc P*]0在传送网路资料时,往往需要将资料的长度放在封包的标头处,让对方Server知道後续还有多少资料需要处理:下列例子为两个null terminated字串加上资料长度,再加上资料,得到最後要送出的资料。51Testing软件测试网)g9~4F"\:HY2Qw&Gc%y
Z:A null terminated (ASCIZ) string, will be null padded.
;Z"~9VQT0my $msg = pack( 'Z*Z*CA*', $src, $dst, length( $sm ), $sm );51Testing软件测试网viy I OH7Jmx

s.G2Qfg0而要取出资料可以用下列方式:51Testing软件测试网m-Z(\(|Zl)^'\3x
( $src, $dst, $len, $sm ) = unpack( 'Z*Z*CA*', $msg );
/cz7U pE0
Wr6N*E|T.eCf.o051Testing软件测试网sF xBQ d
但是,如果我们在pack後面加上另一个C,则在unpack时,会无法正确得到资料。因为A*会把所有的剩余character都抓给$sm,而使得$prio变成无定义。
&|7Rv pX~.S D/o0# pack a message51Testing软件测试网0MX.{/\4LU$x ]J$a
my $msg = pack( 'Z*Z*CA*C', $src, $dst, length( $sm ), $sm, $prio );
UH v+J'p M8j&v E0
"G/T8S~ Lua5t0# unpack fails - $prio remains undefined!
6y$@]\-l"u#r0( $src, $dst, $len, $sm, $prio ) = unpack(51Testing软件测试网i,e/q#M\!})K Y H&o+b
51Testing软件测试网3{"X ` C8@0I*B5kf
因此可以使用下列方式取得资料:
.] w:J-\i UKY0# pack a message: ASCIIZ, ASCIIZ, length/string, byte
)S#| \9NbP;~F,D0my $msg = pack( 'Z* Z* C/A* C', $src, $dst, $sm, $prio );51Testing软件测试网cUB4kT_9J8a

#L Vn8Y/H,qe I0# unpack51Testing软件测试网m;]8t%X&@*p.p.nO/A
( $src, $dst, $sm, $prio ) = unpack( 'Z* Z* C/A* C', $msg );
6MOD-o:[+yb ez~9T051Testing软件测试网F\O[N;{'{G?k
加上”/”符号,可以使得perl在pack A*会记住$sm的长度,然後後续的$prio就可以区别出来储存,而在unpack时,就可以依照$sm的长度,而将$sm的资料取出,剩下来的就是$prio的资料。”/”只有在後面接上A*、a*、Z*时有意义。51Testing软件测试网"l'hf}:]+t,C wQ~
“/”表示後面A*的长度,占1个byte51Testing软件测试网r,E,G(N/n2a6oR
# pack a message: ASCIIZ, ASCIIZ, length/string, byte
[A0V"ay bT2__0my $msg = pack( 'Z* Z* C/A* C', $src, $dst, $sm, $prio );51Testing软件测试网?9E$pO-CEB9u(F*^

`p+n&E^v6J8Y0# unpack
&E?M8qQ!m6L,A0( $src, $dst, $sm, $prio ) = unpack( 'Z* Z* C/A* C', $msg );51Testing软件测试网1a(X/pT)W HA2wL
51Testing软件测试网B(RdI@@Qu
“/”可以代表任何数字,用以表示其後A*的长度51Testing软件测试网E?:Q[#{1_Un%~4|&\
# pack/unpack a string preceded by its length in ASCII51Testing软件测试网/KPH,k8B6K1Q
my $buf = pack( 'A4/A*', "Humpty-Dumpty" );
[ T7N,\kk#j Bt@0# unpack $buf: '13 Humpty-Dumpty'
RXv.d {(Cn0my $txt = unpack( 'A4/A*', $buf );
ra K&M%K4`0h9C051Testing软件测试网p ce6|,`5@nL\
“/”在perl 5.6以後才出现,之前版本并不支援。针对旧版本需要做如下修改已取得长度。51Testing软件测试网8sq7|M2hY)q5]
# pack a message: ASCIIZ, ASCIIZ, length, string, byte (5.005 compatible)51Testing软件测试网1Q.Abb9o
my $msg = pack( 'Z* Z* C A* C', $src, $dst, length $sm, $sm, $prio );
@APoO'M V ~"\0R3]0# unpack51Testing软件测试网 Z.wo/Efj&qO
( undef, undef, $len) = unpack( 'Z* Z* C', $msg );#--先取得长度
g*{[S}#@0#--依照长度设定A的长度51Testing软件测试网:d6i-I0Op
($src, $dst, $sm, $prio) = unpack ( "Z* Z* x A$len C", $msg );51Testing软件测试网So7rE_1_
Dynamic Template51Testing软件测试网G9O0LPJ{y
从前面到现在我们看到的都是有固定长度的范例,但是如果碰到没有固定长度的怎麽办?以下有个例子来说明:
*d cl$a%tP![0my $env = pack( 'A*A*Z*' x keys( %Env ) . 'C',51Testing软件测试网5v:Z-c b-a*MJ2wLN
map( { ( $_, '=', $Env{$_} ) } keys( %Env ) ), 0 );
7a O6Y'N.IX/t{ _J051Testing软件测试网 V*EUiZbO-|^
为了方便让C来Parsing这个string,所以利用两个string与一个null terminated字串,利用map将$ENV中的参数读出来,然後利用(AAA,=,BBB)的方式,储存成一个array,然後传给pack,然後pack利用”A*A*Z*将AAA,=,BBB给打包起来,keys(%ENV)表示ENV hash总共有多少个$element在里面。最後为了方便C parsing,在最後面加上一个”0”。51Testing软件测试网%aX4vjg x bV_g
my $n = $env =~ tr/\0// - 1;51Testing软件测试网0Vx0tw7T;Jy
my %env = map( split( /=/, $_ ), unpack( 'Z*' x $n, $env ) );51Testing软件测试网3[(?+Y/F!q:kP

I#V,HM f&TFU0而在unpack时,要先算出$env里面到底有多少个$element在里面,这可以透过tr来计算.

\+v4yod5@x+S0

TAG: perl

 

评分:0

我来说两句

Open Toolbar