How do I convert little Endian to Big Endian using a Perl Script?
I am using the Perl Win32::SerialPort
module. In this paticular module I sent over data using the input command. The data that I sent over to a embedded system were scalar data (numbers) using the transmit_char
function (if it were C it would be integers, but since its a scripting language I am not sure what the internal format is in perl. My guess is that perl always stores all numbers as 32 bit floating points, which are adjusted by the module when transmitting).
Then after sending the data I receive data using the input command. The data that I recieve is probably in binary form, but perl doesn't know how to interpret it. I use the unpack
function like this
my $binData = $PortObj->input;
my $hexData = unpack("H*",$binData);
Suppose I transmit 0x4294
over the serial cable, which is a command on the embedded system that I am communicating with, I expect a response of 0x5245
. Now the problem is with the endianess: when I unpack I get 0x4552
, which is wrong. Is there a way开发者_开发技巧 to correct that by adjusting the binary data. I also tried h*
, which gives me 0x5425
, which is also not correct.
Note: the data I receive is sent over Byte at a time and the LSB is sent first
Endianess applies to the ordering of bytes of an integer (primarily). You need to know the size of the integer.
Example for 32-bit unsigned:
my $bytes = pack('H*', '1122334455667788');
my @n = unpack('N*', $bytes);
# @n = ( 0x11223344, 0x55667788 );
my $bytes = pack('H*', '4433221188776655');
my @n = unpack('V*', $bytes);
# @n = ( 0x11223344, 0x55667788 );
See pack
. Note the "<
" and ">
" modifiers to control the endianess where of instructions where the default endianess is not the one you want.
Note: If you're reading from the file, you already have bytes. Don't create bytes using pack 'H*'
.
Note: If you're reading from the file, don't forget to binmode
the handle.
Regarding the example the OP added to his post:
To get 0x5245
from "\x45\x52"
, use unpack("v", $two_bytes)
.
What sort of data types are these? Perl's pack has the N
and V
format specifiers for integers, and Perl 5.10 added the >
and <
modifiers so you can read shorts, floats, doubles, and quads (and some other types) in the endianness you want.
With these, you read the data in the endianness it uses in the input. After you do that, you have the data internally-represented as the number you expect and you can re-pack them anyway that you like.
For example, the Q
format doesn't have an endianness partner like the pair N
and V
. I'm always going to get the architecture's interpretation of the octet sequence:
my @octets = ( 0x19, 0x36 );
my $bom = pack 'C*', @octets;
my ( $short ) = unpack 'S', $bom;
my $last = $short & 0x00FF;
my $first = ( $short & 0xFF00 ) >> 8;
printf "SHORT: %x FIRST: %x LAST: %x\n", $short, $first, $last;
my $quad_format = $first == $octets[0] ? 'Q' : 'Q>';
say "QUAD_FORMAT: $quad_format";
my $data = pack 'C*', 0b11011110, 0xAD, 0xBE, 0xEF, 0xAA, 0xBB, 0xCC, 0xDD;
my $q = unpack $quad_format, $data;
printf "$quad_format: %x\n", $q;
The output shows that I get the packed value of 0x1936
comes back as 0x3619
with the plain S
format. That means that this was run on a little-endian architecture. A same thing will happen with a quad value, so I want to read the quad and tell Perl to interpret get the value then force it to be big-endian (the "big" part of '>' touches the Q
) to get the expected internal numerical value:
SHORT: 3619 FIRST: 36 LAST: 19
QUAD_FORMAT: Q>
Q>: deadbeefaabbccdd
I write more about this in Use the > and < pack modifiers to specify the architecture.
精彩评论